From da517466e3a6fbb6553326663a357f400c763870 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" Date: Sat, 27 Apr 2024 08:27:41 +0000 Subject: [PATCH] Build for 9b688622e9e6c034948dc0391b0f56dd5d9a5aff --- ...70a6ef.js => src_index-exposed_js.10413c09803686586db0.js} | 4 ++-- ...s.map => src_index-exposed_js.10413c09803686586db0.js.map} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename chunks/{src_index-exposed_js.b047c16ef5b61870a6ef.js => src_index-exposed_js.10413c09803686586db0.js} (99%) rename chunks/{src_index-exposed_js.b047c16ef5b61870a6ef.js.map => src_index-exposed_js.10413c09803686586db0.js.map} (99%) diff --git a/chunks/src_index-exposed_js.b047c16ef5b61870a6ef.js b/chunks/src_index-exposed_js.10413c09803686586db0.js similarity index 99% rename from chunks/src_index-exposed_js.b047c16ef5b61870a6ef.js rename to chunks/src_index-exposed_js.10413c09803686586db0.js index 7cf3438854..09f55c7279 100644 --- a/chunks/src_index-exposed_js.b047c16ef5b61870a6ef.js +++ b/chunks/src_index-exposed_js.10413c09803686586db0.js @@ -32078,9 +32078,9 @@ module.exports = __webpack_require__.p + "446764135169b4621793.mp3"; /***/ ((module) => { "use strict"; -module.exports = /*#__PURE__*/JSON.parse('{"name":"scratch-vm","version":"4.5.116","description":"Virtual Machine for Scratch 3.0","author":"Massachusetts Institute of Technology","license":"BSD-3-Clause","homepage":"https://github.com/scratchfoundation/scratch-vm#readme","repository":{"type":"git","url":"https://github.com/scratchfoundation/scratch-vm.git","sha":"ceab4e95135ae2e614257b87d8117007a4a92fe5"},"main":"./dist/node/scratch-vm.js","browser":"./dist/web/scratch-vm.js","exports":{"webpack":"./src/index.js","browser":"./dist/web/scratch-vm.js","node":"./dist/node/scratch-vm.js","default":"./src/index.js"},"scripts":{"build":"npm run docs && webpack --progress","coverage":"tap ./test/{unit,integration}/*.js --coverage --coverage-report=lcov","docs":"jsdoc -c .jsdoc.json","i18n:src":"mkdirp translations/core && format-message extract --out-file translations/core/en.json src/extensions/**/index.js","i18n:push":"tx-push-src scratch-editor extensions translations/core/en.json","lint":"eslint . && format-message lint src/**/*.js","prepare":"husky install","prepublish":"in-publish && npm run build || not-in-publish","start":"webpack serve","tap":"tap ./test/{unit,integration}/*.js","tap:unit":"tap ./test/unit/*.js","tap:integration":"tap ./test/integration/*.js","test":"npm run lint && npm run tap","watch":"webpack --progress --watch","version":"json -f package.json -I -e \\"this.repository.sha = \'$(git log -n1 --pretty=format:%H)\'\\""},"config":{"commitizen":{"path":"cz-conventional-changelog"}},"browserslist":["Chrome >= 63","Edge >= 15","Firefox >= 57","Safari >= 11"],"tap":{"branches":60,"functions":70,"lines":70,"statements":70},"dependencies":{"@vernier/godirect":"^1.5.0","arraybuffer-loader":"^1.0.6","atob":"^2.1.2","btoa":"^1.2.1","canvas-toBlob":"^1.0.0","decode-html":"^2.0.0","diff-match-patch":"^1.0.4","format-message":"^6.2.1","htmlparser2":"^3.10.0","immutable":"^3.8.1","jszip":"^3.1.5","minilog":"^3.1.0","scratch-audio":"^1.0.6","scratch-parser":"^5.1.1","scratch-render":"^1.0.13","scratch-sb1-converter":"^1.0.0","scratch-storage":"^2.3.5","scratch-svg-renderer":"2.3.32","scratch-translate-extension-languages":"^1.0.0","text-encoding":"^0.7.0","uuid":"^8.3.2","web-worker":"^1.3.0"},"devDependencies":{"@babel/core":"7.24.4","@babel/eslint-parser":"7.24.1","@babel/preset-env":"7.24.4","@commitlint/cli":"17.8.1","@commitlint/config-conventional":"17.8.1","adm-zip":"0.4.11","babel-loader":"9.1.3","callsite":"1.0.0","copy-webpack-plugin":"4.6.0","docdash":"1.2.0","eslint":"8.57.0","eslint-config-scratch":"9.0.8","expose-loader":"1.0.3","file-loader":"6.2.0","format-message-cli":"6.2.4","husky":"8.0.3","in-publish":"2.0.1","js-md5":"0.7.3","jsdoc":"3.6.11","json":"^9.0.4","pngjs":"3.4.0","scratch-blocks":"1.1.98","scratch-l10n":"3.18.122","scratch-render-fonts":"1.0.41","scratch-semantic-release-config":"1.0.14","scratch-webpack-configuration":"1.3.0","script-loader":"0.7.2","semantic-release":"19.0.5","stats.js":"0.17.0","tap":"16.3.10","webpack":"5.91.0","webpack-cli":"4.10.0","webpack-dev-server":"3.11.3"}}'); +module.exports = /*#__PURE__*/JSON.parse('{"name":"scratch-vm","version":"4.5.116","description":"Virtual Machine for Scratch 3.0","author":"Massachusetts Institute of Technology","license":"BSD-3-Clause","homepage":"https://github.com/scratchfoundation/scratch-vm#readme","repository":{"type":"git","url":"https://github.com/scratchfoundation/scratch-vm.git","sha":"ceab4e95135ae2e614257b87d8117007a4a92fe5"},"main":"./dist/node/scratch-vm.js","browser":"./dist/web/scratch-vm.js","exports":{"webpack":"./src/index.js","browser":"./dist/web/scratch-vm.js","node":"./dist/node/scratch-vm.js","default":"./src/index.js"},"scripts":{"build":"npm run docs && webpack --progress","coverage":"tap ./test/{unit,integration}/*.js --coverage --coverage-report=lcov","docs":"jsdoc -c .jsdoc.json","i18n:src":"mkdirp translations/core && format-message extract --out-file translations/core/en.json src/extensions/**/index.js","i18n:push":"tx-push-src scratch-editor extensions translations/core/en.json","lint":"eslint . && format-message lint src/**/*.js","prepare":"husky install","prepublish":"in-publish && npm run build || not-in-publish","start":"webpack serve","tap":"tap ./test/{unit,integration}/*.js","tap:unit":"tap ./test/unit/*.js","tap:integration":"tap ./test/integration/*.js","test":"npm run lint && npm run tap","watch":"webpack --progress --watch","version":"json -f package.json -I -e \\"this.repository.sha = \'$(git log -n1 --pretty=format:%H)\'\\""},"config":{"commitizen":{"path":"cz-conventional-changelog"}},"browserslist":["Chrome >= 63","Edge >= 15","Firefox >= 57","Safari >= 11"],"tap":{"branches":60,"functions":70,"lines":70,"statements":70},"dependencies":{"@vernier/godirect":"^1.5.0","arraybuffer-loader":"^1.0.6","atob":"^2.1.2","btoa":"^1.2.1","canvas-toBlob":"^1.0.0","decode-html":"^2.0.0","diff-match-patch":"^1.0.4","format-message":"^6.2.1","htmlparser2":"^3.10.0","immutable":"^3.8.1","jszip":"^3.1.5","minilog":"^3.1.0","scratch-audio":"^1.0.6","scratch-parser":"^5.1.1","scratch-render":"^1.0.13","scratch-sb1-converter":"^1.0.0","scratch-storage":"^2.3.5","scratch-svg-renderer":"2.3.33","scratch-translate-extension-languages":"^1.0.0","text-encoding":"^0.7.0","uuid":"^8.3.2","web-worker":"^1.3.0"},"devDependencies":{"@babel/core":"7.24.4","@babel/eslint-parser":"7.24.1","@babel/preset-env":"7.24.4","@commitlint/cli":"17.8.1","@commitlint/config-conventional":"17.8.1","adm-zip":"0.4.11","babel-loader":"9.1.3","callsite":"1.0.0","copy-webpack-plugin":"4.6.0","docdash":"1.2.0","eslint":"8.57.0","eslint-config-scratch":"9.0.8","expose-loader":"1.0.3","file-loader":"6.2.0","format-message-cli":"6.2.4","husky":"8.0.3","in-publish":"2.0.1","js-md5":"0.7.3","jsdoc":"3.6.11","json":"^9.0.4","pngjs":"3.4.0","scratch-blocks":"1.1.98","scratch-l10n":"3.18.122","scratch-render-fonts":"1.0.40","scratch-semantic-release-config":"1.0.14","scratch-webpack-configuration":"1.3.0","script-loader":"0.7.2","semantic-release":"19.0.5","stats.js":"0.17.0","tap":"16.3.10","webpack":"5.91.0","webpack-cli":"4.10.0","webpack-dev-server":"3.11.3"}}'); /***/ }) }]); -//# sourceMappingURL=src_index-exposed_js.b047c16ef5b61870a6ef.js.map \ No newline at end of file +//# sourceMappingURL=src_index-exposed_js.10413c09803686586db0.js.map \ No newline at end of file diff --git a/chunks/src_index-exposed_js.b047c16ef5b61870a6ef.js.map b/chunks/src_index-exposed_js.10413c09803686586db0.js.map similarity index 99% rename from chunks/src_index-exposed_js.b047c16ef5b61870a6ef.js.map rename to chunks/src_index-exposed_js.10413c09803686586db0.js.map index 0bf8167bc1..96b7f4e11d 100644 --- a/chunks/src_index-exposed_js.b047c16ef5b61870a6ef.js.map +++ b/chunks/src_index-exposed_js.10413c09803686586db0.js.map @@ -1 +1 @@ -{"version":3,"file":"chunks/src_index-exposed_js.b047c16ef5b61870a6ef.js","mappings":";;;;;;;;AAAA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACLA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AC7MA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;;;;;;;;;;ACpEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AChQA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACxIA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAIA;AACA;AAGA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAGA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAGA;AAEA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACnmBA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;;;;;;;;;;AC/RA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACzJA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAGA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AC9EA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAAA;AAEA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;;;;;;;;;;AC/UA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;;;;;;;;;;AC7VA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AARA;AAAA;AASA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AC9IA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAHA;AAAA;AAIA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AC1OA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;;;;;;;;;;AC/KA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACjPA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;;;;;;;;;AClBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AC7EA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AClxCA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAGA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;;;;;;;;;;ACvDA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AAAA;AAAA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAKA;;AAEA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACnkBA;AAAA;AAAA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACtBA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AC/CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;;AAGA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACrYA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAIA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;;;;;;;;;AC7nFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AC1BA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACxWA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAMA;AACA;AAEA;;;;;;;;;;AC5BA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;;;;;;;;;;AC9xBA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACnZA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACrEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AC9CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACjDA;AACA;AACA;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AAGA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;;;;;;;;;;ACvbA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AChBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAUA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAaA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AASA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AAQA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAEA;AACA;AACA;AAEA;AAMA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAKA;;AAEA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAKA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AChkEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAaA;AAKA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAKA;AACA;AAIA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;;AAEA;AACA;AAOA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAgBA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAQA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAMA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAMA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AC10CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAEA;AAGA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAKA;AAEA;AACA;AACA;AAEA;AACA;AAKA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;;;;;;;;;;ACp9BA;;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AC3CA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAYA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;;;;;;;;;AC3YA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AAIA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACv9BA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;;AAGA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAOA;AACA;AAyBA;AACA;AAEA;AACA;AASA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACpzCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;AChEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;;;;;;;;;;ACjwBA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;;;;;;;;;AC7vBA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;AC7RA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACjlBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAIA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAIA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AAEA;AAIA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AAMA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AASA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAWA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAKA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAEA;AACA;AACA;AAEA;AAMA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAKA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAIA;AACA;AAAA;AACA;AAAA;AACA;AAIA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AASA;AACA;AAKA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AC/kDA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAMA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAGA;AACA;AAEA;AAGA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;;;;;;;;;AC/ZA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;;;;;;;;;ACnHA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AC/PA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACzMA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACtCA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACxKA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACjJA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AAGA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AChJA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;;;;;;;;;;AC3BA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACvBA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AAJA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;;;;;;;;;;ACjNA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAQA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAQA;AACA;AACA;AACA;AACA;AAEA;AAIA;AACA;AAMA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;;;;;;;;;;ACjLA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;;AAEA;AACA;;AAcA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAGA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;;AAEA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AASA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAGA;AACA;AAEA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAOA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAGA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;;;;;;;;;AC9wCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;;;;;;;;;;ACzxDA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAeA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AAIA;AACA;AACA;AAKA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAIA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AASA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAGA;AAIA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAGA;AAEA;AAIA;AACA;AAAA;AAKA;AAEA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;ACvyCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;;;;;;;;;AC3DA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AC1lCA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AChLA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;;;;;;;;;;AC/CA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACzNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AChBA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AC3MA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;;;;;;;;;;ACpDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AChCA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;;;;;;;;;;ACjHA;AACA;AAEA;;;;;;;;;;ACHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACzHA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACjBA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;AChCA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACxEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACpIA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AC7FA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;ACtGA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AC5BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AC/CA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AAEA;;;;;;;;;;AChCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAGA;AAGA;AAGA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAKA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AAIA;AASA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;AnFjjDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AoFTA","sources":["webpack://scratch-vm/./src/index.js","webpack://scratch-vm/./src/blocks/scratch3_control.js","webpack://scratch-vm/./src/blocks/scratch3_core_example.js","webpack://scratch-vm/./src/blocks/scratch3_data.js","webpack://scratch-vm/./src/blocks/scratch3_event.js","webpack://scratch-vm/./src/blocks/scratch3_looks.js","webpack://scratch-vm/./src/blocks/scratch3_motion.js","webpack://scratch-vm/./src/blocks/scratch3_operators.js","webpack://scratch-vm/./src/blocks/scratch3_procedures.js","webpack://scratch-vm/./src/blocks/scratch3_sensing.js","webpack://scratch-vm/./src/blocks/scratch3_sound.js","webpack://scratch-vm/./src/dispatch/central-dispatch.js","webpack://scratch-vm/./src/dispatch/shared-dispatch.js","webpack://scratch-vm/./src/engine/adapter.js","webpack://scratch-vm/./src/engine/block-utility.js","webpack://scratch-vm/./src/engine/blocks-execute-cache.js","webpack://scratch-vm/./src/engine/blocks-runtime-cache.js","webpack://scratch-vm/./src/engine/blocks.js","webpack://scratch-vm/./src/engine/comment.js","webpack://scratch-vm/./src/engine/execute.js","webpack://scratch-vm/./src/engine/monitor-record.js","webpack://scratch-vm/./src/engine/mutation-adapter.js","webpack://scratch-vm/./src/engine/profiler.js","webpack://scratch-vm/./src/engine/runtime.js","webpack://scratch-vm/./src/engine/scratch-blocks-constants.js","webpack://scratch-vm/./src/engine/sequencer.js","webpack://scratch-vm/./src/engine/stage-layering.js","webpack://scratch-vm/./src/engine/target.js","webpack://scratch-vm/./src/engine/thread.js","webpack://scratch-vm/./src/engine/variable.js","webpack://scratch-vm/./src/extension-support/argument-type.js","webpack://scratch-vm/./src/extension-support/block-type.js","webpack://scratch-vm/./src/extension-support/extension-manager.js","webpack://scratch-vm/./src/extension-support/target-type.js","webpack://scratch-vm/./src/extensions/scratch3_boost/index.js","webpack://scratch-vm/./src/extensions/scratch3_ev3/index.js","webpack://scratch-vm/./src/extensions/scratch3_gdx_for/index.js","webpack://scratch-vm/./src/extensions/scratch3_gdx_for/scratch-link-device-adapter.js","webpack://scratch-vm/./src/extensions/scratch3_makeymakey/index.js","webpack://scratch-vm/./src/extensions/scratch3_microbit/index.js","webpack://scratch-vm/./src/extensions/scratch3_music/index.js","webpack://scratch-vm/./src/extensions/scratch3_music/manifest.js","webpack://scratch-vm/./src/extensions/scratch3_pen/index.js","webpack://scratch-vm/./src/extensions/scratch3_text2speech/index.js","webpack://scratch-vm/./src/extensions/scratch3_translate/index.js","webpack://scratch-vm/./src/extensions/scratch3_video_sensing/index.js","webpack://scratch-vm/./src/extensions/scratch3_wedo2/index.js","webpack://scratch-vm/./src/import/load-costume.js","webpack://scratch-vm/./src/import/load-sound.js","webpack://scratch-vm/./src/io/ble.js","webpack://scratch-vm/./src/io/bt.js","webpack://scratch-vm/./src/io/clock.js","webpack://scratch-vm/./src/io/cloud.js","webpack://scratch-vm/./src/io/keyboard.js","webpack://scratch-vm/./src/io/mouse.js","webpack://scratch-vm/./src/io/mouseWheel.js","webpack://scratch-vm/./src/io/userData.js","webpack://scratch-vm/./src/io/video.js","webpack://scratch-vm/./src/serialization/deserialize-assets.js","webpack://scratch-vm/./src/serialization/sb2.js","webpack://scratch-vm/./src/serialization/sb2_specmap.js","webpack://scratch-vm/./src/serialization/sb3.js","webpack://scratch-vm/./src/serialization/serialize-assets.js","webpack://scratch-vm/./src/sprites/rendered-target.js","webpack://scratch-vm/./src/sprites/sprite.js","webpack://scratch-vm/./src/util/base64-util.js","webpack://scratch-vm/./src/util/cast.js","webpack://scratch-vm/./src/util/clone.js","webpack://scratch-vm/./src/util/color.js","webpack://scratch-vm/./src/util/fetch-with-timeout.js","webpack://scratch-vm/./src/util/get-monitor-id.js","webpack://scratch-vm/./src/util/jsonrpc.js","webpack://scratch-vm/./src/util/log.js","webpack://scratch-vm/./src/util/math-util.js","webpack://scratch-vm/./src/util/maybe-format-message.js","webpack://scratch-vm/./src/util/new-block-ids.js","webpack://scratch-vm/./src/util/rateLimiter.js","webpack://scratch-vm/./src/util/scratch-link-websocket.js","webpack://scratch-vm/./src/util/string-util.js","webpack://scratch-vm/./src/util/timer.js","webpack://scratch-vm/./src/util/uid.js","webpack://scratch-vm/./src/util/variable-util.js","webpack://scratch-vm/./src/util/xml-escape.js","webpack://scratch-vm/./src/virtual-machine.js","webpack://scratch-vm/ignored|/home/runner/work/scratch-vm/scratch-vm/node_modules/htmlparser2/lib|readable-stream"],"sourcesContent":["var ___EXPOSE_LOADER_IMPORT___ = require(\"-!./index.js\");\nvar ___EXPOSE_LOADER_GET_GLOBAL_THIS___ = require(\"../node_modules/expose-loader/dist/runtime/getGlobalThis.js\");\nvar ___EXPOSE_LOADER_GLOBAL_THIS___ = ___EXPOSE_LOADER_GET_GLOBAL_THIS___;\nif (typeof ___EXPOSE_LOADER_GLOBAL_THIS___[\"VirtualMachine\"] === 'undefined') ___EXPOSE_LOADER_GLOBAL_THIS___[\"VirtualMachine\"] = ___EXPOSE_LOADER_IMPORT___;\nelse throw new Error('[exposes-loader] The \"VirtualMachine\" value exists in the global scope, it may not be safe to overwrite it, use the \"override\" option')\nmodule.exports = ___EXPOSE_LOADER_IMPORT___;\n","const Cast = require('../util/cast');\n\nclass Scratch3ControlBlocks {\n constructor (runtime) {\n /**\n * The runtime instantiating this block package.\n * @type {Runtime}\n */\n this.runtime = runtime;\n\n /**\n * The \"counter\" block value. For compatibility with 2.0.\n * @type {number}\n */\n this._counter = 0;\n\n this.runtime.on('RUNTIME_DISPOSED', this.clearCounter.bind(this));\n }\n\n /**\n * Retrieve the block primitives implemented by this package.\n * @return {object.} Mapping of opcode to Function.\n */\n getPrimitives () {\n return {\n control_repeat: this.repeat,\n control_repeat_until: this.repeatUntil,\n control_while: this.repeatWhile,\n control_for_each: this.forEach,\n control_forever: this.forever,\n control_wait: this.wait,\n control_wait_until: this.waitUntil,\n control_if: this.if,\n control_if_else: this.ifElse,\n control_stop: this.stop,\n control_create_clone_of: this.createClone,\n control_delete_this_clone: this.deleteClone,\n control_get_counter: this.getCounter,\n control_incr_counter: this.incrCounter,\n control_clear_counter: this.clearCounter,\n control_all_at_once: this.allAtOnce\n };\n }\n\n getHats () {\n return {\n control_start_as_clone: {\n restartExistingThreads: false\n }\n };\n }\n\n repeat (args, util) {\n const times = Math.round(Cast.toNumber(args.TIMES));\n // Initialize loop\n if (typeof util.stackFrame.loopCounter === 'undefined') {\n util.stackFrame.loopCounter = times;\n }\n // Only execute once per frame.\n // When the branch finishes, `repeat` will be executed again and\n // the second branch will be taken, yielding for the rest of the frame.\n // Decrease counter\n util.stackFrame.loopCounter--;\n // If we still have some left, start the branch.\n if (util.stackFrame.loopCounter >= 0) {\n util.startBranch(1, true);\n }\n }\n\n repeatUntil (args, util) {\n const condition = Cast.toBoolean(args.CONDITION);\n // If the condition is false (repeat UNTIL), start the branch.\n if (!condition) {\n util.startBranch(1, true);\n }\n }\n\n repeatWhile (args, util) {\n const condition = Cast.toBoolean(args.CONDITION);\n // If the condition is true (repeat WHILE), start the branch.\n if (condition) {\n util.startBranch(1, true);\n }\n }\n\n forEach (args, util) {\n const variable = util.target.lookupOrCreateVariable(\n args.VARIABLE.id, args.VARIABLE.name);\n\n if (typeof util.stackFrame.index === 'undefined') {\n util.stackFrame.index = 0;\n }\n\n if (util.stackFrame.index < Number(args.VALUE)) {\n util.stackFrame.index++;\n variable.value = util.stackFrame.index;\n util.startBranch(1, true);\n }\n }\n\n waitUntil (args, util) {\n const condition = Cast.toBoolean(args.CONDITION);\n if (!condition) {\n util.yield();\n }\n }\n\n forever (args, util) {\n util.startBranch(1, true);\n }\n\n wait (args, util) {\n if (util.stackTimerNeedsInit()) {\n const duration = Math.max(0, 1000 * Cast.toNumber(args.DURATION));\n\n util.startStackTimer(duration);\n this.runtime.requestRedraw();\n util.yield();\n } else if (!util.stackTimerFinished()) {\n util.yield();\n }\n }\n\n if (args, util) {\n const condition = Cast.toBoolean(args.CONDITION);\n if (condition) {\n util.startBranch(1, false);\n }\n }\n\n ifElse (args, util) {\n const condition = Cast.toBoolean(args.CONDITION);\n if (condition) {\n util.startBranch(1, false);\n } else {\n util.startBranch(2, false);\n }\n }\n\n stop (args, util) {\n const option = args.STOP_OPTION;\n if (option === 'all') {\n util.stopAll();\n } else if (option === 'other scripts in sprite' ||\n option === 'other scripts in stage') {\n util.stopOtherTargetThreads();\n } else if (option === 'this script') {\n util.stopThisScript();\n }\n }\n\n createClone (args, util) {\n // Cast argument to string\n args.CLONE_OPTION = Cast.toString(args.CLONE_OPTION);\n\n // Set clone target\n let cloneTarget;\n if (args.CLONE_OPTION === '_myself_') {\n cloneTarget = util.target;\n } else {\n cloneTarget = this.runtime.getSpriteTargetByName(args.CLONE_OPTION);\n }\n\n // If clone target is not found, return\n if (!cloneTarget) return;\n\n // Create clone\n const newClone = cloneTarget.makeClone();\n if (newClone) {\n this.runtime.addTarget(newClone);\n\n // Place behind the original target.\n newClone.goBehindOther(cloneTarget);\n }\n }\n\n deleteClone (args, util) {\n if (util.target.isOriginal) return;\n this.runtime.disposeTarget(util.target);\n this.runtime.stopForTarget(util.target);\n }\n\n getCounter () {\n return this._counter;\n }\n\n clearCounter () {\n this._counter = 0;\n }\n\n incrCounter () {\n this._counter++;\n }\n\n allAtOnce (args, util) {\n // Since the \"all at once\" block is implemented for compatiblity with\n // Scratch 2.0 projects, it behaves the same way it did in 2.0, which\n // is to simply run the contained script (like \"if 1 = 1\").\n // (In early versions of Scratch 2.0, it would work the same way as\n // \"run without screen refresh\" custom blocks do now, but this was\n // removed before the release of 2.0.)\n util.startBranch(1, false);\n }\n}\n\nmodule.exports = Scratch3ControlBlocks;\n","const BlockType = require('../extension-support/block-type');\nconst ArgumentType = require('../extension-support/argument-type');\n\n/* eslint-disable-next-line max-len */\nconst blockIconURI = 'data:image/svg+xml,%3Csvg id=\"rotate-counter-clockwise\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:%233d79cc;%7D.cls-2%7Bfill:%23fff;%7D%3C/style%3E%3C/defs%3E%3Ctitle%3Erotate-counter-clockwise%3C/title%3E%3Cpath class=\"cls-1\" d=\"M22.68,12.2a1.6,1.6,0,0,1-1.27.63H13.72a1.59,1.59,0,0,1-1.16-2.58l1.12-1.41a4.82,4.82,0,0,0-3.14-.77,4.31,4.31,0,0,0-2,.8,4.25,4.25,0,0,0-1.34,1.73,5.06,5.06,0,0,0,.54,4.62A5.58,5.58,0,0,0,12,17.74h0a2.26,2.26,0,0,1-.16,4.52A10.25,10.25,0,0,1,3.74,18,10.14,10.14,0,0,1,2.25,8.78,9.7,9.7,0,0,1,5.08,4.64,9.92,9.92,0,0,1,9.66,2.5a10.66,10.66,0,0,1,7.72,1.68l1.08-1.35a1.57,1.57,0,0,1,1.24-.6,1.6,1.6,0,0,1,1.54,1.21l1.7,7.37A1.57,1.57,0,0,1,22.68,12.2Z\"/%3E%3Cpath class=\"cls-2\" d=\"M21.38,11.83H13.77a.59.59,0,0,1-.43-1l1.75-2.19a5.9,5.9,0,0,0-4.7-1.58,5.07,5.07,0,0,0-4.11,3.17A6,6,0,0,0,7,15.77a6.51,6.51,0,0,0,5,2.92,1.31,1.31,0,0,1-.08,2.62,9.3,9.3,0,0,1-7.35-3.82A9.16,9.16,0,0,1,3.17,9.12,8.51,8.51,0,0,1,5.71,5.4,8.76,8.76,0,0,1,9.82,3.48a9.71,9.71,0,0,1,7.75,2.07l1.67-2.1a.59.59,0,0,1,1,.21L22,11.08A.59.59,0,0,1,21.38,11.83Z\"/%3E%3C/svg%3E';\n\n/**\n * An example core block implemented using the extension spec.\n * This is not loaded as part of the core blocks in the VM but it is provided\n * and used as part of tests.\n */\nclass Scratch3CoreExample {\n constructor (runtime) {\n /**\n * The runtime instantiating this block package.\n * @type {Runtime}\n */\n this.runtime = runtime;\n }\n\n /**\n * @returns {object} metadata for this extension and its blocks.\n */\n getInfo () {\n return {\n id: 'coreExample',\n name: 'CoreEx', // This string does not need to be translated as this extension is only used as an example.\n blocks: [\n {\n func: 'MAKE_A_VARIABLE',\n blockType: BlockType.BUTTON,\n text: 'make a variable (CoreEx)'\n },\n {\n opcode: 'exampleOpcode',\n blockType: BlockType.REPORTER,\n text: 'example block'\n },\n {\n opcode: 'exampleWithInlineImage',\n blockType: BlockType.COMMAND,\n text: 'block with image [CLOCKWISE] inline',\n arguments: {\n CLOCKWISE: {\n type: ArgumentType.IMAGE,\n dataURI: blockIconURI\n }\n }\n }\n ]\n };\n }\n\n /**\n * Example opcode just returns the name of the stage target.\n * @returns {string} The name of the first target in the project.\n */\n exampleOpcode () {\n const stage = this.runtime.getTargetForStage();\n return stage ? stage.getName() : 'no stage yet';\n }\n\n exampleWithInlineImage () {\n return;\n }\n\n}\n\nmodule.exports = Scratch3CoreExample;\n","const Cast = require('../util/cast');\n\nclass Scratch3DataBlocks {\n constructor (runtime) {\n /**\n * The runtime instantiating this block package.\n * @type {Runtime}\n */\n this.runtime = runtime;\n }\n\n /**\n * Retrieve the block primitives implemented by this package.\n * @return {object.} Mapping of opcode to Function.\n */\n getPrimitives () {\n return {\n data_variable: this.getVariable,\n data_setvariableto: this.setVariableTo,\n data_changevariableby: this.changeVariableBy,\n data_hidevariable: this.hideVariable,\n data_showvariable: this.showVariable,\n data_listcontents: this.getListContents,\n data_addtolist: this.addToList,\n data_deleteoflist: this.deleteOfList,\n data_deletealloflist: this.deleteAllOfList,\n data_insertatlist: this.insertAtList,\n data_replaceitemoflist: this.replaceItemOfList,\n data_itemoflist: this.getItemOfList,\n data_itemnumoflist: this.getItemNumOfList,\n data_lengthoflist: this.lengthOfList,\n data_listcontainsitem: this.listContainsItem,\n data_hidelist: this.hideList,\n data_showlist: this.showList\n };\n }\n\n getVariable (args, util) {\n const variable = util.target.lookupOrCreateVariable(\n args.VARIABLE.id, args.VARIABLE.name);\n return variable.value;\n }\n\n setVariableTo (args, util) {\n const variable = util.target.lookupOrCreateVariable(\n args.VARIABLE.id, args.VARIABLE.name);\n variable.value = args.VALUE;\n\n if (variable.isCloud) {\n util.ioQuery('cloud', 'requestUpdateVariable', [variable.name, args.VALUE]);\n }\n }\n\n changeVariableBy (args, util) {\n const variable = util.target.lookupOrCreateVariable(\n args.VARIABLE.id, args.VARIABLE.name);\n const castedValue = Cast.toNumber(variable.value);\n const dValue = Cast.toNumber(args.VALUE);\n const newValue = castedValue + dValue;\n variable.value = newValue;\n\n if (variable.isCloud) {\n util.ioQuery('cloud', 'requestUpdateVariable', [variable.name, newValue]);\n }\n }\n\n changeMonitorVisibility (id, visible) {\n // Send the monitor blocks an event like the flyout checkbox event.\n // This both updates the monitor state and changes the isMonitored block flag.\n this.runtime.monitorBlocks.changeBlock({\n id: id, // Monitor blocks for variables are the variable ID.\n element: 'checkbox', // Mimic checkbox event from flyout.\n value: visible\n }, this.runtime);\n }\n\n showVariable (args) {\n this.changeMonitorVisibility(args.VARIABLE.id, true);\n }\n\n hideVariable (args) {\n this.changeMonitorVisibility(args.VARIABLE.id, false);\n }\n\n showList (args) {\n this.changeMonitorVisibility(args.LIST.id, true);\n }\n\n hideList (args) {\n this.changeMonitorVisibility(args.LIST.id, false);\n }\n\n getListContents (args, util) {\n const list = util.target.lookupOrCreateList(\n args.LIST.id, args.LIST.name);\n\n // If block is running for monitors, return copy of list as an array if changed.\n if (util.thread.updateMonitor) {\n // Return original list value if up-to-date, which doesn't trigger monitor update.\n if (list._monitorUpToDate) return list.value;\n // If value changed, reset the flag and return a copy to trigger monitor update.\n // Because monitors use Immutable data structures, only new objects trigger updates.\n list._monitorUpToDate = true;\n return list.value.slice();\n }\n\n // Determine if the list is all single letters.\n // If it is, report contents joined together with no separator.\n // If it's not, report contents joined together with a space.\n let allSingleLetters = true;\n for (let i = 0; i < list.value.length; i++) {\n const listItem = list.value[i];\n if (!((typeof listItem === 'string') &&\n (listItem.length === 1))) {\n allSingleLetters = false;\n break;\n }\n }\n if (allSingleLetters) {\n return list.value.join('');\n }\n return list.value.join(' ');\n\n }\n\n addToList (args, util) {\n const list = util.target.lookupOrCreateList(\n args.LIST.id, args.LIST.name);\n if (list.value.length < Scratch3DataBlocks.LIST_ITEM_LIMIT) {\n list.value.push(args.ITEM);\n list._monitorUpToDate = false;\n }\n }\n\n deleteOfList (args, util) {\n const list = util.target.lookupOrCreateList(\n args.LIST.id, args.LIST.name);\n const index = Cast.toListIndex(args.INDEX, list.value.length, true);\n if (index === Cast.LIST_INVALID) {\n return;\n } else if (index === Cast.LIST_ALL) {\n list.value = [];\n return;\n }\n list.value.splice(index - 1, 1);\n list._monitorUpToDate = false;\n }\n\n deleteAllOfList (args, util) {\n const list = util.target.lookupOrCreateList(\n args.LIST.id, args.LIST.name);\n list.value = [];\n return;\n }\n\n insertAtList (args, util) {\n const item = args.ITEM;\n const list = util.target.lookupOrCreateList(\n args.LIST.id, args.LIST.name);\n const index = Cast.toListIndex(args.INDEX, list.value.length + 1, false);\n if (index === Cast.LIST_INVALID) {\n return;\n }\n const listLimit = Scratch3DataBlocks.LIST_ITEM_LIMIT;\n if (index > listLimit) return;\n list.value.splice(index - 1, 0, item);\n if (list.value.length > listLimit) {\n // If inserting caused the list to grow larger than the limit,\n // remove the last element in the list\n list.value.pop();\n }\n list._monitorUpToDate = false;\n }\n\n replaceItemOfList (args, util) {\n const item = args.ITEM;\n const list = util.target.lookupOrCreateList(\n args.LIST.id, args.LIST.name);\n const index = Cast.toListIndex(args.INDEX, list.value.length, false);\n if (index === Cast.LIST_INVALID) {\n return;\n }\n list.value[index - 1] = item;\n list._monitorUpToDate = false;\n }\n\n getItemOfList (args, util) {\n const list = util.target.lookupOrCreateList(\n args.LIST.id, args.LIST.name);\n const index = Cast.toListIndex(args.INDEX, list.value.length, false);\n if (index === Cast.LIST_INVALID) {\n return '';\n }\n return list.value[index - 1];\n }\n\n getItemNumOfList (args, util) {\n const item = args.ITEM;\n const list = util.target.lookupOrCreateList(\n args.LIST.id, args.LIST.name);\n\n // Go through the list items one-by-one using Cast.compare. This is for\n // cases like checking if 123 is contained in a list [4, 7, '123'] --\n // Scratch considers 123 and '123' to be equal.\n for (let i = 0; i < list.value.length; i++) {\n if (Cast.compare(list.value[i], item) === 0) {\n return i + 1;\n }\n }\n\n // We don't bother using .indexOf() at all, because it would end up with\n // edge cases such as the index of '123' in [4, 7, 123, '123', 9].\n // If we use indexOf(), this block would return 4 instead of 3, because\n // indexOf() sees the first occurence of the string 123 as the fourth\n // item in the list. With Scratch, this would be confusing -- after all,\n // '123' and 123 look the same, so one would expect the block to say\n // that the first occurrence of '123' (or 123) to be the third item.\n\n // Default to 0 if there's no match. Since Scratch lists are 1-indexed,\n // we don't have to worry about this conflicting with the \"this item is\n // the first value\" number (in JS that is 0, but in Scratch it's 1).\n return 0;\n }\n\n lengthOfList (args, util) {\n const list = util.target.lookupOrCreateList(\n args.LIST.id, args.LIST.name);\n return list.value.length;\n }\n\n listContainsItem (args, util) {\n const item = args.ITEM;\n const list = util.target.lookupOrCreateList(\n args.LIST.id, args.LIST.name);\n if (list.value.indexOf(item) >= 0) {\n return true;\n }\n // Try using Scratch comparison operator on each item.\n // (Scratch considers the string '123' equal to the number 123).\n for (let i = 0; i < list.value.length; i++) {\n if (Cast.compare(list.value[i], item) === 0) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Type representation for list variables.\n * @const {number}\n */\n static get LIST_ITEM_LIMIT () {\n return 200000;\n }\n}\n\nmodule.exports = Scratch3DataBlocks;\n","const Cast = require('../util/cast');\n\nclass Scratch3EventBlocks {\n constructor (runtime) {\n /**\n * The runtime instantiating this block package.\n * @type {Runtime}\n */\n this.runtime = runtime;\n\n this.runtime.on('KEY_PRESSED', key => {\n this.runtime.startHats('event_whenkeypressed', {\n KEY_OPTION: key\n });\n this.runtime.startHats('event_whenkeypressed', {\n KEY_OPTION: 'any'\n });\n });\n }\n\n /**\n * Retrieve the block primitives implemented by this package.\n * @return {object.} Mapping of opcode to Function.\n */\n getPrimitives () {\n return {\n event_whentouchingobject: this.touchingObject,\n event_broadcast: this.broadcast,\n event_broadcastandwait: this.broadcastAndWait,\n event_whengreaterthan: this.hatGreaterThanPredicate\n };\n }\n\n getHats () {\n return {\n event_whenflagclicked: {\n restartExistingThreads: true\n },\n event_whenkeypressed: {\n restartExistingThreads: false\n },\n event_whenthisspriteclicked: {\n restartExistingThreads: true\n },\n event_whentouchingobject: {\n restartExistingThreads: false,\n edgeActivated: true\n },\n event_whenstageclicked: {\n restartExistingThreads: true\n },\n event_whenbackdropswitchesto: {\n restartExistingThreads: true\n },\n event_whengreaterthan: {\n restartExistingThreads: false,\n edgeActivated: true\n },\n event_whenbroadcastreceived: {\n restartExistingThreads: true\n }\n };\n }\n\n touchingObject (args, util) {\n return util.target.isTouchingObject(args.TOUCHINGOBJECTMENU);\n }\n\n hatGreaterThanPredicate (args, util) {\n const option = Cast.toString(args.WHENGREATERTHANMENU).toLowerCase();\n const value = Cast.toNumber(args.VALUE);\n switch (option) {\n case 'timer':\n return util.ioQuery('clock', 'projectTimer') > value;\n case 'loudness':\n return this.runtime.audioEngine && this.runtime.audioEngine.getLoudness() > value;\n }\n return false;\n }\n\n broadcast (args, util) {\n const broadcastVar = util.runtime.getTargetForStage().lookupBroadcastMsg(\n args.BROADCAST_OPTION.id, args.BROADCAST_OPTION.name);\n if (broadcastVar) {\n const broadcastOption = broadcastVar.name;\n util.startHats('event_whenbroadcastreceived', {\n BROADCAST_OPTION: broadcastOption\n });\n }\n }\n\n broadcastAndWait (args, util) {\n if (!util.stackFrame.broadcastVar) {\n util.stackFrame.broadcastVar = util.runtime.getTargetForStage().lookupBroadcastMsg(\n args.BROADCAST_OPTION.id, args.BROADCAST_OPTION.name);\n }\n if (util.stackFrame.broadcastVar) {\n const broadcastOption = util.stackFrame.broadcastVar.name;\n // Have we run before, starting threads?\n if (!util.stackFrame.startedThreads) {\n // No - start hats for this broadcast.\n util.stackFrame.startedThreads = util.startHats(\n 'event_whenbroadcastreceived', {\n BROADCAST_OPTION: broadcastOption\n }\n );\n if (util.stackFrame.startedThreads.length === 0) {\n // Nothing was started.\n return;\n }\n }\n // We've run before; check if the wait is still going on.\n const instance = this;\n // Scratch 2 considers threads to be waiting if they are still in\n // runtime.threads. Threads that have run all their blocks, or are\n // marked done but still in runtime.threads are still considered to\n // be waiting.\n const waiting = util.stackFrame.startedThreads\n .some(thread => instance.runtime.threads.indexOf(thread) !== -1);\n if (waiting) {\n // If all threads are waiting for the next tick or later yield\n // for a tick as well. Otherwise yield until the next loop of\n // the threads.\n if (\n util.stackFrame.startedThreads\n .every(thread => instance.runtime.isWaitingThread(thread))\n ) {\n util.yieldTick();\n } else {\n util.yield();\n }\n }\n }\n }\n}\n\nmodule.exports = Scratch3EventBlocks;\n","const Cast = require('../util/cast');\nconst Clone = require('../util/clone');\nconst RenderedTarget = require('../sprites/rendered-target');\nconst uid = require('../util/uid');\nconst StageLayering = require('../engine/stage-layering');\nconst getMonitorIdForBlockWithArgs = require('../util/get-monitor-id');\nconst MathUtil = require('../util/math-util');\n\n/**\n * @typedef {object} BubbleState - the bubble state associated with a particular target.\n * @property {Boolean} onSpriteRight - tracks whether the bubble is right or left of the sprite.\n * @property {?int} drawableId - the ID of the associated bubble Drawable, null if none.\n * @property {string} text - the text of the bubble.\n * @property {string} type - the type of the bubble, \"say\" or \"think\"\n * @property {?string} usageId - ID indicating the most recent usage of the say/think bubble.\n * Used for comparison when determining whether to clear a say/think bubble.\n */\n\nclass Scratch3LooksBlocks {\n constructor (runtime) {\n /**\n * The runtime instantiating this block package.\n * @type {Runtime}\n */\n this.runtime = runtime;\n\n this._onTargetChanged = this._onTargetChanged.bind(this);\n this._onResetBubbles = this._onResetBubbles.bind(this);\n this._onTargetWillExit = this._onTargetWillExit.bind(this);\n this._updateBubble = this._updateBubble.bind(this);\n\n // Reset all bubbles on start/stop\n this.runtime.on('PROJECT_STOP_ALL', this._onResetBubbles);\n this.runtime.on('targetWasRemoved', this._onTargetWillExit);\n\n // Enable other blocks to use bubbles like ask/answer\n this.runtime.on(Scratch3LooksBlocks.SAY_OR_THINK, this._updateBubble);\n }\n\n /**\n * The default bubble state, to be used when a target has no existing bubble state.\n * @type {BubbleState}\n */\n static get DEFAULT_BUBBLE_STATE () {\n return {\n drawableId: null,\n onSpriteRight: true,\n skinId: null,\n text: '',\n type: 'say',\n usageId: null\n };\n }\n\n /**\n * The key to load & store a target's bubble-related state.\n * @type {string}\n */\n static get STATE_KEY () {\n return 'Scratch.looks';\n }\n\n /**\n * Event name for a text bubble being created or updated.\n * @const {string}\n */\n static get SAY_OR_THINK () {\n // There are currently many places in the codebase which explicitly refer to this event by the string 'SAY',\n // so keep this as the string 'SAY' for now rather than changing it to 'SAY_OR_THINK' and breaking things.\n return 'SAY';\n }\n\n /**\n * Limit for say bubble string.\n * @const {string}\n */\n static get SAY_BUBBLE_LIMIT () {\n return 330;\n }\n\n /**\n * Limit for ghost effect\n * @const {object}\n */\n static get EFFECT_GHOST_LIMIT (){\n return {min: 0, max: 100};\n }\n\n /**\n * Limit for brightness effect\n * @const {object}\n */\n static get EFFECT_BRIGHTNESS_LIMIT (){\n return {min: -100, max: 100};\n }\n\n /**\n * @param {Target} target - collect bubble state for this target. Probably, but not necessarily, a RenderedTarget.\n * @returns {BubbleState} the mutable bubble state associated with that target. This will be created if necessary.\n * @private\n */\n _getBubbleState (target) {\n let bubbleState = target.getCustomState(Scratch3LooksBlocks.STATE_KEY);\n if (!bubbleState) {\n bubbleState = Clone.simple(Scratch3LooksBlocks.DEFAULT_BUBBLE_STATE);\n target.setCustomState(Scratch3LooksBlocks.STATE_KEY, bubbleState);\n }\n return bubbleState;\n }\n\n /**\n * Handle a target which has moved.\n * @param {RenderedTarget} target - the target which has moved.\n * @private\n */\n _onTargetChanged (target) {\n const bubbleState = this._getBubbleState(target);\n if (bubbleState.drawableId) {\n this._positionBubble(target);\n }\n }\n\n /**\n * Handle a target which is exiting.\n * @param {RenderedTarget} target - the target.\n * @private\n */\n _onTargetWillExit (target) {\n const bubbleState = this._getBubbleState(target);\n if (bubbleState.drawableId && bubbleState.skinId) {\n this.runtime.renderer.destroyDrawable(bubbleState.drawableId, StageLayering.SPRITE_LAYER);\n this.runtime.renderer.destroySkin(bubbleState.skinId);\n bubbleState.drawableId = null;\n bubbleState.skinId = null;\n this.runtime.requestRedraw();\n }\n target.removeListener(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this._onTargetChanged);\n }\n\n /**\n * Handle project start/stop by clearing all visible bubbles.\n * @private\n */\n _onResetBubbles () {\n for (let n = 0; n < this.runtime.targets.length; n++) {\n const bubbleState = this._getBubbleState(this.runtime.targets[n]);\n bubbleState.text = '';\n this._onTargetWillExit(this.runtime.targets[n]);\n }\n clearTimeout(this._bubbleTimeout);\n }\n\n /**\n * Position the bubble of a target. If it doesn't fit on the specified side, flip and rerender.\n * @param {!Target} target Target whose bubble needs positioning.\n * @private\n */\n _positionBubble (target) {\n if (!target.visible) return;\n const bubbleState = this._getBubbleState(target);\n const [bubbleWidth, bubbleHeight] = this.runtime.renderer.getCurrentSkinSize(bubbleState.drawableId);\n let targetBounds;\n try {\n targetBounds = target.getBoundsForBubble();\n } catch (error_) {\n // Bounds calculation could fail (e.g. on empty costumes), in that case\n // use the x/y position of the target.\n targetBounds = {\n left: target.x,\n right: target.x,\n top: target.y,\n bottom: target.y\n };\n }\n const stageSize = this.runtime.renderer.getNativeSize();\n const stageBounds = {\n left: -stageSize[0] / 2,\n right: stageSize[0] / 2,\n top: stageSize[1] / 2,\n bottom: -stageSize[1] / 2\n };\n if (bubbleState.onSpriteRight && bubbleWidth + targetBounds.right > stageBounds.right &&\n (targetBounds.left - bubbleWidth > stageBounds.left)) { // Only flip if it would fit\n bubbleState.onSpriteRight = false;\n this._renderBubble(target);\n } else if (!bubbleState.onSpriteRight && targetBounds.left - bubbleWidth < stageBounds.left &&\n (bubbleWidth + targetBounds.right < stageBounds.right)) { // Only flip if it would fit\n bubbleState.onSpriteRight = true;\n this._renderBubble(target);\n } else {\n this.runtime.renderer.updateDrawablePosition(bubbleState.drawableId, [\n bubbleState.onSpriteRight ? (\n Math.max(\n stageBounds.left, // Bubble should not extend past left edge of stage\n Math.min(stageBounds.right - bubbleWidth, targetBounds.right)\n )\n ) : (\n Math.min(\n stageBounds.right - bubbleWidth, // Bubble should not extend past right edge of stage\n Math.max(stageBounds.left, targetBounds.left - bubbleWidth)\n )\n ),\n // Bubble should not extend past the top of the stage\n Math.min(stageBounds.top, targetBounds.bottom + bubbleHeight)\n ]);\n this.runtime.requestRedraw();\n }\n }\n\n /**\n * Create a visible bubble for a target. If a bubble exists for the target,\n * just set it to visible and update the type/text. Otherwise create a new\n * bubble and update the relevant custom state.\n * @param {!Target} target Target who needs a bubble.\n * @return {undefined} Early return if text is empty string.\n * @private\n */\n _renderBubble (target) {\n if (!this.runtime.renderer) return;\n\n const bubbleState = this._getBubbleState(target);\n const {type, text, onSpriteRight} = bubbleState;\n\n // Remove the bubble if target is not visible, or text is being set to blank.\n if (!target.visible || text === '') {\n this._onTargetWillExit(target);\n return;\n }\n\n if (bubbleState.skinId) {\n this.runtime.renderer.updateTextSkin(bubbleState.skinId, type, text, onSpriteRight, [0, 0]);\n } else {\n target.addListener(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this._onTargetChanged);\n bubbleState.drawableId = this.runtime.renderer.createDrawable(StageLayering.SPRITE_LAYER);\n bubbleState.skinId = this.runtime.renderer.createTextSkin(type, text, bubbleState.onSpriteRight, [0, 0]);\n this.runtime.renderer.updateDrawableSkinId(bubbleState.drawableId, bubbleState.skinId);\n }\n\n this._positionBubble(target);\n }\n\n /**\n * Properly format text for a text bubble.\n * @param {string} text The text to be formatted\n * @return {string} The formatted text\n * @private\n */\n _formatBubbleText (text) {\n if (text === '') return text;\n\n // Non-integers should be rounded to 2 decimal places (no more, no less), unless they're small enough that\n // rounding would display them as 0.00. This matches 2.0's behavior:\n // https://github.com/scratchfoundation/scratch-flash/blob/2e4a402ceb205a042887f54b26eebe1c2e6da6c0/src/scratch/ScratchSprite.as#L579-L585\n if (typeof text === 'number' &&\n Math.abs(text) >= 0.01 && text % 1 !== 0) {\n text = text.toFixed(2);\n }\n\n // Limit the length of the string.\n text = String(text).substr(0, Scratch3LooksBlocks.SAY_BUBBLE_LIMIT);\n\n return text;\n }\n\n /**\n * The entry point for say/think blocks. Clears existing bubble if the text is empty.\n * Set the bubble custom state and then call _renderBubble.\n * @param {!Target} target Target that say/think blocks are being called on.\n * @param {!string} type Either \"say\" or \"think\"\n * @param {!string} text The text for the bubble, empty string clears the bubble.\n * @private\n */\n _updateBubble (target, type, text) {\n const bubbleState = this._getBubbleState(target);\n bubbleState.type = type;\n bubbleState.text = this._formatBubbleText(text);\n bubbleState.usageId = uid();\n this._renderBubble(target);\n }\n\n /**\n * Retrieve the block primitives implemented by this package.\n * @return {object.} Mapping of opcode to Function.\n */\n getPrimitives () {\n return {\n looks_say: this.say,\n looks_sayforsecs: this.sayforsecs,\n looks_think: this.think,\n looks_thinkforsecs: this.thinkforsecs,\n looks_show: this.show,\n looks_hide: this.hide,\n looks_hideallsprites: () => {}, // legacy no-op block\n looks_switchcostumeto: this.switchCostume,\n looks_switchbackdropto: this.switchBackdrop,\n looks_switchbackdroptoandwait: this.switchBackdropAndWait,\n looks_nextcostume: this.nextCostume,\n looks_nextbackdrop: this.nextBackdrop,\n looks_changeeffectby: this.changeEffect,\n looks_seteffectto: this.setEffect,\n looks_cleargraphiceffects: this.clearEffects,\n looks_changesizeby: this.changeSize,\n looks_setsizeto: this.setSize,\n looks_changestretchby: () => {}, // legacy no-op blocks\n looks_setstretchto: () => {},\n looks_gotofrontback: this.goToFrontBack,\n looks_goforwardbackwardlayers: this.goForwardBackwardLayers,\n looks_size: this.getSize,\n looks_costumenumbername: this.getCostumeNumberName,\n looks_backdropnumbername: this.getBackdropNumberName\n };\n }\n\n getMonitored () {\n return {\n looks_size: {\n isSpriteSpecific: true,\n getId: targetId => `${targetId}_size`\n },\n looks_costumenumbername: {\n isSpriteSpecific: true,\n getId: (targetId, fields) => getMonitorIdForBlockWithArgs(`${targetId}_costumenumbername`, fields)\n },\n looks_backdropnumbername: {\n getId: (_, fields) => getMonitorIdForBlockWithArgs('backdropnumbername', fields)\n }\n };\n }\n\n say (args, util) {\n // @TODO in 2.0 calling say/think resets the right/left bias of the bubble\n this.runtime.emit(Scratch3LooksBlocks.SAY_OR_THINK, util.target, 'say', args.MESSAGE);\n }\n\n sayforsecs (args, util) {\n this.say(args, util);\n const target = util.target;\n const usageId = this._getBubbleState(target).usageId;\n return new Promise(resolve => {\n this._bubbleTimeout = setTimeout(() => {\n this._bubbleTimeout = null;\n // Clear say bubble if it hasn't been changed and proceed.\n if (this._getBubbleState(target).usageId === usageId) {\n this._updateBubble(target, 'say', '');\n }\n resolve();\n }, 1000 * args.SECS);\n });\n }\n\n think (args, util) {\n this.runtime.emit(Scratch3LooksBlocks.SAY_OR_THINK, util.target, 'think', args.MESSAGE);\n }\n\n thinkforsecs (args, util) {\n this.think(args, util);\n const target = util.target;\n const usageId = this._getBubbleState(target).usageId;\n return new Promise(resolve => {\n this._bubbleTimeout = setTimeout(() => {\n this._bubbleTimeout = null;\n // Clear think bubble if it hasn't been changed and proceed.\n if (this._getBubbleState(target).usageId === usageId) {\n this._updateBubble(target, 'think', '');\n }\n resolve();\n }, 1000 * args.SECS);\n });\n }\n\n show (args, util) {\n util.target.setVisible(true);\n this._renderBubble(util.target);\n }\n\n hide (args, util) {\n util.target.setVisible(false);\n this._renderBubble(util.target);\n }\n\n /**\n * Utility function to set the costume of a target.\n * Matches the behavior of Scratch 2.0 for different types of arguments.\n * @param {!Target} target Target to set costume to.\n * @param {Any} requestedCostume Costume requested, e.g., 0, 'name', etc.\n * @param {boolean=} optZeroIndex Set to zero-index the requestedCostume.\n * @return {Array.} Any threads started by this switch.\n */\n _setCostume (target, requestedCostume, optZeroIndex) {\n if (typeof requestedCostume === 'number') {\n // Numbers should be treated as costume indices, always\n target.setCostume(optZeroIndex ? requestedCostume : requestedCostume - 1);\n } else {\n // Strings should be treated as costume names, where possible\n const costumeIndex = target.getCostumeIndexByName(requestedCostume.toString());\n\n if (costumeIndex !== -1) {\n target.setCostume(costumeIndex);\n } else if (requestedCostume === 'next costume') {\n target.setCostume(target.currentCostume + 1);\n } else if (requestedCostume === 'previous costume') {\n target.setCostume(target.currentCostume - 1);\n // Try to cast the string to a number (and treat it as a costume index)\n // Pure whitespace should not be treated as a number\n // Note: isNaN will cast the string to a number before checking if it's NaN\n } else if (!(isNaN(requestedCostume) || Cast.isWhiteSpace(requestedCostume))) {\n target.setCostume(optZeroIndex ? Number(requestedCostume) : Number(requestedCostume) - 1);\n }\n }\n\n // Per 2.0, 'switch costume' can't start threads even in the Stage.\n return [];\n }\n\n /**\n * Utility function to set the backdrop of a target.\n * Matches the behavior of Scratch 2.0 for different types of arguments.\n * @param {!Target} stage Target to set backdrop to.\n * @param {Any} requestedBackdrop Backdrop requested, e.g., 0, 'name', etc.\n * @param {boolean=} optZeroIndex Set to zero-index the requestedBackdrop.\n * @return {Array.} Any threads started by this switch.\n */\n _setBackdrop (stage, requestedBackdrop, optZeroIndex) {\n if (typeof requestedBackdrop === 'number') {\n // Numbers should be treated as backdrop indices, always\n stage.setCostume(optZeroIndex ? requestedBackdrop : requestedBackdrop - 1);\n } else {\n // Strings should be treated as backdrop names where possible\n const costumeIndex = stage.getCostumeIndexByName(requestedBackdrop.toString());\n\n if (costumeIndex !== -1) {\n stage.setCostume(costumeIndex);\n } else if (requestedBackdrop === 'next backdrop') {\n stage.setCostume(stage.currentCostume + 1);\n } else if (requestedBackdrop === 'previous backdrop') {\n stage.setCostume(stage.currentCostume - 1);\n } else if (requestedBackdrop === 'random backdrop') {\n const numCostumes = stage.getCostumes().length;\n if (numCostumes > 1) {\n // Don't pick the current backdrop, so that the block\n // will always have an observable effect.\n const lowerBound = 0;\n const upperBound = numCostumes - 1;\n const costumeToExclude = stage.currentCostume;\n\n const nextCostume = MathUtil.inclusiveRandIntWithout(lowerBound, upperBound, costumeToExclude);\n\n stage.setCostume(nextCostume);\n }\n // Try to cast the string to a number (and treat it as a costume index)\n // Pure whitespace should not be treated as a number\n // Note: isNaN will cast the string to a number before checking if it's NaN\n } else if (!(isNaN(requestedBackdrop) || Cast.isWhiteSpace(requestedBackdrop))) {\n stage.setCostume(optZeroIndex ? Number(requestedBackdrop) : Number(requestedBackdrop) - 1);\n }\n }\n\n const newName = stage.getCostumes()[stage.currentCostume].name;\n return this.runtime.startHats('event_whenbackdropswitchesto', {\n BACKDROP: newName\n });\n }\n\n switchCostume (args, util) {\n this._setCostume(util.target, args.COSTUME);\n }\n\n nextCostume (args, util) {\n this._setCostume(\n util.target, util.target.currentCostume + 1, true\n );\n }\n\n switchBackdrop (args) {\n this._setBackdrop(this.runtime.getTargetForStage(), args.BACKDROP);\n }\n\n switchBackdropAndWait (args, util) {\n // Have we run before, starting threads?\n if (!util.stackFrame.startedThreads) {\n // No - switch the backdrop.\n util.stackFrame.startedThreads = (\n this._setBackdrop(\n this.runtime.getTargetForStage(),\n args.BACKDROP\n )\n );\n if (util.stackFrame.startedThreads.length === 0) {\n // Nothing was started.\n return;\n }\n }\n // We've run before; check if the wait is still going on.\n const instance = this;\n // Scratch 2 considers threads to be waiting if they are still in\n // runtime.threads. Threads that have run all their blocks, or are\n // marked done but still in runtime.threads are still considered to\n // be waiting.\n const waiting = util.stackFrame.startedThreads\n .some(thread => instance.runtime.threads.indexOf(thread) !== -1);\n if (waiting) {\n // If all threads are waiting for the next tick or later yield\n // for a tick as well. Otherwise yield until the next loop of\n // the threads.\n if (\n util.stackFrame.startedThreads\n .every(thread => instance.runtime.isWaitingThread(thread))\n ) {\n util.yieldTick();\n } else {\n util.yield();\n }\n }\n }\n\n nextBackdrop () {\n const stage = this.runtime.getTargetForStage();\n this._setBackdrop(\n stage, stage.currentCostume + 1, true\n );\n }\n\n clampEffect (effect, value) {\n let clampedValue = value;\n switch (effect) {\n case 'ghost':\n clampedValue = MathUtil.clamp(value,\n Scratch3LooksBlocks.EFFECT_GHOST_LIMIT.min,\n Scratch3LooksBlocks.EFFECT_GHOST_LIMIT.max);\n break;\n case 'brightness':\n clampedValue = MathUtil.clamp(value,\n Scratch3LooksBlocks.EFFECT_BRIGHTNESS_LIMIT.min,\n Scratch3LooksBlocks.EFFECT_BRIGHTNESS_LIMIT.max);\n break;\n }\n return clampedValue;\n }\n\n changeEffect (args, util) {\n const effect = Cast.toString(args.EFFECT).toLowerCase();\n const change = Cast.toNumber(args.CHANGE);\n if (!Object.prototype.hasOwnProperty.call(util.target.effects, effect)) return;\n let newValue = change + util.target.effects[effect];\n newValue = this.clampEffect(effect, newValue);\n util.target.setEffect(effect, newValue);\n }\n\n setEffect (args, util) {\n const effect = Cast.toString(args.EFFECT).toLowerCase();\n let value = Cast.toNumber(args.VALUE);\n value = this.clampEffect(effect, value);\n util.target.setEffect(effect, value);\n }\n\n clearEffects (args, util) {\n util.target.clearEffects();\n }\n\n changeSize (args, util) {\n const change = Cast.toNumber(args.CHANGE);\n util.target.setSize(util.target.size + change);\n }\n\n setSize (args, util) {\n const size = Cast.toNumber(args.SIZE);\n util.target.setSize(size);\n }\n\n goToFrontBack (args, util) {\n if (!util.target.isStage) {\n if (args.FRONT_BACK === 'front') {\n util.target.goToFront();\n } else {\n util.target.goToBack();\n }\n }\n }\n\n goForwardBackwardLayers (args, util) {\n if (!util.target.isStage) {\n if (args.FORWARD_BACKWARD === 'forward') {\n util.target.goForwardLayers(Cast.toNumber(args.NUM));\n } else {\n util.target.goBackwardLayers(Cast.toNumber(args.NUM));\n }\n }\n }\n\n getSize (args, util) {\n return Math.round(util.target.size);\n }\n\n getBackdropNumberName (args) {\n const stage = this.runtime.getTargetForStage();\n if (args.NUMBER_NAME === 'number') {\n return stage.currentCostume + 1;\n }\n // Else return name\n return stage.getCostumes()[stage.currentCostume].name;\n }\n\n getCostumeNumberName (args, util) {\n if (args.NUMBER_NAME === 'number') {\n return util.target.currentCostume + 1;\n }\n // Else return name\n return util.target.getCostumes()[util.target.currentCostume].name;\n }\n}\n\nmodule.exports = Scratch3LooksBlocks;\n","const Cast = require('../util/cast');\nconst MathUtil = require('../util/math-util');\nconst Timer = require('../util/timer');\n\nclass Scratch3MotionBlocks {\n constructor (runtime) {\n /**\n * The runtime instantiating this block package.\n * @type {Runtime}\n */\n this.runtime = runtime;\n }\n\n /**\n * Retrieve the block primitives implemented by this package.\n * @return {object.} Mapping of opcode to Function.\n */\n getPrimitives () {\n return {\n motion_movesteps: this.moveSteps,\n motion_gotoxy: this.goToXY,\n motion_goto: this.goTo,\n motion_turnright: this.turnRight,\n motion_turnleft: this.turnLeft,\n motion_pointindirection: this.pointInDirection,\n motion_pointtowards: this.pointTowards,\n motion_glidesecstoxy: this.glide,\n motion_glideto: this.glideTo,\n motion_ifonedgebounce: this.ifOnEdgeBounce,\n motion_setrotationstyle: this.setRotationStyle,\n motion_changexby: this.changeX,\n motion_setx: this.setX,\n motion_changeyby: this.changeY,\n motion_sety: this.setY,\n motion_xposition: this.getX,\n motion_yposition: this.getY,\n motion_direction: this.getDirection,\n // Legacy no-op blocks:\n motion_scroll_right: () => {},\n motion_scroll_up: () => {},\n motion_align_scene: () => {},\n motion_xscroll: () => {},\n motion_yscroll: () => {}\n };\n }\n\n getMonitored () {\n return {\n motion_xposition: {\n isSpriteSpecific: true,\n getId: targetId => `${targetId}_xposition`\n },\n motion_yposition: {\n isSpriteSpecific: true,\n getId: targetId => `${targetId}_yposition`\n },\n motion_direction: {\n isSpriteSpecific: true,\n getId: targetId => `${targetId}_direction`\n }\n };\n }\n\n moveSteps (args, util) {\n const steps = Cast.toNumber(args.STEPS);\n const radians = MathUtil.degToRad(90 - util.target.direction);\n const dx = steps * Math.cos(radians);\n const dy = steps * Math.sin(radians);\n util.target.setXY(util.target.x + dx, util.target.y + dy);\n }\n\n goToXY (args, util) {\n const x = Cast.toNumber(args.X);\n const y = Cast.toNumber(args.Y);\n util.target.setXY(x, y);\n }\n\n getTargetXY (targetName, util) {\n let targetX = 0;\n let targetY = 0;\n if (targetName === '_mouse_') {\n targetX = util.ioQuery('mouse', 'getScratchX');\n targetY = util.ioQuery('mouse', 'getScratchY');\n } else if (targetName === '_random_') {\n const stageWidth = this.runtime.constructor.STAGE_WIDTH;\n const stageHeight = this.runtime.constructor.STAGE_HEIGHT;\n targetX = Math.round(stageWidth * (Math.random() - 0.5));\n targetY = Math.round(stageHeight * (Math.random() - 0.5));\n } else {\n targetName = Cast.toString(targetName);\n const goToTarget = this.runtime.getSpriteTargetByName(targetName);\n if (!goToTarget) return;\n targetX = goToTarget.x;\n targetY = goToTarget.y;\n }\n return [targetX, targetY];\n }\n\n goTo (args, util) {\n const targetXY = this.getTargetXY(args.TO, util);\n if (targetXY) {\n util.target.setXY(targetXY[0], targetXY[1]);\n }\n }\n\n turnRight (args, util) {\n const degrees = Cast.toNumber(args.DEGREES);\n util.target.setDirection(util.target.direction + degrees);\n }\n\n turnLeft (args, util) {\n const degrees = Cast.toNumber(args.DEGREES);\n util.target.setDirection(util.target.direction - degrees);\n }\n\n pointInDirection (args, util) {\n const direction = Cast.toNumber(args.DIRECTION);\n util.target.setDirection(direction);\n }\n\n pointTowards (args, util) {\n let targetX = 0;\n let targetY = 0;\n if (args.TOWARDS === '_mouse_') {\n targetX = util.ioQuery('mouse', 'getScratchX');\n targetY = util.ioQuery('mouse', 'getScratchY');\n } else if (args.TOWARDS === '_random_') {\n util.target.setDirection(Math.round(Math.random() * 360) - 180);\n return;\n } else {\n args.TOWARDS = Cast.toString(args.TOWARDS);\n const pointTarget = this.runtime.getSpriteTargetByName(args.TOWARDS);\n if (!pointTarget) return;\n targetX = pointTarget.x;\n targetY = pointTarget.y;\n }\n\n const dx = targetX - util.target.x;\n const dy = targetY - util.target.y;\n const direction = 90 - MathUtil.radToDeg(Math.atan2(dy, dx));\n util.target.setDirection(direction);\n }\n\n glide (args, util) {\n if (util.stackFrame.timer) {\n const timeElapsed = util.stackFrame.timer.timeElapsed();\n if (timeElapsed < util.stackFrame.duration * 1000) {\n // In progress: move to intermediate position.\n const frac = timeElapsed / (util.stackFrame.duration * 1000);\n const dx = frac * (util.stackFrame.endX - util.stackFrame.startX);\n const dy = frac * (util.stackFrame.endY - util.stackFrame.startY);\n util.target.setXY(\n util.stackFrame.startX + dx,\n util.stackFrame.startY + dy\n );\n util.yield();\n } else {\n // Finished: move to final position.\n util.target.setXY(util.stackFrame.endX, util.stackFrame.endY);\n }\n } else {\n // First time: save data for future use.\n util.stackFrame.timer = new Timer();\n util.stackFrame.timer.start();\n util.stackFrame.duration = Cast.toNumber(args.SECS);\n util.stackFrame.startX = util.target.x;\n util.stackFrame.startY = util.target.y;\n util.stackFrame.endX = Cast.toNumber(args.X);\n util.stackFrame.endY = Cast.toNumber(args.Y);\n if (util.stackFrame.duration <= 0) {\n // Duration too short to glide.\n util.target.setXY(util.stackFrame.endX, util.stackFrame.endY);\n return;\n }\n util.yield();\n }\n }\n\n glideTo (args, util) {\n const targetXY = this.getTargetXY(args.TO, util);\n if (targetXY) {\n this.glide({SECS: args.SECS, X: targetXY[0], Y: targetXY[1]}, util);\n }\n }\n\n ifOnEdgeBounce (args, util) {\n const bounds = util.target.getBounds();\n if (!bounds) {\n return;\n }\n // Measure distance to edges.\n // Values are positive when the sprite is far away,\n // and clamped to zero when the sprite is beyond.\n const stageWidth = this.runtime.constructor.STAGE_WIDTH;\n const stageHeight = this.runtime.constructor.STAGE_HEIGHT;\n const distLeft = Math.max(0, (stageWidth / 2) + bounds.left);\n const distTop = Math.max(0, (stageHeight / 2) - bounds.top);\n const distRight = Math.max(0, (stageWidth / 2) - bounds.right);\n const distBottom = Math.max(0, (stageHeight / 2) + bounds.bottom);\n // Find the nearest edge.\n let nearestEdge = '';\n let minDist = Infinity;\n if (distLeft < minDist) {\n minDist = distLeft;\n nearestEdge = 'left';\n }\n if (distTop < minDist) {\n minDist = distTop;\n nearestEdge = 'top';\n }\n if (distRight < minDist) {\n minDist = distRight;\n nearestEdge = 'right';\n }\n if (distBottom < minDist) {\n minDist = distBottom;\n nearestEdge = 'bottom';\n }\n if (minDist > 0) {\n return; // Not touching any edge.\n }\n // Point away from the nearest edge.\n const radians = MathUtil.degToRad(90 - util.target.direction);\n let dx = Math.cos(radians);\n let dy = -Math.sin(radians);\n if (nearestEdge === 'left') {\n dx = Math.max(0.2, Math.abs(dx));\n } else if (nearestEdge === 'top') {\n dy = Math.max(0.2, Math.abs(dy));\n } else if (nearestEdge === 'right') {\n dx = 0 - Math.max(0.2, Math.abs(dx));\n } else if (nearestEdge === 'bottom') {\n dy = 0 - Math.max(0.2, Math.abs(dy));\n }\n const newDirection = MathUtil.radToDeg(Math.atan2(dy, dx)) + 90;\n util.target.setDirection(newDirection);\n // Keep within the stage.\n const fencedPosition = util.target.keepInFence(util.target.x, util.target.y);\n util.target.setXY(fencedPosition[0], fencedPosition[1]);\n }\n\n setRotationStyle (args, util) {\n util.target.setRotationStyle(args.STYLE);\n }\n\n changeX (args, util) {\n const dx = Cast.toNumber(args.DX);\n util.target.setXY(util.target.x + dx, util.target.y);\n }\n\n setX (args, util) {\n const x = Cast.toNumber(args.X);\n util.target.setXY(x, util.target.y);\n }\n\n changeY (args, util) {\n const dy = Cast.toNumber(args.DY);\n util.target.setXY(util.target.x, util.target.y + dy);\n }\n\n setY (args, util) {\n const y = Cast.toNumber(args.Y);\n util.target.setXY(util.target.x, y);\n }\n\n getX (args, util) {\n return this.limitPrecision(util.target.x);\n }\n\n getY (args, util) {\n return this.limitPrecision(util.target.y);\n }\n\n getDirection (args, util) {\n return util.target.direction;\n }\n\n // This corresponds to snapToInteger in Scratch 2\n limitPrecision (coordinate) {\n const rounded = Math.round(coordinate);\n const delta = coordinate - rounded;\n const limitedCoord = (Math.abs(delta) < 1e-9) ? rounded : coordinate;\n\n return limitedCoord;\n }\n}\n\nmodule.exports = Scratch3MotionBlocks;\n","const Cast = require('../util/cast.js');\nconst MathUtil = require('../util/math-util.js');\n\nclass Scratch3OperatorsBlocks {\n constructor (runtime) {\n /**\n * The runtime instantiating this block package.\n * @type {Runtime}\n */\n this.runtime = runtime;\n }\n\n /**\n * Retrieve the block primitives implemented by this package.\n * @return {object.} Mapping of opcode to Function.\n */\n getPrimitives () {\n return {\n operator_add: this.add,\n operator_subtract: this.subtract,\n operator_multiply: this.multiply,\n operator_divide: this.divide,\n operator_lt: this.lt,\n operator_equals: this.equals,\n operator_gt: this.gt,\n operator_and: this.and,\n operator_or: this.or,\n operator_not: this.not,\n operator_random: this.random,\n operator_join: this.join,\n operator_letter_of: this.letterOf,\n operator_length: this.length,\n operator_contains: this.contains,\n operator_mod: this.mod,\n operator_round: this.round,\n operator_mathop: this.mathop\n };\n }\n\n add (args) {\n return Cast.toNumber(args.NUM1) + Cast.toNumber(args.NUM2);\n }\n\n subtract (args) {\n return Cast.toNumber(args.NUM1) - Cast.toNumber(args.NUM2);\n }\n\n multiply (args) {\n return Cast.toNumber(args.NUM1) * Cast.toNumber(args.NUM2);\n }\n\n divide (args) {\n return Cast.toNumber(args.NUM1) / Cast.toNumber(args.NUM2);\n }\n\n lt (args) {\n return Cast.compare(args.OPERAND1, args.OPERAND2) < 0;\n }\n\n equals (args) {\n return Cast.compare(args.OPERAND1, args.OPERAND2) === 0;\n }\n\n gt (args) {\n return Cast.compare(args.OPERAND1, args.OPERAND2) > 0;\n }\n\n and (args) {\n return Cast.toBoolean(args.OPERAND1) && Cast.toBoolean(args.OPERAND2);\n }\n\n or (args) {\n return Cast.toBoolean(args.OPERAND1) || Cast.toBoolean(args.OPERAND2);\n }\n\n not (args) {\n return !Cast.toBoolean(args.OPERAND);\n }\n\n random (args) {\n const nFrom = Cast.toNumber(args.FROM);\n const nTo = Cast.toNumber(args.TO);\n const low = nFrom <= nTo ? nFrom : nTo;\n const high = nFrom <= nTo ? nTo : nFrom;\n if (low === high) return low;\n // If both arguments are ints, truncate the result to an int.\n if (Cast.isInt(args.FROM) && Cast.isInt(args.TO)) {\n return low + Math.floor(Math.random() * ((high + 1) - low));\n }\n return (Math.random() * (high - low)) + low;\n }\n\n join (args) {\n return Cast.toString(args.STRING1) + Cast.toString(args.STRING2);\n }\n\n letterOf (args) {\n const index = Cast.toNumber(args.LETTER) - 1;\n const str = Cast.toString(args.STRING);\n // Out of bounds?\n if (index < 0 || index >= str.length) {\n return '';\n }\n return str.charAt(index);\n }\n\n length (args) {\n return Cast.toString(args.STRING).length;\n }\n\n contains (args) {\n const format = function (string) {\n return Cast.toString(string).toLowerCase();\n };\n return format(args.STRING1).includes(format(args.STRING2));\n }\n\n mod (args) {\n const n = Cast.toNumber(args.NUM1);\n const modulus = Cast.toNumber(args.NUM2);\n let result = n % modulus;\n // Scratch mod uses floored division instead of truncated division.\n if (result / modulus < 0) result += modulus;\n return result;\n }\n\n round (args) {\n return Math.round(Cast.toNumber(args.NUM));\n }\n\n mathop (args) {\n const operator = Cast.toString(args.OPERATOR).toLowerCase();\n const n = Cast.toNumber(args.NUM);\n switch (operator) {\n case 'abs': return Math.abs(n);\n case 'floor': return Math.floor(n);\n case 'ceiling': return Math.ceil(n);\n case 'sqrt': return Math.sqrt(n);\n case 'sin': return parseFloat(Math.sin((Math.PI * n) / 180).toFixed(10));\n case 'cos': return parseFloat(Math.cos((Math.PI * n) / 180).toFixed(10));\n case 'tan': return MathUtil.tan(n);\n case 'asin': return (Math.asin(n) * 180) / Math.PI;\n case 'acos': return (Math.acos(n) * 180) / Math.PI;\n case 'atan': return (Math.atan(n) * 180) / Math.PI;\n case 'ln': return Math.log(n);\n case 'log': return Math.log(n) / Math.LN10;\n case 'e ^': return Math.exp(n);\n case '10 ^': return Math.pow(10, n);\n }\n return 0;\n }\n}\n\nmodule.exports = Scratch3OperatorsBlocks;\n","class Scratch3ProcedureBlocks {\n constructor (runtime) {\n /**\n * The runtime instantiating this block package.\n * @type {Runtime}\n */\n this.runtime = runtime;\n }\n\n /**\n * Retrieve the block primitives implemented by this package.\n * @return {object.} Mapping of opcode to Function.\n */\n getPrimitives () {\n return {\n procedures_definition: this.definition,\n procedures_call: this.call,\n argument_reporter_string_number: this.argumentReporterStringNumber,\n argument_reporter_boolean: this.argumentReporterBoolean\n };\n }\n\n definition () {\n // No-op: execute the blocks.\n }\n\n call (args, util) {\n if (!util.stackFrame.executed) {\n const procedureCode = args.mutation.proccode;\n const paramNamesIdsAndDefaults = util.getProcedureParamNamesIdsAndDefaults(procedureCode);\n\n // If null, procedure could not be found, which can happen if custom\n // block is dragged between sprites without the definition.\n // Match Scratch 2.0 behavior and noop.\n if (paramNamesIdsAndDefaults === null) {\n return;\n }\n\n const [paramNames, paramIds, paramDefaults] = paramNamesIdsAndDefaults;\n\n // Initialize params for the current stackFrame to {}, even if the procedure does\n // not take any arguments. This is so that `getParam` down the line does not look\n // at earlier stack frames for the values of a given parameter (#1729)\n util.initParams();\n for (let i = 0; i < paramIds.length; i++) {\n if (Object.prototype.hasOwnProperty.call(args, paramIds[i])) {\n util.pushParam(paramNames[i], args[paramIds[i]]);\n } else {\n util.pushParam(paramNames[i], paramDefaults[i]);\n }\n }\n\n util.stackFrame.executed = true;\n util.startProcedure(procedureCode);\n }\n }\n\n argumentReporterStringNumber (args, util) {\n const value = util.getParam(args.VALUE);\n if (value === null) {\n // When the parameter is not found in the most recent procedure\n // call, the default is always 0.\n return 0;\n }\n return value;\n }\n\n argumentReporterBoolean (args, util) {\n const value = util.getParam(args.VALUE);\n if (value === null) {\n // When the parameter is not found in the most recent procedure\n // call, the default is always 0.\n return 0;\n }\n return value;\n }\n}\n\nmodule.exports = Scratch3ProcedureBlocks;\n","const Cast = require('../util/cast');\nconst Timer = require('../util/timer');\nconst getMonitorIdForBlockWithArgs = require('../util/get-monitor-id');\n\nclass Scratch3SensingBlocks {\n constructor (runtime) {\n /**\n * The runtime instantiating this block package.\n * @type {Runtime}\n */\n this.runtime = runtime;\n\n /**\n * The \"answer\" block value.\n * @type {string}\n */\n this._answer = '';\n\n /**\n * The timer utility.\n * @type {Timer}\n */\n this._timer = new Timer();\n\n /**\n * The stored microphone loudness measurement.\n * @type {number}\n */\n this._cachedLoudness = -1;\n\n /**\n * The time of the most recent microphone loudness measurement.\n * @type {number}\n */\n this._cachedLoudnessTimestamp = 0;\n\n /**\n * The list of queued questions and respective `resolve` callbacks.\n * @type {!Array}\n */\n this._questionList = [];\n\n this.runtime.on('ANSWER', this._onAnswer.bind(this));\n this.runtime.on('PROJECT_START', this._resetAnswer.bind(this));\n this.runtime.on('PROJECT_STOP_ALL', this._clearAllQuestions.bind(this));\n this.runtime.on('STOP_FOR_TARGET', this._clearTargetQuestions.bind(this));\n this.runtime.on('RUNTIME_DISPOSED', this._resetAnswer.bind(this));\n }\n\n /**\n * Retrieve the block primitives implemented by this package.\n * @return {object.} Mapping of opcode to Function.\n */\n getPrimitives () {\n return {\n sensing_touchingobject: this.touchingObject,\n sensing_touchingcolor: this.touchingColor,\n sensing_coloristouchingcolor: this.colorTouchingColor,\n sensing_distanceto: this.distanceTo,\n sensing_timer: this.getTimer,\n sensing_resettimer: this.resetTimer,\n sensing_of: this.getAttributeOf,\n sensing_mousex: this.getMouseX,\n sensing_mousey: this.getMouseY,\n sensing_setdragmode: this.setDragMode,\n sensing_mousedown: this.getMouseDown,\n sensing_keypressed: this.getKeyPressed,\n sensing_current: this.current,\n sensing_dayssince2000: this.daysSince2000,\n sensing_loudness: this.getLoudness,\n sensing_loud: this.isLoud,\n sensing_askandwait: this.askAndWait,\n sensing_answer: this.getAnswer,\n sensing_username: this.getUsername,\n sensing_userid: () => {} // legacy no-op block\n };\n }\n\n getMonitored () {\n return {\n sensing_answer: {\n getId: () => 'answer'\n },\n sensing_loudness: {\n getId: () => 'loudness'\n },\n sensing_timer: {\n getId: () => 'timer'\n },\n sensing_current: {\n // This is different from the default toolbox xml id in order to support\n // importing multiple monitors from the same opcode from sb2 files,\n // something that is not currently supported in scratch 3.\n getId: (_, fields) => getMonitorIdForBlockWithArgs('current', fields) // _${param}`\n }\n };\n }\n\n _onAnswer (answer) {\n this._answer = answer;\n const questionObj = this._questionList.shift();\n if (questionObj) {\n const [_question, resolve, target, wasVisible, wasStage] = questionObj;\n // If the target was visible when asked, hide the say bubble unless the target was the stage.\n if (wasVisible && !wasStage) {\n this.runtime.emit('SAY', target, 'say', '');\n }\n resolve();\n this._askNextQuestion();\n }\n }\n\n _resetAnswer () {\n this._answer = '';\n }\n\n _enqueueAsk (question, resolve, target, wasVisible, wasStage) {\n this._questionList.push([question, resolve, target, wasVisible, wasStage]);\n }\n\n _askNextQuestion () {\n if (this._questionList.length > 0) {\n const [question, _resolve, target, wasVisible, wasStage] = this._questionList[0];\n // If the target is visible, emit a blank question and use the\n // say event to trigger a bubble unless the target was the stage.\n if (wasVisible && !wasStage) {\n this.runtime.emit('SAY', target, 'say', question);\n this.runtime.emit('QUESTION', '');\n } else {\n this.runtime.emit('QUESTION', question);\n }\n }\n }\n\n _clearAllQuestions () {\n this._questionList = [];\n this.runtime.emit('QUESTION', null);\n }\n\n _clearTargetQuestions (stopTarget) {\n const currentlyAsking = this._questionList.length > 0 && this._questionList[0][2] === stopTarget;\n this._questionList = this._questionList.filter(question => (\n question[2] !== stopTarget\n ));\n\n if (currentlyAsking) {\n this.runtime.emit('SAY', stopTarget, 'say', '');\n if (this._questionList.length > 0) {\n this._askNextQuestion();\n } else {\n this.runtime.emit('QUESTION', null);\n }\n }\n }\n\n askAndWait (args, util) {\n const _target = util.target;\n return new Promise(resolve => {\n const isQuestionAsked = this._questionList.length > 0;\n this._enqueueAsk(String(args.QUESTION), resolve, _target, _target.visible, _target.isStage);\n if (!isQuestionAsked) {\n this._askNextQuestion();\n }\n });\n }\n\n getAnswer () {\n return this._answer;\n }\n\n touchingObject (args, util) {\n return util.target.isTouchingObject(args.TOUCHINGOBJECTMENU);\n }\n\n touchingColor (args, util) {\n const color = Cast.toRgbColorList(args.COLOR);\n return util.target.isTouchingColor(color);\n }\n\n colorTouchingColor (args, util) {\n const maskColor = Cast.toRgbColorList(args.COLOR);\n const targetColor = Cast.toRgbColorList(args.COLOR2);\n return util.target.colorIsTouchingColor(targetColor, maskColor);\n }\n\n distanceTo (args, util) {\n if (util.target.isStage) return 10000;\n\n let targetX = 0;\n let targetY = 0;\n if (args.DISTANCETOMENU === '_mouse_') {\n targetX = util.ioQuery('mouse', 'getScratchX');\n targetY = util.ioQuery('mouse', 'getScratchY');\n } else {\n args.DISTANCETOMENU = Cast.toString(args.DISTANCETOMENU);\n const distTarget = this.runtime.getSpriteTargetByName(\n args.DISTANCETOMENU\n );\n if (!distTarget) return 10000;\n targetX = distTarget.x;\n targetY = distTarget.y;\n }\n\n const dx = util.target.x - targetX;\n const dy = util.target.y - targetY;\n return Math.sqrt((dx * dx) + (dy * dy));\n }\n\n setDragMode (args, util) {\n util.target.setDraggable(args.DRAG_MODE === 'draggable');\n }\n\n getTimer (args, util) {\n return util.ioQuery('clock', 'projectTimer');\n }\n\n resetTimer (args, util) {\n util.ioQuery('clock', 'resetProjectTimer');\n }\n\n getMouseX (args, util) {\n return util.ioQuery('mouse', 'getScratchX');\n }\n\n getMouseY (args, util) {\n return util.ioQuery('mouse', 'getScratchY');\n }\n\n getMouseDown (args, util) {\n return util.ioQuery('mouse', 'getIsDown');\n }\n\n current (args) {\n const menuOption = Cast.toString(args.CURRENTMENU).toLowerCase();\n const date = new Date();\n switch (menuOption) {\n case 'year': return date.getFullYear();\n case 'month': return date.getMonth() + 1; // getMonth is zero-based\n case 'date': return date.getDate();\n case 'dayofweek': return date.getDay() + 1; // getDay is zero-based, Sun=0\n case 'hour': return date.getHours();\n case 'minute': return date.getMinutes();\n case 'second': return date.getSeconds();\n }\n return 0;\n }\n\n getKeyPressed (args, util) {\n return util.ioQuery('keyboard', 'getKeyIsDown', [args.KEY_OPTION]);\n }\n\n daysSince2000 () {\n const msPerDay = 24 * 60 * 60 * 1000;\n const start = new Date(2000, 0, 1); // Months are 0-indexed.\n const today = new Date();\n const dstAdjust = today.getTimezoneOffset() - start.getTimezoneOffset();\n let mSecsSinceStart = today.valueOf() - start.valueOf();\n mSecsSinceStart += ((today.getTimezoneOffset() - dstAdjust) * 60 * 1000);\n return mSecsSinceStart / msPerDay;\n }\n\n getLoudness () {\n if (typeof this.runtime.audioEngine === 'undefined') return -1;\n if (this.runtime.currentStepTime === null) return -1;\n\n // Only measure loudness once per step\n const timeSinceLoudness = this._timer.time() - this._cachedLoudnessTimestamp;\n if (timeSinceLoudness < this.runtime.currentStepTime) {\n return this._cachedLoudness;\n }\n\n this._cachedLoudnessTimestamp = this._timer.time();\n this._cachedLoudness = this.runtime.audioEngine.getLoudness();\n return this._cachedLoudness;\n }\n\n isLoud () {\n return this.getLoudness() > 10;\n }\n\n getAttributeOf (args) {\n let attrTarget;\n\n if (args.OBJECT === '_stage_') {\n attrTarget = this.runtime.getTargetForStage();\n } else {\n args.OBJECT = Cast.toString(args.OBJECT);\n attrTarget = this.runtime.getSpriteTargetByName(args.OBJECT);\n }\n\n // attrTarget can be undefined if the target does not exist\n // (e.g. single sprite uploaded from larger project referencing\n // another sprite that wasn't uploaded)\n if (!attrTarget) return 0;\n\n // Generic attributes\n if (attrTarget.isStage) {\n switch (args.PROPERTY) {\n // Scratch 1.4 support\n case 'background #': return attrTarget.currentCostume + 1;\n\n case 'backdrop #': return attrTarget.currentCostume + 1;\n case 'backdrop name':\n return attrTarget.getCostumes()[attrTarget.currentCostume].name;\n case 'volume': return attrTarget.volume;\n }\n } else {\n switch (args.PROPERTY) {\n case 'x position': return attrTarget.x;\n case 'y position': return attrTarget.y;\n case 'direction': return attrTarget.direction;\n case 'costume #': return attrTarget.currentCostume + 1;\n case 'costume name':\n return attrTarget.getCostumes()[attrTarget.currentCostume].name;\n case 'size': return attrTarget.size;\n case 'volume': return attrTarget.volume;\n }\n }\n\n // Target variables.\n const varName = args.PROPERTY;\n const variable = attrTarget.lookupVariableByNameAndType(varName, '', true);\n if (variable) {\n return variable.value;\n }\n\n // Otherwise, 0\n return 0;\n }\n\n getUsername (args, util) {\n return util.ioQuery('userData', 'getUsername');\n }\n}\n\nmodule.exports = Scratch3SensingBlocks;\n","const MathUtil = require('../util/math-util');\nconst Cast = require('../util/cast');\nconst Clone = require('../util/clone');\n\n/**\n * Occluded boolean value to make its use more understandable.\n * @const {boolean}\n */\nconst STORE_WAITING = true;\n\nclass Scratch3SoundBlocks {\n constructor (runtime) {\n /**\n * The runtime instantiating this block package.\n * @type {Runtime}\n */\n this.runtime = runtime;\n\n this.waitingSounds = {};\n\n // Clear sound effects on green flag and stop button events.\n this.stopAllSounds = this.stopAllSounds.bind(this);\n this._stopWaitingSoundsForTarget = this._stopWaitingSoundsForTarget.bind(this);\n this._clearEffectsForAllTargets = this._clearEffectsForAllTargets.bind(this);\n if (this.runtime) {\n this.runtime.on('PROJECT_STOP_ALL', this.stopAllSounds);\n this.runtime.on('PROJECT_STOP_ALL', this._clearEffectsForAllTargets);\n this.runtime.on('STOP_FOR_TARGET', this._stopWaitingSoundsForTarget);\n this.runtime.on('PROJECT_START', this._clearEffectsForAllTargets);\n }\n\n this._onTargetCreated = this._onTargetCreated.bind(this);\n if (this.runtime) {\n runtime.on('targetWasCreated', this._onTargetCreated);\n }\n }\n\n /**\n * The key to load & store a target's sound-related state.\n * @type {string}\n */\n static get STATE_KEY () {\n return 'Scratch.sound';\n }\n\n /**\n * The default sound-related state, to be used when a target has no existing sound state.\n * @type {SoundState}\n */\n static get DEFAULT_SOUND_STATE () {\n return {\n effects: {\n pitch: 0,\n pan: 0\n }\n };\n }\n\n /**\n * The minimum and maximum MIDI note numbers, for clamping the input to play note.\n * @type {{min: number, max: number}}\n */\n static get MIDI_NOTE_RANGE () {\n return {min: 36, max: 96}; // C2 to C7\n }\n\n /**\n * The minimum and maximum beat values, for clamping the duration of play note, play drum and rest.\n * 100 beats at the default tempo of 60bpm is 100 seconds.\n * @type {{min: number, max: number}}\n */\n static get BEAT_RANGE () {\n return {min: 0, max: 100};\n }\n\n /** The minimum and maximum tempo values, in bpm.\n * @type {{min: number, max: number}}\n */\n static get TEMPO_RANGE () {\n return {min: 20, max: 500};\n }\n\n /** The minimum and maximum values for each sound effect.\n * @type {{effect:{min: number, max: number}}}\n */\n static get EFFECT_RANGE () {\n return {\n pitch: {min: -360, max: 360}, // -3 to 3 octaves\n pan: {min: -100, max: 100} // 100% left to 100% right\n };\n }\n\n /**\n * @param {Target} target - collect sound state for this target.\n * @returns {SoundState} the mutable sound state associated with that target. This will be created if necessary.\n * @private\n */\n _getSoundState (target) {\n let soundState = target.getCustomState(Scratch3SoundBlocks.STATE_KEY);\n if (!soundState) {\n soundState = Clone.simple(Scratch3SoundBlocks.DEFAULT_SOUND_STATE);\n target.setCustomState(Scratch3SoundBlocks.STATE_KEY, soundState);\n target.soundEffects = soundState.effects;\n }\n return soundState;\n }\n\n /**\n * When a Target is cloned, clone the sound state.\n * @param {Target} newTarget - the newly created target.\n * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any.\n * @listens Runtime#event:targetWasCreated\n * @private\n */\n _onTargetCreated (newTarget, sourceTarget) {\n if (sourceTarget) {\n const soundState = sourceTarget.getCustomState(Scratch3SoundBlocks.STATE_KEY);\n if (soundState && newTarget) {\n newTarget.setCustomState(Scratch3SoundBlocks.STATE_KEY, Clone.simple(soundState));\n this._syncEffectsForTarget(newTarget);\n }\n }\n }\n\n /**\n * Retrieve the block primitives implemented by this package.\n * @return {object.} Mapping of opcode to Function.\n */\n getPrimitives () {\n return {\n sound_play: this.playSound,\n sound_playuntildone: this.playSoundAndWait,\n sound_stopallsounds: this.stopAllSounds,\n sound_seteffectto: this.setEffect,\n sound_changeeffectby: this.changeEffect,\n sound_cleareffects: this.clearEffects,\n sound_sounds_menu: this.soundsMenu,\n sound_beats_menu: this.beatsMenu,\n sound_effects_menu: this.effectsMenu,\n sound_setvolumeto: this.setVolume,\n sound_changevolumeby: this.changeVolume,\n sound_volume: this.getVolume\n };\n }\n\n getMonitored () {\n return {\n sound_volume: {\n isSpriteSpecific: true,\n getId: targetId => `${targetId}_volume`\n }\n };\n }\n\n playSound (args, util) {\n // Don't return the promise, it's the only difference for AndWait\n this._playSound(args, util);\n }\n\n playSoundAndWait (args, util) {\n return this._playSound(args, util, STORE_WAITING);\n }\n\n _playSound (args, util, storeWaiting) {\n const index = this._getSoundIndex(args.SOUND_MENU, util);\n if (index >= 0) {\n const {target} = util;\n const {sprite} = target;\n const {soundId} = sprite.sounds[index];\n if (sprite.soundBank) {\n if (storeWaiting === STORE_WAITING) {\n this._addWaitingSound(target.id, soundId);\n } else {\n this._removeWaitingSound(target.id, soundId);\n }\n return sprite.soundBank.playSound(target, soundId);\n }\n }\n }\n\n _addWaitingSound (targetId, soundId) {\n if (!this.waitingSounds[targetId]) {\n this.waitingSounds[targetId] = new Set();\n }\n this.waitingSounds[targetId].add(soundId);\n }\n\n _removeWaitingSound (targetId, soundId) {\n if (!this.waitingSounds[targetId]) {\n return;\n }\n this.waitingSounds[targetId].delete(soundId);\n }\n\n _getSoundIndex (soundName, util) {\n // if the sprite has no sounds, return -1\n const len = util.target.sprite.sounds.length;\n if (len === 0) {\n return -1;\n }\n\n // look up by name first\n const index = this.getSoundIndexByName(soundName, util);\n if (index !== -1) {\n return index;\n }\n\n // then try using the sound name as a 1-indexed index\n const oneIndexedIndex = parseInt(soundName, 10);\n if (!isNaN(oneIndexedIndex)) {\n return MathUtil.wrapClamp(oneIndexedIndex - 1, 0, len - 1);\n }\n\n // could not be found as a name or converted to index, return -1\n return -1;\n }\n\n getSoundIndexByName (soundName, util) {\n const sounds = util.target.sprite.sounds;\n for (let i = 0; i < sounds.length; i++) {\n if (sounds[i].name === soundName) {\n return i;\n }\n }\n // if there is no sound by that name, return -1\n return -1;\n }\n\n stopAllSounds () {\n if (this.runtime.targets === null) return;\n const allTargets = this.runtime.targets;\n for (let i = 0; i < allTargets.length; i++) {\n this._stopAllSoundsForTarget(allTargets[i]);\n }\n }\n\n _stopAllSoundsForTarget (target) {\n if (target.sprite.soundBank) {\n target.sprite.soundBank.stopAllSounds(target);\n if (this.waitingSounds[target.id]) {\n this.waitingSounds[target.id].clear();\n }\n }\n }\n\n _stopWaitingSoundsForTarget (target) {\n if (target.sprite.soundBank) {\n if (this.waitingSounds[target.id]) {\n for (const soundId of this.waitingSounds[target.id].values()) {\n target.sprite.soundBank.stop(target, soundId);\n }\n this.waitingSounds[target.id].clear();\n }\n }\n }\n\n setEffect (args, util) {\n return this._updateEffect(args, util, false);\n }\n\n changeEffect (args, util) {\n return this._updateEffect(args, util, true);\n }\n\n _updateEffect (args, util, change) {\n const effect = Cast.toString(args.EFFECT).toLowerCase();\n const value = Cast.toNumber(args.VALUE);\n\n const soundState = this._getSoundState(util.target);\n if (!Object.prototype.hasOwnProperty.call(soundState.effects, effect)) return;\n\n if (change) {\n soundState.effects[effect] += value;\n } else {\n soundState.effects[effect] = value;\n }\n\n const {min, max} = Scratch3SoundBlocks.EFFECT_RANGE[effect];\n soundState.effects[effect] = MathUtil.clamp(soundState.effects[effect], min, max);\n\n this._syncEffectsForTarget(util.target);\n // Yield until the next tick.\n return Promise.resolve();\n }\n\n _syncEffectsForTarget (target) {\n if (!target || !target.sprite.soundBank) return;\n target.soundEffects = this._getSoundState(target).effects;\n\n target.sprite.soundBank.setEffects(target);\n }\n\n clearEffects (args, util) {\n this._clearEffectsForTarget(util.target);\n }\n\n _clearEffectsForTarget (target) {\n const soundState = this._getSoundState(target);\n for (const effect in soundState.effects) {\n if (!Object.prototype.hasOwnProperty.call(soundState.effects, effect)) continue;\n soundState.effects[effect] = 0;\n }\n this._syncEffectsForTarget(target);\n }\n\n _clearEffectsForAllTargets () {\n if (this.runtime.targets === null) return;\n const allTargets = this.runtime.targets;\n for (let i = 0; i < allTargets.length; i++) {\n this._clearEffectsForTarget(allTargets[i]);\n }\n }\n\n setVolume (args, util) {\n const volume = Cast.toNumber(args.VOLUME);\n return this._updateVolume(volume, util);\n }\n\n changeVolume (args, util) {\n const volume = Cast.toNumber(args.VOLUME) + util.target.volume;\n return this._updateVolume(volume, util);\n }\n\n _updateVolume (volume, util) {\n volume = MathUtil.clamp(volume, 0, 100);\n util.target.volume = volume;\n this._syncEffectsForTarget(util.target);\n\n // Yield until the next tick.\n return Promise.resolve();\n }\n\n getVolume (args, util) {\n return util.target.volume;\n }\n\n soundsMenu (args) {\n return args.SOUND_MENU;\n }\n\n beatsMenu (args) {\n return args.BEATS;\n }\n\n effectsMenu (args) {\n return args.EFFECT;\n }\n}\n\nmodule.exports = Scratch3SoundBlocks;\n","const SharedDispatch = require('./shared-dispatch');\n\nconst log = require('../util/log');\n\n/**\n * This class serves as the central broker for message dispatch. It expects to operate on the main thread / Window and\n * it must be informed of any Worker threads which will participate in the messaging system. From any context in the\n * messaging system, the dispatcher's \"call\" method can call any method on any \"service\" provided in any participating\n * context. The dispatch system will forward function arguments and return values across worker boundaries as needed.\n * @see {WorkerDispatch}\n */\nclass CentralDispatch extends SharedDispatch {\n constructor () {\n super();\n\n /**\n * Map of channel name to worker or local service provider.\n * If the entry is a Worker, the service is provided by an object on that worker.\n * Otherwise, the service is provided locally and methods on the service will be called directly.\n * @see {setService}\n * @type {object.}\n */\n this.services = {};\n\n /**\n * The constructor we will use to recognize workers.\n * @type {Function}\n */\n this.workerClass = (typeof Worker === 'undefined' ? null : Worker);\n\n /**\n * List of workers attached to this dispatcher.\n * @type {Array}\n */\n this.workers = [];\n }\n\n /**\n * Synchronously call a particular method on a particular service provided locally.\n * Calling this function on a remote service will fail.\n * @param {string} service - the name of the service.\n * @param {string} method - the name of the method.\n * @param {*} [args] - the arguments to be copied to the method, if any.\n * @returns {*} - the return value of the service method.\n */\n callSync (service, method, ...args) {\n const {provider, isRemote} = this._getServiceProvider(service);\n if (provider) {\n if (isRemote) {\n throw new Error(`Cannot use 'callSync' on remote provider for service ${service}.`);\n }\n\n // TODO: verify correct `this` after switching from apply to spread\n // eslint-disable-next-line prefer-spread\n return provider[method].apply(provider, args);\n }\n throw new Error(`Provider not found for service: ${service}`);\n }\n\n /**\n * Synchronously set a local object as the global provider of the specified service.\n * WARNING: Any method on the provider can be called from any worker within the dispatch system.\n * @param {string} service - a globally unique string identifying this service. Examples: 'vm', 'gui', 'extension9'.\n * @param {object} provider - a local object which provides this service.\n */\n setServiceSync (service, provider) {\n if (Object.prototype.hasOwnProperty.call(this.services, service)) {\n log.warn(`Central dispatch replacing existing service provider for ${service}`);\n }\n this.services[service] = provider;\n }\n\n /**\n * Set a local object as the global provider of the specified service.\n * WARNING: Any method on the provider can be called from any worker within the dispatch system.\n * @param {string} service - a globally unique string identifying this service. Examples: 'vm', 'gui', 'extension9'.\n * @param {object} provider - a local object which provides this service.\n * @returns {Promise} - a promise which will resolve once the service is registered.\n */\n setService (service, provider) {\n /** Return a promise for consistency with {@link WorkerDispatch#setService} */\n try {\n this.setServiceSync(service, provider);\n return Promise.resolve();\n } catch (e) {\n return Promise.reject(e);\n }\n }\n\n /**\n * Add a worker to the message dispatch system. The worker must implement a compatible message dispatch framework.\n * The dispatcher will immediately attempt to \"handshake\" with the worker.\n * @param {Worker} worker - the worker to add into the dispatch system.\n */\n addWorker (worker) {\n if (this.workers.indexOf(worker) === -1) {\n this.workers.push(worker);\n worker.onmessage = this._onMessage.bind(this, worker);\n this._remoteCall(worker, 'dispatch', 'handshake').catch(e => {\n log.error(`Could not handshake with worker: ${JSON.stringify(e)}`);\n });\n } else {\n log.warn('Central dispatch ignoring attempt to add duplicate worker');\n }\n }\n\n /**\n * Fetch the service provider object for a particular service name.\n * @override\n * @param {string} service - the name of the service to look up\n * @returns {{provider:(object|Worker), isRemote:boolean}} - the means to contact the service, if found\n * @protected\n */\n _getServiceProvider (service) {\n const provider = this.services[service];\n return provider && {\n provider,\n isRemote: Boolean(this.workerClass && provider instanceof this.workerClass)\n };\n }\n\n /**\n * Handle a call message sent to the dispatch service itself\n * @override\n * @param {Worker} worker - the worker which sent the message.\n * @param {DispatchCallMessage} message - the message to be handled.\n * @returns {Promise|undefined} - a promise for the results of this operation, if appropriate\n * @protected\n */\n _onDispatchMessage (worker, message) {\n let promise;\n switch (message.method) {\n case 'setService':\n promise = this.setService(message.args[0], worker);\n break;\n default:\n log.error(`Central dispatch received message for unknown method: ${message.method}`);\n }\n return promise;\n }\n}\n\nmodule.exports = new CentralDispatch();\n","const log = require('../util/log');\n\n/**\n * @typedef {object} DispatchCallMessage - a message to the dispatch system representing a service method call\n * @property {*} responseId - send a response message with this response ID. See {@link DispatchResponseMessage}\n * @property {string} service - the name of the service to be called\n * @property {string} method - the name of the method to be called\n * @property {Array|undefined} args - the arguments to be passed to the method\n */\n\n/**\n * @typedef {object} DispatchResponseMessage - a message to the dispatch system representing the results of a call\n * @property {*} responseId - a copy of the response ID from the call which generated this response\n * @property {*|undefined} error - if this is truthy, then it contains results from a failed call (such as an exception)\n * @property {*|undefined} result - if error is not truthy, then this contains the return value of the call (if any)\n */\n\n/**\n * @typedef {DispatchCallMessage|DispatchResponseMessage} DispatchMessage\n * Any message to the dispatch system.\n */\n\n/**\n * The SharedDispatch class is responsible for dispatch features shared by\n * {@link CentralDispatch} and {@link WorkerDispatch}.\n */\nclass SharedDispatch {\n constructor () {\n /**\n * List of callback registrations for promises waiting for a response from a call to a service on another\n * worker. A callback registration is an array of [resolve,reject] Promise functions.\n * Calls to local services don't enter this list.\n * @type {Array.}\n */\n this.callbacks = [];\n\n /**\n * The next response ID to be used.\n * @type {int}\n */\n this.nextResponseId = 0;\n }\n\n /**\n * Call a particular method on a particular service, regardless of whether that service is provided locally or on\n * a worker. If the service is provided by a worker, the `args` will be copied using the Structured Clone\n * algorithm, except for any items which are also in the `transfer` list. Ownership of those items will be\n * transferred to the worker, and they should not be used after this call.\n * @example\n * dispatcher.call('vm', 'setData', 'cat', 42);\n * // this finds the worker for the 'vm' service, then on that worker calls:\n * vm.setData('cat', 42);\n * @param {string} service - the name of the service.\n * @param {string} method - the name of the method.\n * @param {*} [args] - the arguments to be copied to the method, if any.\n * @returns {Promise} - a promise for the return value of the service method.\n */\n call (service, method, ...args) {\n return this.transferCall(service, method, null, ...args);\n }\n\n /**\n * Call a particular method on a particular service, regardless of whether that service is provided locally or on\n * a worker. If the service is provided by a worker, the `args` will be copied using the Structured Clone\n * algorithm, except for any items which are also in the `transfer` list. Ownership of those items will be\n * transferred to the worker, and they should not be used after this call.\n * @example\n * dispatcher.transferCall('vm', 'setData', [myArrayBuffer], 'cat', myArrayBuffer);\n * // this finds the worker for the 'vm' service, transfers `myArrayBuffer` to it, then on that worker calls:\n * vm.setData('cat', myArrayBuffer);\n * @param {string} service - the name of the service.\n * @param {string} method - the name of the method.\n * @param {Array} [transfer] - objects to be transferred instead of copied. Must be present in `args` to be useful.\n * @param {*} [args] - the arguments to be copied to the method, if any.\n * @returns {Promise} - a promise for the return value of the service method.\n */\n transferCall (service, method, transfer, ...args) {\n try {\n const {provider, isRemote} = this._getServiceProvider(service);\n if (provider) {\n if (isRemote) {\n return this._remoteTransferCall(provider, service, method, transfer, ...args);\n }\n\n // TODO: verify correct `this` after switching from apply to spread\n // eslint-disable-next-line prefer-spread\n const result = provider[method].apply(provider, args);\n return Promise.resolve(result);\n }\n return Promise.reject(new Error(`Service not found: ${service}`));\n } catch (e) {\n return Promise.reject(e);\n }\n }\n\n /**\n * Check if a particular service lives on another worker.\n * @param {string} service - the service to check.\n * @returns {boolean} - true if the service is remote (calls must cross a Worker boundary), false otherwise.\n * @private\n */\n _isRemoteService (service) {\n return this._getServiceProvider(service).isRemote;\n }\n\n /**\n * Like {@link call}, but force the call to be posted through a particular communication channel.\n * @param {object} provider - send the call through this object's `postMessage` function.\n * @param {string} service - the name of the service.\n * @param {string} method - the name of the method.\n * @param {*} [args] - the arguments to be copied to the method, if any.\n * @returns {Promise} - a promise for the return value of the service method.\n */\n _remoteCall (provider, service, method, ...args) {\n return this._remoteTransferCall(provider, service, method, null, ...args);\n }\n\n /**\n * Like {@link transferCall}, but force the call to be posted through a particular communication channel.\n * @param {object} provider - send the call through this object's `postMessage` function.\n * @param {string} service - the name of the service.\n * @param {string} method - the name of the method.\n * @param {Array} [transfer] - objects to be transferred instead of copied. Must be present in `args` to be useful.\n * @param {*} [args] - the arguments to be copied to the method, if any.\n * @returns {Promise} - a promise for the return value of the service method.\n */\n _remoteTransferCall (provider, service, method, transfer, ...args) {\n return new Promise((resolve, reject) => {\n const responseId = this._storeCallbacks(resolve, reject);\n\n /** @TODO: remove this hack! this is just here so we don't try to send `util` to a worker */\n if ((args.length > 0) && (typeof args[args.length - 1].yield === 'function')) {\n args.pop();\n }\n\n if (transfer) {\n provider.postMessage({service, method, responseId, args}, transfer);\n } else {\n provider.postMessage({service, method, responseId, args});\n }\n });\n }\n\n /**\n * Store callback functions pending a response message.\n * @param {Function} resolve - function to call if the service method returns.\n * @param {Function} reject - function to call if the service method throws.\n * @returns {*} - a unique response ID for this set of callbacks. See {@link _deliverResponse}.\n * @protected\n */\n _storeCallbacks (resolve, reject) {\n const responseId = this.nextResponseId++;\n this.callbacks[responseId] = [resolve, reject];\n return responseId;\n }\n\n /**\n * Deliver call response from a worker. This should only be called as the result of a message from a worker.\n * @param {int} responseId - the response ID of the callback set to call.\n * @param {DispatchResponseMessage} message - the message containing the response value(s).\n * @protected\n */\n _deliverResponse (responseId, message) {\n try {\n const [resolve, reject] = this.callbacks[responseId];\n delete this.callbacks[responseId];\n if (message.error) {\n reject(message.error);\n } else {\n resolve(message.result);\n }\n } catch (e) {\n log.error(`Dispatch callback failed: ${JSON.stringify(e)}`);\n }\n }\n\n /**\n * Handle a message event received from a connected worker.\n * @param {Worker} worker - the worker which sent the message, or the global object if running in a worker.\n * @param {MessageEvent} event - the message event to be handled.\n * @protected\n */\n _onMessage (worker, event) {\n /** @type {DispatchMessage} */\n const message = event.data;\n message.args = message.args || [];\n let promise;\n if (message.service) {\n if (message.service === 'dispatch') {\n promise = this._onDispatchMessage(worker, message);\n } else {\n promise = this.call(message.service, message.method, ...message.args);\n }\n } else if (typeof message.responseId === 'undefined') {\n log.error(`Dispatch caught malformed message from a worker: ${JSON.stringify(event)}`);\n } else {\n this._deliverResponse(message.responseId, message);\n }\n if (promise) {\n if (typeof message.responseId === 'undefined') {\n log.error(`Dispatch message missing required response ID: ${JSON.stringify(event)}`);\n } else {\n promise.then(\n result => worker.postMessage({responseId: message.responseId, result}),\n error => worker.postMessage({responseId: message.responseId, error})\n );\n }\n }\n }\n\n /**\n * Fetch the service provider object for a particular service name.\n * @abstract\n * @param {string} service - the name of the service to look up\n * @returns {{provider:(object|Worker), isRemote:boolean}} - the means to contact the service, if found\n * @protected\n */\n _getServiceProvider (service) {\n throw new Error(`Could not get provider for ${service}: _getServiceProvider not implemented`);\n }\n\n /**\n * Handle a call message sent to the dispatch service itself\n * @abstract\n * @param {Worker} worker - the worker which sent the message.\n * @param {DispatchCallMessage} message - the message to be handled.\n * @returns {Promise|undefined} - a promise for the results of this operation, if appropriate\n * @private\n */\n _onDispatchMessage (worker, message) {\n throw new Error(`Unimplemented dispatch message handler cannot handle ${message.method} method`);\n }\n}\n\nmodule.exports = SharedDispatch;\n","const mutationAdapter = require('./mutation-adapter');\nconst html = require('htmlparser2');\nconst uid = require('../util/uid');\n\n/**\n * Convert and an individual block DOM to the representation tree.\n * Based on Blockly's `domToBlockHeadless_`.\n * @param {Element} blockDOM DOM tree for an individual block.\n * @param {object} blocks Collection of blocks to add to.\n * @param {boolean} isTopBlock Whether blocks at this level are \"top blocks.\"\n * @param {?string} parent Parent block ID.\n * @return {undefined}\n */\nconst domToBlock = function (blockDOM, blocks, isTopBlock, parent) {\n if (!blockDOM.attribs.id) {\n blockDOM.attribs.id = uid();\n }\n\n // Block skeleton.\n const block = {\n id: blockDOM.attribs.id, // Block ID\n opcode: blockDOM.attribs.type, // For execution, \"event_whengreenflag\".\n inputs: {}, // Inputs to this block and the blocks they point to.\n fields: {}, // Fields on this block and their values.\n next: null, // Next block in the stack, if one exists.\n topLevel: isTopBlock, // If this block starts a stack.\n parent: parent, // Parent block ID, if available.\n shadow: blockDOM.name === 'shadow', // If this represents a shadow/slot.\n x: blockDOM.attribs.x, // X position of script, if top-level.\n y: blockDOM.attribs.y // Y position of script, if top-level.\n };\n\n // Add the block to the representation tree.\n blocks[block.id] = block;\n\n // Process XML children and find enclosed blocks, fields, etc.\n for (let i = 0; i < blockDOM.children.length; i++) {\n const xmlChild = blockDOM.children[i];\n // Enclosed blocks and shadows\n let childBlockNode = null;\n let childShadowNode = null;\n for (let j = 0; j < xmlChild.children.length; j++) {\n const grandChildNode = xmlChild.children[j];\n if (!grandChildNode.name) {\n // Non-XML tag node.\n continue;\n }\n const grandChildNodeName = grandChildNode.name.toLowerCase();\n if (grandChildNodeName === 'block') {\n childBlockNode = grandChildNode;\n } else if (grandChildNodeName === 'shadow') {\n childShadowNode = grandChildNode;\n }\n }\n\n // Use shadow block only if there's no real block node.\n if (!childBlockNode && childShadowNode) {\n childBlockNode = childShadowNode;\n }\n\n // Not all Blockly-type blocks are handled here,\n // as we won't be using all of them for Scratch.\n switch (xmlChild.name.toLowerCase()) {\n case 'field':\n {\n // Add the field to this block.\n const fieldName = xmlChild.attribs.name;\n // Add id in case it is a variable field\n const fieldId = xmlChild.attribs.id;\n let fieldData = '';\n if (xmlChild.children.length > 0 && xmlChild.children[0].data) {\n fieldData = xmlChild.children[0].data;\n } else {\n // If the child of the field with a data property\n // doesn't exist, set the data to an empty string.\n fieldData = '';\n }\n block.fields[fieldName] = {\n name: fieldName,\n id: fieldId,\n value: fieldData\n };\n const fieldVarType = xmlChild.attribs.variabletype;\n if (typeof fieldVarType === 'string') {\n block.fields[fieldName].variableType = fieldVarType;\n }\n break;\n }\n case 'comment':\n {\n block.comment = xmlChild.attribs.id;\n break;\n }\n case 'value':\n case 'statement':\n {\n // Recursively generate block structure for input block.\n domToBlock(childBlockNode, blocks, false, block.id);\n if (childShadowNode && childBlockNode !== childShadowNode) {\n // Also generate the shadow block.\n domToBlock(childShadowNode, blocks, false, block.id);\n }\n // Link this block's input to the child block.\n const inputName = xmlChild.attribs.name;\n block.inputs[inputName] = {\n name: inputName,\n block: childBlockNode.attribs.id,\n shadow: childShadowNode ? childShadowNode.attribs.id : null\n };\n break;\n }\n case 'next':\n {\n if (!childBlockNode || !childBlockNode.attribs) {\n // Invalid child block.\n continue;\n }\n // Recursively generate block structure for next block.\n domToBlock(childBlockNode, blocks, false, block.id);\n // Link next block to this block.\n block.next = childBlockNode.attribs.id;\n break;\n }\n case 'mutation':\n {\n block.mutation = mutationAdapter(xmlChild);\n break;\n }\n }\n }\n};\n\n/**\n * Convert outer blocks DOM from a Blockly CREATE event\n * to a usable form for the Scratch runtime.\n * This structure is based on Blockly xml.js:`domToWorkspace` and `domToBlock`.\n * @param {Element} blocksDOM DOM tree for this event.\n * @return {Array.} Usable list of blocks from this CREATE event.\n */\nconst domToBlocks = function (blocksDOM) {\n // At this level, there could be multiple blocks adjacent in the DOM tree.\n const blocks = {};\n for (let i = 0; i < blocksDOM.length; i++) {\n const block = blocksDOM[i];\n if (!block.name || !block.attribs) {\n continue;\n }\n const tagName = block.name.toLowerCase();\n if (tagName === 'block' || tagName === 'shadow') {\n domToBlock(block, blocks, true, null);\n }\n }\n // Flatten blocks object into a list.\n const blocksList = [];\n for (const b in blocks) {\n if (!Object.prototype.hasOwnProperty.call(blocks, b)) continue;\n blocksList.push(blocks[b]);\n }\n return blocksList;\n};\n\n/**\n * Adapter between block creation events and block representation which can be\n * used by the Scratch runtime.\n * @param {object} e `Blockly.events.create` or `Blockly.events.endDrag`\n * @return {Array.} List of blocks from this CREATE event.\n */\nconst adapter = function (e) {\n // Validate input\n if (typeof e !== 'object') return;\n if (typeof e.xml !== 'object') return;\n\n return domToBlocks(html.parseDOM(e.xml.outerHTML, {decodeEntities: true}));\n};\n\nmodule.exports = adapter;\n","const Thread = require('./thread');\nconst Timer = require('../util/timer');\n\n/**\n * @fileoverview\n * Interface provided to block primitive functions for interacting with the\n * runtime, thread, target, and convenient methods.\n */\n\nclass BlockUtility {\n constructor (sequencer = null, thread = null) {\n /**\n * A sequencer block primitives use to branch or start procedures with\n * @type {?Sequencer}\n */\n this.sequencer = sequencer;\n\n /**\n * The block primitives thread with the block's target, stackFrame and\n * modifiable status.\n * @type {?Thread}\n */\n this.thread = thread;\n\n this._nowObj = {\n now: () => this.sequencer.runtime.currentMSecs\n };\n }\n\n /**\n * The target the primitive is working on.\n * @type {Target}\n */\n get target () {\n return this.thread.target;\n }\n\n /**\n * The runtime the block primitive is running in.\n * @type {Runtime}\n */\n get runtime () {\n return this.sequencer.runtime;\n }\n\n /**\n * Use the runtime's currentMSecs value as a timestamp value for now\n * This is useful in some cases where we need compatibility with Scratch 2\n * @type {function}\n */\n get nowObj () {\n if (this.runtime) {\n return this._nowObj;\n }\n return null;\n }\n\n /**\n * The stack frame used by loop and other blocks to track internal state.\n * @type {object}\n */\n get stackFrame () {\n const frame = this.thread.peekStackFrame();\n if (frame.executionContext === null) {\n frame.executionContext = {};\n }\n return frame.executionContext;\n }\n\n /**\n * Check the stack timer and return a boolean based on whether it has finished or not.\n * @return {boolean} - true if the stack timer has finished.\n */\n stackTimerFinished () {\n const timeElapsed = this.stackFrame.timer.timeElapsed();\n if (timeElapsed < this.stackFrame.duration) {\n return false;\n }\n return true;\n }\n\n /**\n * Check if the stack timer needs initialization.\n * @return {boolean} - true if the stack timer needs to be initialized.\n */\n stackTimerNeedsInit () {\n return !this.stackFrame.timer;\n }\n\n /**\n * Create and start a stack timer\n * @param {number} duration - a duration in milliseconds to set the timer for.\n */\n startStackTimer (duration) {\n if (this.nowObj) {\n this.stackFrame.timer = new Timer(this.nowObj);\n } else {\n this.stackFrame.timer = new Timer();\n }\n this.stackFrame.timer.start();\n this.stackFrame.duration = duration;\n }\n\n /**\n * Set the thread to yield.\n */\n yield () {\n this.thread.status = Thread.STATUS_YIELD;\n }\n\n /**\n * Set the thread to yield until the next tick of the runtime.\n */\n yieldTick () {\n this.thread.status = Thread.STATUS_YIELD_TICK;\n }\n\n /**\n * Start a branch in the current block.\n * @param {number} branchNum Which branch to step to (i.e., 1, 2).\n * @param {boolean} isLoop Whether this block is a loop.\n */\n startBranch (branchNum, isLoop) {\n this.sequencer.stepToBranch(this.thread, branchNum, isLoop);\n }\n\n /**\n * Stop all threads.\n */\n stopAll () {\n this.sequencer.runtime.stopAll();\n }\n\n /**\n * Stop threads other on this target other than the thread holding the\n * executed block.\n */\n stopOtherTargetThreads () {\n this.sequencer.runtime.stopForTarget(this.thread.target, this.thread);\n }\n\n /**\n * Stop this thread.\n */\n stopThisScript () {\n this.thread.stopThisScript();\n }\n\n /**\n * Start a specified procedure on this thread.\n * @param {string} procedureCode Procedure code for procedure to start.\n */\n startProcedure (procedureCode) {\n this.sequencer.stepToProcedure(this.thread, procedureCode);\n }\n\n /**\n * Get names and ids of parameters for the given procedure.\n * @param {string} procedureCode Procedure code for procedure to query.\n * @return {Array.} List of param names for a procedure.\n */\n getProcedureParamNamesAndIds (procedureCode) {\n return this.thread.target.blocks.getProcedureParamNamesAndIds(procedureCode);\n }\n\n /**\n * Get names, ids, and defaults of parameters for the given procedure.\n * @param {string} procedureCode Procedure code for procedure to query.\n * @return {Array.} List of param names for a procedure.\n */\n getProcedureParamNamesIdsAndDefaults (procedureCode) {\n return this.thread.target.blocks.getProcedureParamNamesIdsAndDefaults(procedureCode);\n }\n\n /**\n * Initialize procedure parameters in the thread before pushing parameters.\n */\n initParams () {\n this.thread.initParams();\n }\n\n /**\n * Store a procedure parameter value by its name.\n * @param {string} paramName The procedure's parameter name.\n * @param {*} paramValue The procedure's parameter value.\n */\n pushParam (paramName, paramValue) {\n this.thread.pushParam(paramName, paramValue);\n }\n\n /**\n * Retrieve the stored parameter value for a given parameter name.\n * @param {string} paramName The procedure's parameter name.\n * @return {*} The parameter's current stored value.\n */\n getParam (paramName) {\n return this.thread.getParam(paramName);\n }\n\n /**\n * Start all relevant hats.\n * @param {!string} requestedHat Opcode of hats to start.\n * @param {object=} optMatchFields Optionally, fields to match on the hat.\n * @param {Target=} optTarget Optionally, a target to restrict to.\n * @return {Array.} List of threads started by this function.\n */\n startHats (requestedHat, optMatchFields, optTarget) {\n // Store thread and sequencer to ensure we can return to the calling block's context.\n // startHats may execute further blocks and dirty the BlockUtility's execution context\n // and confuse the calling block when we return to it.\n const callerThread = this.thread;\n const callerSequencer = this.sequencer;\n const result = this.sequencer.runtime.startHats(requestedHat, optMatchFields, optTarget);\n\n // Restore thread and sequencer to prior values before we return to the calling block.\n this.thread = callerThread;\n this.sequencer = callerSequencer;\n\n return result;\n }\n\n /**\n * Query a named IO device.\n * @param {string} device The name of like the device, like keyboard.\n * @param {string} func The name of the device's function to query.\n * @param {Array.<*>} args Arguments to pass to the device's function.\n * @return {*} The expected output for the device's function.\n */\n ioQuery (device, func, args) {\n // Find the I/O device and execute the query/function call.\n if (\n this.sequencer.runtime.ioDevices[device] &&\n this.sequencer.runtime.ioDevices[device][func]) {\n const devObject = this.sequencer.runtime.ioDevices[device];\n // TODO: verify correct `this` after switching from apply to spread\n // eslint-disable-next-line prefer-spread\n return devObject[func].apply(devObject, args);\n }\n }\n}\n\nmodule.exports = BlockUtility;\n","/**\n * @fileoverview\n * Access point for private method shared between blocks.js and execute.js for\n * caching execute information.\n */\n\n/**\n * A private method shared with execute to build an object containing the block\n * information execute needs and that is reset when other cached Blocks info is\n * reset.\n * @param {Blocks} blocks Blocks containing the expected blockId\n * @param {string} blockId blockId for the desired execute cache\n */\nexports.getCached = function () {\n throw new Error('blocks.js has not initialized BlocksExecuteCache');\n};\n\n// Call after the default throwing getCached is assigned for Blocks to replace.\nrequire('./blocks');\n","/**\n * @fileoverview\n * The BlocksRuntimeCache caches data about the top block of scripts so that\n * Runtime can iterate a targeted opcode and iterate the returned set faster.\n * Many top blocks need to match fields as well as opcode, since that matching\n * compares strings in uppercase we can go ahead and uppercase the cached value\n * so we don't need to in the future.\n */\n\n/**\n * A set of cached data about the top block of a script.\n * @param {Blocks} container - Container holding the block and related data\n * @param {string} blockId - Id for whose block data is cached in this instance\n */\nclass RuntimeScriptCache {\n constructor (container, blockId) {\n /**\n * Container with block data for blockId.\n * @type {Blocks}\n */\n this.container = container;\n\n /**\n * ID for block this instance caches.\n * @type {string}\n */\n this.blockId = blockId;\n\n const block = container.getBlock(blockId);\n const fields = container.getFields(block);\n\n /**\n * Formatted fields or fields of input blocks ready for comparison in\n * runtime.\n *\n * This is a clone of parts of the targeted blocks. Changes to these\n * clones are limited to copies under RuntimeScriptCache and will not\n * appear in the original blocks in their container. This copy is\n * modified changing the case of strings to uppercase. These uppercase\n * values will be compared later by the VM.\n * @type {object}\n */\n this.fieldsOfInputs = Object.assign({}, fields);\n if (Object.keys(fields).length === 0) {\n const inputs = container.getInputs(block);\n for (const input in inputs) {\n if (!Object.prototype.hasOwnProperty.call(inputs, input)) continue;\n const id = inputs[input].block;\n const inputBlock = container.getBlock(id);\n const inputFields = container.getFields(inputBlock);\n Object.assign(this.fieldsOfInputs, inputFields);\n }\n }\n for (const key in this.fieldsOfInputs) {\n const field = this.fieldsOfInputs[key] = Object.assign({}, this.fieldsOfInputs[key]);\n if (field.value.toUpperCase) {\n field.value = field.value.toUpperCase();\n }\n }\n }\n}\n\n/**\n * Get an array of scripts from a block container prefiltered to match opcode.\n * @param {Blocks} container - Container of blocks\n * @param {string} opcode - Opcode to filter top blocks by\n */\nexports.getScripts = function () {\n throw new Error('blocks.js has not initialized BlocksRuntimeCache');\n};\n\n/**\n * Exposed RuntimeScriptCache class used by integration in blocks.js.\n * @private\n */\nexports._RuntimeScriptCache = RuntimeScriptCache;\n\nrequire('./blocks');\n","const adapter = require('./adapter');\nconst mutationAdapter = require('./mutation-adapter');\nconst xmlEscape = require('../util/xml-escape');\nconst MonitorRecord = require('./monitor-record');\nconst Clone = require('../util/clone');\nconst {Map} = require('immutable');\nconst BlocksExecuteCache = require('./blocks-execute-cache');\nconst BlocksRuntimeCache = require('./blocks-runtime-cache');\nconst log = require('../util/log');\nconst Variable = require('./variable');\nconst getMonitorIdForBlockWithArgs = require('../util/get-monitor-id');\n\n/**\n * @fileoverview\n * Store and mutate the VM block representation,\n * and handle updates from Scratch Blocks events.\n */\n\n/**\n * Create a block container.\n * @param {Runtime} runtime The runtime this block container operates within\n * @param {boolean} optNoGlow Optional flag to indicate that blocks in this container\n * should not request glows. This does not affect glows when clicking on a block to execute it.\n */\nclass Blocks {\n constructor (runtime, optNoGlow) {\n this.runtime = runtime;\n\n /**\n * All blocks in the workspace.\n * Keys are block IDs, values are metadata about the block.\n * @type {Object.}\n */\n this._blocks = {};\n\n /**\n * All top-level scripts in the workspace.\n * A list of block IDs that represent scripts (i.e., first block in script).\n * @type {Array.}\n */\n this._scripts = [];\n\n /**\n * Runtime Cache\n * @type {{inputs: {}, procedureParamNames: {}, procedureDefinitions: {}}}\n * @private\n */\n Object.defineProperty(this, '_cache', {writable: true, enumerable: false});\n this._cache = {\n /**\n * Cache block inputs by block id\n * @type {object.>}\n */\n inputs: {},\n /**\n * Cache procedure Param Names by block id\n * @type {object.>}\n */\n procedureParamNames: {},\n /**\n * Cache procedure definitions by block id\n * @type {object.}\n */\n procedureDefinitions: {},\n\n /**\n * A cache for execute to use and store on. Only available to\n * execute.\n * @type {object.}\n */\n _executeCached: {},\n\n /**\n * A cache of block IDs and targets to start threads on as they are\n * actively monitored.\n * @type {Array<{blockId: string, target: Target}>}\n */\n _monitored: null,\n\n /**\n * A cache of hat opcodes to collection of theads to execute.\n * @type {object.}\n */\n scripts: {}\n };\n\n /**\n * Flag which indicates that blocks in this container should not glow.\n * Blocks will still glow when clicked on, but this flag is used to control\n * whether the blocks in this container can request a glow as part of\n * a running stack. E.g. the flyout block container and the monitor block container\n * should not be able to request a glow, but blocks containers belonging to\n * sprites should.\n * @type {boolean}\n */\n this.forceNoGlow = optNoGlow || false;\n }\n\n /**\n * Blockly inputs that represent statements/branch.\n * are prefixed with this string.\n * @const{string}\n */\n static get BRANCH_INPUT_PREFIX () {\n return 'SUBSTACK';\n }\n\n /**\n * Provide an object with metadata for the requested block ID.\n * @param {!string} blockId ID of block we have stored.\n * @return {?object} Metadata about the block, if it exists.\n */\n getBlock (blockId) {\n return this._blocks[blockId];\n }\n\n /**\n * Get all known top-level blocks that start scripts.\n * @return {Array.} List of block IDs.\n */\n getScripts () {\n return this._scripts;\n }\n\n /**\n * Get the next block for a particular block\n * @param {?string} id ID of block to get the next block for\n * @return {?string} ID of next block in the sequence\n */\n getNextBlock (id) {\n const block = this._blocks[id];\n return (typeof block === 'undefined') ? null : block.next;\n }\n\n /**\n * Get the branch for a particular C-shaped block.\n * @param {?string} id ID for block to get the branch for.\n * @param {?number} branchNum Which branch to select (e.g. for if-else).\n * @return {?string} ID of block in the branch.\n */\n getBranch (id, branchNum) {\n const block = this._blocks[id];\n if (typeof block === 'undefined') return null;\n if (!branchNum) branchNum = 1;\n\n let inputName = Blocks.BRANCH_INPUT_PREFIX;\n if (branchNum > 1) {\n inputName += branchNum;\n }\n\n // Empty C-block?\n const input = block.inputs[inputName];\n return (typeof input === 'undefined') ? null : input.block;\n }\n\n /**\n * Get the opcode for a particular block\n * @param {?object} block The block to query\n * @return {?string} the opcode corresponding to that block\n */\n getOpcode (block) {\n return (typeof block === 'undefined') ? null : block.opcode;\n }\n\n /**\n * Get all fields and their values for a block.\n * @param {?object} block The block to query.\n * @return {?object} All fields and their values.\n */\n getFields (block) {\n return (typeof block === 'undefined') ? null : block.fields;\n }\n\n /**\n * Get all non-branch inputs for a block.\n * @param {?object} block the block to query.\n * @return {?Array.} All non-branch inputs and their associated blocks.\n */\n getInputs (block) {\n if (typeof block === 'undefined') return null;\n let inputs = this._cache.inputs[block.id];\n if (typeof inputs !== 'undefined') {\n return inputs;\n }\n\n inputs = {};\n for (const input in block.inputs) {\n // Ignore blocks prefixed with branch prefix.\n if (input.substring(0, Blocks.BRANCH_INPUT_PREFIX.length) !==\n Blocks.BRANCH_INPUT_PREFIX) {\n inputs[input] = block.inputs[input];\n }\n }\n\n this._cache.inputs[block.id] = inputs;\n return inputs;\n }\n\n /**\n * Get mutation data for a block.\n * @param {?object} block The block to query.\n * @return {?object} Mutation for the block.\n */\n getMutation (block) {\n return (typeof block === 'undefined') ? null : block.mutation;\n }\n\n /**\n * Get the top-level script for a given block.\n * @param {?string} id ID of block to query.\n * @return {?string} ID of top-level script block.\n */\n getTopLevelScript (id) {\n let block = this._blocks[id];\n if (typeof block === 'undefined') return null;\n while (block.parent !== null) {\n block = this._blocks[block.parent];\n }\n return block.id;\n }\n\n /**\n * Get the procedure definition for a given name.\n * @param {?string} name Name of procedure to query.\n * @return {?string} ID of procedure definition.\n */\n getProcedureDefinition (name) {\n const blockID = this._cache.procedureDefinitions[name];\n if (typeof blockID !== 'undefined') {\n return blockID;\n }\n\n for (const id in this._blocks) {\n if (!Object.prototype.hasOwnProperty.call(this._blocks, id)) continue;\n const block = this._blocks[id];\n if (block.opcode === 'procedures_definition') {\n const internal = this._getCustomBlockInternal(block);\n if (internal && internal.mutation.proccode === name) {\n this._cache.procedureDefinitions[name] = id; // The outer define block id\n return id;\n }\n }\n }\n\n this._cache.procedureDefinitions[name] = null;\n return null;\n }\n\n /**\n * Get names and ids of parameters for the given procedure.\n * @param {?string} name Name of procedure to query.\n * @return {?Array.} List of param names for a procedure.\n */\n getProcedureParamNamesAndIds (name) {\n return this.getProcedureParamNamesIdsAndDefaults(name).slice(0, 2);\n }\n\n /**\n * Get names, ids, and defaults of parameters for the given procedure.\n * @param {?string} name Name of procedure to query.\n * @return {?Array.} List of param names for a procedure.\n */\n getProcedureParamNamesIdsAndDefaults (name) {\n const cachedNames = this._cache.procedureParamNames[name];\n if (typeof cachedNames !== 'undefined') {\n return cachedNames;\n }\n\n for (const id in this._blocks) {\n if (!Object.prototype.hasOwnProperty.call(this._blocks, id)) continue;\n const block = this._blocks[id];\n if (block.opcode === 'procedures_prototype' &&\n block.mutation.proccode === name) {\n const names = JSON.parse(block.mutation.argumentnames);\n const ids = JSON.parse(block.mutation.argumentids);\n const defaults = JSON.parse(block.mutation.argumentdefaults);\n\n this._cache.procedureParamNames[name] = [names, ids, defaults];\n return this._cache.procedureParamNames[name];\n }\n }\n\n this._cache.procedureParamNames[name] = null;\n return null;\n }\n\n duplicate () {\n const newBlocks = new Blocks(this.runtime, this.forceNoGlow);\n newBlocks._blocks = Clone.simple(this._blocks);\n newBlocks._scripts = Clone.simple(this._scripts);\n return newBlocks;\n }\n // ---------------------------------------------------------------------\n\n /**\n * Create event listener for blocks, variables, and comments. Handles validation and\n * serves as a generic adapter between the blocks, variables, and the\n * runtime interface.\n * @param {object} e Blockly \"block\" or \"variable\" event\n */\n blocklyListen (e) {\n // Validate event\n if (typeof e !== 'object') return;\n if (typeof e.blockId !== 'string' && typeof e.varId !== 'string' &&\n typeof e.commentId !== 'string') {\n return;\n }\n const stage = this.runtime.getTargetForStage();\n const editingTarget = this.runtime.getEditingTarget();\n\n // UI event: clicked scripts toggle in the runtime.\n if (e.element === 'stackclick') {\n this.runtime.toggleScript(e.blockId, {stackClick: true});\n return;\n }\n\n // Block create/update/destroy\n switch (e.type) {\n case 'create': {\n const newBlocks = adapter(e);\n // A create event can create many blocks. Add them all.\n for (let i = 0; i < newBlocks.length; i++) {\n this.createBlock(newBlocks[i]);\n }\n break;\n }\n case 'change':\n this.changeBlock({\n id: e.blockId,\n element: e.element,\n name: e.name,\n value: e.newValue\n });\n break;\n case 'move':\n this.moveBlock({\n id: e.blockId,\n oldParent: e.oldParentId,\n oldInput: e.oldInputName,\n newParent: e.newParentId,\n newInput: e.newInputName,\n newCoordinate: e.newCoordinate\n });\n break;\n case 'dragOutside':\n this.runtime.emitBlockDragUpdate(e.isOutside);\n break;\n case 'endDrag':\n this.runtime.emitBlockDragUpdate(false /* areBlocksOverGui */);\n\n // Drag blocks onto another sprite\n if (e.isOutside) {\n const newBlocks = adapter(e);\n this.runtime.emitBlockEndDrag(newBlocks, e.blockId);\n }\n break;\n case 'delete':\n // Don't accept delete events for missing blocks,\n // or shadow blocks being obscured.\n if (!Object.prototype.hasOwnProperty.call(this._blocks, e.blockId) ||\n this._blocks[e.blockId].shadow) {\n return;\n }\n // Inform any runtime to forget about glows on this script.\n if (this._blocks[e.blockId].topLevel) {\n this.runtime.quietGlow(e.blockId);\n }\n this.deleteBlock(e.blockId);\n break;\n case 'var_create':\n // Check if the variable being created is global or local\n // If local, create a local var on the current editing target, as long\n // as there are no conflicts, and the current target is actually a sprite\n // If global or if the editing target is not present or we somehow got\n // into a state where a local var was requested for the stage,\n // create a stage (global) var after checking for name conflicts\n // on all the sprites.\n if (e.isLocal && editingTarget && !editingTarget.isStage && !e.isCloud) {\n if (!editingTarget.lookupVariableById(e.varId)) {\n editingTarget.createVariable(e.varId, e.varName, e.varType);\n this.emitProjectChanged();\n }\n } else {\n if (stage.lookupVariableById(e.varId)) {\n // Do not re-create a variable if it already exists\n return;\n }\n // Check for name conflicts in all of the targets\n const allTargets = this.runtime.targets.filter(t => t.isOriginal);\n for (const target of allTargets) {\n if (target.lookupVariableByNameAndType(e.varName, e.varType, true)) {\n return;\n }\n }\n stage.createVariable(e.varId, e.varName, e.varType, e.isCloud);\n this.emitProjectChanged();\n }\n break;\n case 'var_rename':\n if (editingTarget && Object.prototype.hasOwnProperty.call(editingTarget.variables, e.varId)) {\n // This is a local variable, rename on the current target\n editingTarget.renameVariable(e.varId, e.newName);\n // Update all the blocks on the current target that use\n // this variable\n editingTarget.blocks.updateBlocksAfterVarRename(e.varId, e.newName);\n } else {\n // This is a global variable\n stage.renameVariable(e.varId, e.newName);\n // Update all blocks on all targets that use the renamed variable\n const targets = this.runtime.targets;\n for (let i = 0; i < targets.length; i++) {\n const currTarget = targets[i];\n currTarget.blocks.updateBlocksAfterVarRename(e.varId, e.newName);\n }\n }\n this.emitProjectChanged();\n break;\n case 'var_delete': {\n const target = (editingTarget && Object.prototype.hasOwnProperty.call(editingTarget.variables, e.varId)) ?\n editingTarget : stage;\n target.deleteVariable(e.varId);\n this.emitProjectChanged();\n break;\n }\n case 'comment_create':\n if (this.runtime.getEditingTarget()) {\n const currTarget = this.runtime.getEditingTarget();\n currTarget.createComment(e.commentId, e.blockId, e.text,\n e.xy.x, e.xy.y, e.width, e.height, e.minimized);\n\n if (currTarget.comments[e.commentId].x === null &&\n currTarget.comments[e.commentId].y === null) {\n // Block comments imported from 2.0 projects are imported with their\n // x and y coordinates set to null so that scratch-blocks can\n // auto-position them. If we are receiving a create event for these\n // comments, then the auto positioning should have taken place.\n // Update the x and y position of these comments to match the\n // one from the event.\n currTarget.comments[e.commentId].x = e.xy.x;\n currTarget.comments[e.commentId].y = e.xy.y;\n }\n }\n this.emitProjectChanged();\n break;\n case 'comment_change':\n if (this.runtime.getEditingTarget()) {\n const currTarget = this.runtime.getEditingTarget();\n if (!Object.prototype.hasOwnProperty.call(currTarget.comments, e.commentId)) {\n log.warn(`Cannot change comment with id ${e.commentId} because it does not exist.`);\n return;\n }\n const comment = currTarget.comments[e.commentId];\n const change = e.newContents_;\n if (Object.prototype.hasOwnProperty.call(change, 'minimized')) {\n comment.minimized = change.minimized;\n }\n if (Object.prototype.hasOwnProperty.call(change, 'width') &&\n Object.prototype.hasOwnProperty.call(change, 'height')) {\n comment.width = change.width;\n comment.height = change.height;\n }\n if (Object.prototype.hasOwnProperty.call(change, 'text')) {\n comment.text = change.text;\n }\n this.emitProjectChanged();\n }\n break;\n case 'comment_move':\n if (this.runtime.getEditingTarget()) {\n const currTarget = this.runtime.getEditingTarget();\n if (currTarget && !Object.prototype.hasOwnProperty.call(currTarget.comments, e.commentId)) {\n log.warn(`Cannot change comment with id ${e.commentId} because it does not exist.`);\n return;\n }\n const comment = currTarget.comments[e.commentId];\n const newCoord = e.newCoordinate_;\n comment.x = newCoord.x;\n comment.y = newCoord.y;\n\n this.emitProjectChanged();\n }\n break;\n case 'comment_delete':\n if (this.runtime.getEditingTarget()) {\n const currTarget = this.runtime.getEditingTarget();\n if (!Object.prototype.hasOwnProperty.call(currTarget.comments, e.commentId)) {\n // If we're in this state, we have probably received\n // a delete event from a workspace that we switched from\n // (e.g. a delete event for a comment on sprite a's workspace\n // when switching from sprite a to sprite b)\n return;\n }\n delete currTarget.comments[e.commentId];\n if (e.blockId) {\n const block = currTarget.blocks.getBlock(e.blockId);\n if (!block) {\n log.warn(`Could not find block referenced by comment with id: ${e.commentId}`);\n return;\n }\n delete block.comment;\n }\n\n this.emitProjectChanged();\n }\n break;\n }\n }\n\n // ---------------------------------------------------------------------\n\n /**\n * Reset all runtime caches.\n */\n resetCache () {\n this._cache.inputs = {};\n this._cache.procedureParamNames = {};\n this._cache.procedureDefinitions = {};\n this._cache._executeCached = {};\n this._cache._monitored = null;\n this._cache.scripts = {};\n }\n\n /**\n * Emit a project changed event if this is a block container\n * that can affect the project state.\n */\n emitProjectChanged () {\n if (!this.forceNoGlow) {\n this.runtime.emitProjectChanged();\n }\n }\n\n /**\n * Block management: create blocks and scripts from a `create` event\n * @param {!object} block Blockly create event to be processed\n */\n createBlock (block) {\n // Does the block already exist?\n // Could happen, e.g., for an unobscured shadow.\n if (Object.prototype.hasOwnProperty.call(this._blocks, block.id)) {\n return;\n }\n // Create new block.\n this._blocks[block.id] = block;\n // Push block id to scripts array.\n // Blocks are added as a top-level stack if they are marked as a top-block\n // (if they were top-level XML in the event).\n if (block.topLevel) {\n this._addScript(block.id);\n }\n\n this.resetCache();\n\n // A new block was actually added to the block container,\n // emit a project changed event\n this.emitProjectChanged();\n }\n\n /**\n * Block management: change block field values\n * @param {!object} args Blockly change event to be processed\n */\n changeBlock (args) {\n // Validate\n if (['field', 'mutation', 'checkbox'].indexOf(args.element) === -1) return;\n let block = this._blocks[args.id];\n if (typeof block === 'undefined') return;\n switch (args.element) {\n case 'field':\n // TODO when the field of a monitored block changes,\n // update the checkbox in the flyout based on whether\n // a monitor for that current combination of selected parameters exists\n // e.g.\n // 1. check (current [v year])\n // 2. switch dropdown in flyout block to (current [v minute])\n // 3. the checkbox should become unchecked if we're not already\n // monitoring current minute\n\n\n // Update block value\n if (!block.fields[args.name]) return;\n if (args.name === 'VARIABLE' || args.name === 'LIST' ||\n args.name === 'BROADCAST_OPTION') {\n // Get variable name using the id in args.value.\n const variable = this.runtime.getEditingTarget().lookupVariableById(args.value);\n if (variable) {\n block.fields[args.name].value = variable.name;\n block.fields[args.name].id = args.value;\n }\n } else {\n // Changing the value in a dropdown\n block.fields[args.name].value = args.value;\n\n // The selected item in the sensing of block menu needs to change based on the\n // selected target. Set it to the first item in the menu list.\n // TODO: (#1787)\n if (block.opcode === 'sensing_of_object_menu') {\n if (block.fields.OBJECT.value === '_stage_') {\n this._blocks[block.parent].fields.PROPERTY.value = 'backdrop #';\n } else {\n this._blocks[block.parent].fields.PROPERTY.value = 'x position';\n }\n this.runtime.requestBlocksUpdate();\n }\n\n const flyoutBlock = block.shadow && block.parent ? this._blocks[block.parent] : block;\n if (flyoutBlock.isMonitored) {\n this.runtime.requestUpdateMonitor(Map({\n id: flyoutBlock.id,\n params: this._getBlockParams(flyoutBlock)\n }));\n }\n }\n break;\n case 'mutation':\n block.mutation = mutationAdapter(args.value);\n break;\n case 'checkbox': {\n // A checkbox usually has a one to one correspondence with the monitor\n // block but in the case of monitored reporters that have arguments,\n // map the old id to a new id, creating a new monitor block if necessary\n if (block.fields && Object.keys(block.fields).length > 0 &&\n block.opcode !== 'data_variable' && block.opcode !== 'data_listcontents') {\n\n // This block has an argument which needs to get separated out into\n // multiple monitor blocks with ids based on the selected argument\n const newId = getMonitorIdForBlockWithArgs(block.id, block.fields);\n // Note: we're not just constantly creating a longer and longer id everytime we check\n // the checkbox because we're using the id of the block in the flyout as the base\n\n // check if a block with the new id already exists, otherwise create\n let newBlock = this.runtime.monitorBlocks.getBlock(newId);\n if (!newBlock) {\n newBlock = JSON.parse(JSON.stringify(block));\n newBlock.id = newId;\n this.runtime.monitorBlocks.createBlock(newBlock);\n }\n\n block = newBlock; // Carry on through the rest of this code with newBlock\n }\n\n const wasMonitored = block.isMonitored;\n block.isMonitored = args.value;\n\n // Variable blocks may be sprite specific depending on the owner of the variable\n let isSpriteLocalVariable = false;\n if (block.opcode === 'data_variable') {\n isSpriteLocalVariable = !(this.runtime.getTargetForStage().variables[block.fields.VARIABLE.id]);\n } else if (block.opcode === 'data_listcontents') {\n isSpriteLocalVariable = !(this.runtime.getTargetForStage().variables[block.fields.LIST.id]);\n }\n\n const isSpriteSpecific = isSpriteLocalVariable ||\n (Object.prototype.hasOwnProperty.call(this.runtime.monitorBlockInfo, block.opcode) &&\n this.runtime.monitorBlockInfo[block.opcode].isSpriteSpecific);\n if (isSpriteSpecific) {\n // If creating a new sprite specific monitor, the only possible target is\n // the current editing one b/c you cannot dynamically create monitors.\n // Also, do not change the targetId if it has already been assigned\n block.targetId = block.targetId || this.runtime.getEditingTarget().id;\n } else {\n block.targetId = null;\n }\n\n if (wasMonitored && !block.isMonitored) {\n this.runtime.requestHideMonitor(block.id);\n } else if (!wasMonitored && block.isMonitored) {\n // Tries to show the monitor for specified block. If it doesn't exist, add the monitor.\n if (!this.runtime.requestShowMonitor(block.id)) {\n this.runtime.requestAddMonitor(MonitorRecord({\n id: block.id,\n targetId: block.targetId,\n spriteName: block.targetId ? this.runtime.getTargetById(block.targetId).getName() : null,\n opcode: block.opcode,\n params: this._getBlockParams(block),\n // @todo(vm#565) for numerical values with decimals, some countries use comma\n value: '',\n mode: block.opcode === 'data_listcontents' ? 'list' : 'default'\n }));\n }\n }\n break;\n }\n }\n\n this.emitProjectChanged();\n\n this.resetCache();\n }\n\n /**\n * Block management: move blocks from parent to parent\n * @param {!object} e Blockly move event to be processed\n */\n moveBlock (e) {\n if (!Object.prototype.hasOwnProperty.call(this._blocks, e.id)) {\n return;\n }\n\n const block = this._blocks[e.id];\n // Track whether a change actually occurred\n // ignoring changes like routine re-positioning\n // of a block when loading a workspace\n let didChange = false;\n\n // Move coordinate changes.\n if (e.newCoordinate) {\n\n didChange = (block.x !== e.newCoordinate.x) || (block.y !== e.newCoordinate.y);\n\n block.x = e.newCoordinate.x;\n block.y = e.newCoordinate.y;\n }\n\n // Remove from any old parent.\n if (typeof e.oldParent !== 'undefined') {\n const oldParent = this._blocks[e.oldParent];\n if (typeof e.oldInput !== 'undefined' &&\n oldParent.inputs[e.oldInput].block === e.id) {\n // This block was connected to the old parent's input.\n oldParent.inputs[e.oldInput].block = null;\n } else if (oldParent.next === e.id) {\n // This block was connected to the old parent's next connection.\n oldParent.next = null;\n }\n this._blocks[e.id].parent = null;\n didChange = true;\n }\n\n // Is this block a top-level block?\n if (typeof e.newParent === 'undefined') {\n this._addScript(e.id);\n } else {\n // Remove script, if one exists.\n this._deleteScript(e.id);\n // Otherwise, try to connect it in its new place.\n if (typeof e.newInput === 'undefined') {\n // Moved to the new parent's next connection.\n this._blocks[e.newParent].next = e.id;\n } else {\n // Moved to the new parent's input.\n // Don't obscure the shadow block.\n let oldShadow = null;\n if (Object.prototype.hasOwnProperty.call(this._blocks[e.newParent].inputs, e.newInput)) {\n oldShadow = this._blocks[e.newParent].inputs[e.newInput].shadow;\n }\n\n // If the block being attached is itself a shadow, make sure to set\n // both block and shadow to that blocks ID. This happens when adding\n // inputs to a custom procedure.\n if (this._blocks[e.id].shadow) oldShadow = e.id;\n\n this._blocks[e.newParent].inputs[e.newInput] = {\n name: e.newInput,\n block: e.id,\n shadow: oldShadow\n };\n }\n this._blocks[e.id].parent = e.newParent;\n didChange = true;\n }\n this.resetCache();\n\n if (didChange) this.emitProjectChanged();\n }\n\n\n /**\n * Block management: run all blocks.\n * @param {!object} runtime Runtime to run all blocks in.\n */\n runAllMonitored (runtime) {\n if (this._cache._monitored === null) {\n this._cache._monitored = Object.keys(this._blocks)\n .filter(blockId => this.getBlock(blockId).isMonitored)\n .map(blockId => {\n const targetId = this.getBlock(blockId).targetId;\n return {\n blockId,\n target: targetId ? runtime.getTargetById(targetId) : null\n };\n });\n }\n\n const monitored = this._cache._monitored;\n for (let i = 0; i < monitored.length; i++) {\n const {blockId, target} = monitored[i];\n runtime.addMonitorScript(blockId, target);\n }\n }\n\n /**\n * Block management: delete blocks and their associated scripts. Does nothing if a block\n * with the given ID does not exist.\n * @param {!string} blockId Id of block to delete\n */\n deleteBlock (blockId) {\n // @todo In runtime, stop threads running on this script.\n\n // Get block\n const block = this._blocks[blockId];\n if (!block) {\n // No block with the given ID exists\n return;\n }\n\n // Delete children\n if (block.next !== null) {\n this.deleteBlock(block.next);\n }\n\n // Delete inputs (including branches)\n for (const input in block.inputs) {\n // If it's null, the block in this input moved away.\n if (block.inputs[input].block !== null) {\n this.deleteBlock(block.inputs[input].block);\n }\n // Delete obscured shadow blocks.\n if (block.inputs[input].shadow !== null &&\n block.inputs[input].shadow !== block.inputs[input].block) {\n this.deleteBlock(block.inputs[input].shadow);\n }\n }\n\n // Delete any script starting with this block.\n this._deleteScript(blockId);\n\n // Delete block itself.\n delete this._blocks[blockId];\n\n this.resetCache();\n this.emitProjectChanged();\n }\n\n /**\n * Delete all blocks and their associated scripts.\n */\n deleteAllBlocks () {\n const blockIds = Object.keys(this._blocks);\n blockIds.forEach(blockId => this.deleteBlock(blockId));\n }\n\n /**\n * Returns a map of all references to variables or lists from blocks\n * in this block container.\n * @param {Array} optBlocks Optional list of blocks to constrain the search to.\n * This is useful for getting variable/list references for a stack of blocks instead\n * of all blocks on the workspace\n * @param {?boolean} optIncludeBroadcast Optional whether to include broadcast fields.\n * @return {object} A map of variable ID to a list of all variable references\n * for that ID. A variable reference contains the field referencing that variable\n * and also the type of the variable being referenced.\n */\n getAllVariableAndListReferences (optBlocks, optIncludeBroadcast) {\n const blocks = optBlocks ? optBlocks : this._blocks;\n const allReferences = Object.create(null);\n for (const blockId in blocks) {\n let varOrListField = null;\n let varType = null;\n if (blocks[blockId].fields.VARIABLE) {\n varOrListField = blocks[blockId].fields.VARIABLE;\n varType = Variable.SCALAR_TYPE;\n } else if (blocks[blockId].fields.LIST) {\n varOrListField = blocks[blockId].fields.LIST;\n varType = Variable.LIST_TYPE;\n } else if (optIncludeBroadcast && blocks[blockId].fields.BROADCAST_OPTION) {\n varOrListField = blocks[blockId].fields.BROADCAST_OPTION;\n varType = Variable.BROADCAST_MESSAGE_TYPE;\n }\n if (varOrListField) {\n const currVarId = varOrListField.id;\n if (allReferences[currVarId]) {\n allReferences[currVarId].push({\n referencingField: varOrListField,\n type: varType\n });\n } else {\n allReferences[currVarId] = [{\n referencingField: varOrListField,\n type: varType\n }];\n }\n }\n }\n return allReferences;\n }\n\n /**\n * Keep blocks up to date after a variable gets renamed.\n * @param {string} varId The id of the variable that was renamed\n * @param {string} newName The new name of the variable that was renamed\n */\n updateBlocksAfterVarRename (varId, newName) {\n const blocks = this._blocks;\n for (const blockId in blocks) {\n let varOrListField = null;\n if (blocks[blockId].fields.VARIABLE) {\n varOrListField = blocks[blockId].fields.VARIABLE;\n } else if (blocks[blockId].fields.LIST) {\n varOrListField = blocks[blockId].fields.LIST;\n }\n if (varOrListField) {\n const currFieldId = varOrListField.id;\n if (varId === currFieldId) {\n varOrListField.value = newName;\n }\n }\n }\n }\n\n /**\n * Keep blocks up to date after they are shared between targets.\n * @param {boolean} isStage If the new target is a stage.\n */\n updateTargetSpecificBlocks (isStage) {\n const blocks = this._blocks;\n for (const blockId in blocks) {\n if (isStage && blocks[blockId].opcode === 'event_whenthisspriteclicked') {\n blocks[blockId].opcode = 'event_whenstageclicked';\n } else if (!isStage && blocks[blockId].opcode === 'event_whenstageclicked') {\n blocks[blockId].opcode = 'event_whenthisspriteclicked';\n }\n }\n }\n\n /**\n * Update blocks after a sound, costume, or backdrop gets renamed.\n * Any block referring to the old name of the asset should get updated\n * to refer to the new name.\n * @param {string} oldName The old name of the asset that was renamed.\n * @param {string} newName The new name of the asset that was renamed.\n * @param {string} assetType String representation of the kind of asset\n * that was renamed. This can be one of 'sprite','costume', 'sound', or\n * 'backdrop'.\n */\n updateAssetName (oldName, newName, assetType) {\n let getAssetField;\n if (assetType === 'costume') {\n getAssetField = this._getCostumeField.bind(this);\n } else if (assetType === 'sound') {\n getAssetField = this._getSoundField.bind(this);\n } else if (assetType === 'backdrop') {\n getAssetField = this._getBackdropField.bind(this);\n } else if (assetType === 'sprite') {\n getAssetField = this._getSpriteField.bind(this);\n } else {\n return;\n }\n const blocks = this._blocks;\n for (const blockId in blocks) {\n const assetField = getAssetField(blockId);\n if (assetField && assetField.value === oldName) {\n assetField.value = newName;\n }\n }\n }\n\n /**\n * Update sensing_of blocks after a variable gets renamed.\n * @param {string} oldName The old name of the variable that was renamed.\n * @param {string} newName The new name of the variable that was renamed.\n * @param {string} targetName The name of the target the variable belongs to.\n * @return {boolean} Returns true if any of the blocks were updated.\n */\n updateSensingOfReference (oldName, newName, targetName) {\n const blocks = this._blocks;\n let blockUpdated = false;\n for (const blockId in blocks) {\n const block = blocks[blockId];\n if (block.opcode === 'sensing_of' &&\n block.fields.PROPERTY.value === oldName &&\n // If block and shadow are different, it means a block is inserted to OBJECT, and should be ignored.\n block.inputs.OBJECT.block === block.inputs.OBJECT.shadow) {\n const inputBlock = this.getBlock(block.inputs.OBJECT.block);\n if (inputBlock.fields.OBJECT.value === targetName) {\n block.fields.PROPERTY.value = newName;\n blockUpdated = true;\n }\n }\n }\n if (blockUpdated) this.resetCache();\n return blockUpdated;\n }\n\n /**\n * Helper function to retrieve a costume menu field from a block given its id.\n * @param {string} blockId A unique identifier for a block\n * @return {?object} The costume menu field of the block with the given block id.\n * Null if either a block with the given id doesn't exist or if a costume menu field\n * does not exist on the block with the given id.\n */\n _getCostumeField (blockId) {\n const block = this.getBlock(blockId);\n if (block && Object.prototype.hasOwnProperty.call(block.fields, 'COSTUME')) {\n return block.fields.COSTUME;\n }\n return null;\n }\n\n /**\n * Helper function to retrieve a sound menu field from a block given its id.\n * @param {string} blockId A unique identifier for a block\n * @return {?object} The sound menu field of the block with the given block id.\n * Null, if either a block with the given id doesn't exist or if a sound menu field\n * does not exist on the block with the given id.\n */\n _getSoundField (blockId) {\n const block = this.getBlock(blockId);\n if (block && Object.prototype.hasOwnProperty.call(block.fields, 'SOUND_MENU')) {\n return block.fields.SOUND_MENU;\n }\n return null;\n }\n\n /**\n * Helper function to retrieve a backdrop menu field from a block given its id.\n * @param {string} blockId A unique identifier for a block\n * @return {?object} The backdrop menu field of the block with the given block id.\n * Null, if either a block with the given id doesn't exist or if a backdrop menu field\n * does not exist on the block with the given id.\n */\n _getBackdropField (blockId) {\n const block = this.getBlock(blockId);\n if (block && Object.prototype.hasOwnProperty.call(block.fields, 'BACKDROP')) {\n return block.fields.BACKDROP;\n }\n return null;\n }\n\n /**\n * Helper function to retrieve a sprite menu field from a block given its id.\n * @param {string} blockId A unique identifier for a block\n * @return {?object} The sprite menu field of the block with the given block id.\n * Null, if either a block with the given id doesn't exist or if a sprite menu field\n * does not exist on the block with the given id.\n */\n _getSpriteField (blockId) {\n const block = this.getBlock(blockId);\n if (!block) {\n return null;\n }\n const spriteMenuNames = ['TOWARDS', 'TO', 'OBJECT', 'VIDEOONMENU2',\n 'DISTANCETOMENU', 'TOUCHINGOBJECTMENU', 'CLONE_OPTION'];\n for (let i = 0; i < spriteMenuNames.length; i++) {\n const menuName = spriteMenuNames[i];\n if (Object.prototype.hasOwnProperty.call(block.fields, menuName)) {\n return block.fields[menuName];\n }\n }\n return null;\n }\n\n // ---------------------------------------------------------------------\n\n /**\n * Encode all of `this._blocks` as an XML string usable\n * by a Blockly/scratch-blocks workspace.\n * @param {object} comments Map of comments referenced by id\n * @return {string} String of XML representing this object's blocks.\n */\n toXML (comments) {\n return this._scripts.map(script => this.blockToXML(script, comments)).join();\n }\n\n /**\n * Recursively encode an individual block and its children\n * into a Blockly/scratch-blocks XML string.\n * @param {!string} blockId ID of block to encode.\n * @param {object} comments Map of comments referenced by id\n * @return {string} String of XML representing this block and any children.\n */\n blockToXML (blockId, comments) {\n const block = this._blocks[blockId];\n // block should exist, but currently some blocks' next property point\n // to a blockId for non-existent blocks. Until we track down that behavior,\n // this early exit allows the project to load.\n if (!block) return;\n // Encode properties of this block.\n const tagName = (block.shadow) ? 'shadow' : 'block';\n let xmlString =\n `<${tagName}\n id=\"${block.id}\"\n type=\"${block.opcode}\"\n ${block.topLevel ? `x=\"${block.x}\" y=\"${block.y}\"` : ''}\n >`;\n const commentId = block.comment;\n if (commentId) {\n if (comments) {\n if (Object.prototype.hasOwnProperty.call(comments, commentId)) {\n xmlString += comments[commentId].toXML();\n } else {\n log.warn(`Could not find comment with id: ${commentId} in provided comment descriptions.`);\n }\n } else {\n log.warn(`Cannot serialize comment with id: ${commentId}; no comment descriptions provided.`);\n }\n }\n // Add any mutation. Must come before inputs.\n if (block.mutation) {\n xmlString += this.mutationToXML(block.mutation);\n }\n // Add any inputs on this block.\n for (const input in block.inputs) {\n if (!Object.prototype.hasOwnProperty.call(block.inputs, input)) continue;\n const blockInput = block.inputs[input];\n // Only encode a value tag if the value input is occupied.\n if (blockInput.block || blockInput.shadow) {\n xmlString += ``;\n if (blockInput.block) {\n xmlString += this.blockToXML(blockInput.block, comments);\n }\n if (blockInput.shadow && blockInput.shadow !== blockInput.block) {\n // Obscured shadow.\n xmlString += this.blockToXML(blockInput.shadow, comments);\n }\n xmlString += '';\n }\n }\n // Add any fields on this block.\n for (const field in block.fields) {\n if (!Object.prototype.hasOwnProperty.call(block.fields, field)) continue;\n const blockField = block.fields[field];\n xmlString += `${value}`;\n }\n // Add blocks connected to the next connection.\n if (block.next) {\n xmlString += `${this.blockToXML(block.next, comments)}`;\n }\n xmlString += ``;\n return xmlString;\n }\n\n /**\n * Recursively encode a mutation object to XML.\n * @param {!object} mutation Object representing a mutation.\n * @return {string} XML string representing a mutation.\n */\n mutationToXML (mutation) {\n let mutationString = `<${mutation.tagName}`;\n for (const prop in mutation) {\n if (prop === 'children' || prop === 'tagName') continue;\n let mutationValue = (typeof mutation[prop] === 'string') ?\n xmlEscape(mutation[prop]) : mutation[prop];\n\n // Handle dynamic extension blocks\n if (prop === 'blockInfo') {\n mutationValue = xmlEscape(JSON.stringify(mutation[prop]));\n }\n\n mutationString += ` ${prop}=\"${mutationValue}\"`;\n }\n mutationString += '>';\n for (let i = 0; i < mutation.children.length; i++) {\n mutationString += this.mutationToXML(mutation.children[i]);\n }\n mutationString += ``;\n return mutationString;\n }\n\n // ---------------------------------------------------------------------\n /**\n * Helper to serialize block fields and input fields for reporting new monitors\n * @param {!object} block Block to be paramified.\n * @return {!object} object of param key/values.\n */\n _getBlockParams (block) {\n const params = {};\n for (const key in block.fields) {\n params[key] = block.fields[key].value;\n }\n for (const inputKey in block.inputs) {\n const inputBlock = this._blocks[block.inputs[inputKey].block];\n for (const key in inputBlock.fields) {\n params[key] = inputBlock.fields[key].value;\n }\n }\n return params;\n }\n\n /**\n * Helper to get the corresponding internal procedure definition block\n * @param {!object} defineBlock Outer define block.\n * @return {!object} internal definition block which has the mutation.\n */\n _getCustomBlockInternal (defineBlock) {\n if (defineBlock.inputs && defineBlock.inputs.custom_block) {\n return this._blocks[defineBlock.inputs.custom_block.block];\n }\n }\n\n /**\n * Helper to add a stack to `this._scripts`.\n * @param {?string} topBlockId ID of block that starts the script.\n */\n _addScript (topBlockId) {\n const i = this._scripts.indexOf(topBlockId);\n if (i > -1) return; // Already in scripts.\n this._scripts.push(topBlockId);\n // Update `topLevel` property on the top block.\n this._blocks[topBlockId].topLevel = true;\n }\n\n /**\n * Helper to remove a script from `this._scripts`.\n * @param {?string} topBlockId ID of block that starts the script.\n */\n _deleteScript (topBlockId) {\n const i = this._scripts.indexOf(topBlockId);\n if (i > -1) this._scripts.splice(i, 1);\n // Update `topLevel` property on the top block.\n if (this._blocks[topBlockId]) this._blocks[topBlockId].topLevel = false;\n }\n}\n\n/**\n * A private method shared with execute to build an object containing the block\n * information execute needs and that is reset when other cached Blocks info is\n * reset.\n * @param {Blocks} blocks Blocks containing the expected blockId\n * @param {string} blockId blockId for the desired execute cache\n * @param {function} CacheType constructor for cached block information\n * @return {object} execute cache object\n */\nBlocksExecuteCache.getCached = function (blocks, blockId, CacheType) {\n let cached = blocks._cache._executeCached[blockId];\n if (typeof cached !== 'undefined') {\n return cached;\n }\n\n const block = blocks.getBlock(blockId);\n if (typeof block === 'undefined') return null;\n\n if (typeof CacheType === 'undefined') {\n cached = {\n id: blockId,\n opcode: blocks.getOpcode(block),\n fields: blocks.getFields(block),\n inputs: blocks.getInputs(block),\n mutation: blocks.getMutation(block)\n };\n } else {\n cached = new CacheType(blocks, {\n id: blockId,\n opcode: blocks.getOpcode(block),\n fields: blocks.getFields(block),\n inputs: blocks.getInputs(block),\n mutation: blocks.getMutation(block)\n });\n }\n\n blocks._cache._executeCached[blockId] = cached;\n return cached;\n};\n\n/**\n * Cache class constructor for runtime. Used to consider what threads should\n * start based on hat data.\n * @type {function}\n */\nconst RuntimeScriptCache = BlocksRuntimeCache._RuntimeScriptCache;\n\n/**\n * Get an array of scripts from a block container prefiltered to match opcode.\n * @param {Blocks} blocks - Container of blocks\n * @param {string} opcode - Opcode to filter top blocks by\n * @returns {Array.} - Array of RuntimeScriptCache cache\n * objects\n */\nBlocksRuntimeCache.getScripts = function (blocks, opcode) {\n let scripts = blocks._cache.scripts[opcode];\n if (!scripts) {\n scripts = blocks._cache.scripts[opcode] = [];\n\n const allScripts = blocks._scripts;\n for (let i = 0; i < allScripts.length; i++) {\n const topBlockId = allScripts[i];\n const block = blocks.getBlock(topBlockId);\n if (block.opcode === opcode) {\n scripts.push(new RuntimeScriptCache(blocks, topBlockId));\n }\n }\n }\n return scripts;\n};\n\nmodule.exports = Blocks;\n","/**\n * @fileoverview\n * Object representing a Scratch Comment (block or workspace).\n */\n\nconst uid = require('../util/uid');\nconst xmlEscape = require('../util/xml-escape');\n\nclass Comment {\n /**\n * @param {string} id Id of the comment.\n * @param {string} text Text content of the comment.\n * @param {number} x X position of the comment on the workspace.\n * @param {number} y Y position of the comment on the workspace.\n * @param {number} width The width of the comment when it is full size.\n * @param {number} height The height of the comment when it is full size.\n * @param {boolean} minimized Whether the comment is minimized.\n * @constructor\n */\n constructor (id, text, x, y, width, height, minimized) {\n this.id = id || uid();\n this.text = text;\n this.x = x;\n this.y = y;\n this.width = Math.max(Number(width), Comment.MIN_WIDTH);\n this.height = Math.max(Number(height), Comment.MIN_HEIGHT);\n this.minimized = minimized || false;\n this.blockId = null;\n }\n\n toXML () {\n return `${xmlEscape(this.text)}`;\n }\n\n // TODO choose min and defaults for width and height\n static get MIN_WIDTH () {\n return 20;\n }\n\n static get MIN_HEIGHT () {\n return 20;\n }\n\n static get DEFAULT_WIDTH () {\n return 100;\n }\n\n static get DEFAULT_HEIGHT () {\n return 100;\n }\n\n}\n\nmodule.exports = Comment;\n","const BlockUtility = require('./block-utility');\nconst BlocksExecuteCache = require('./blocks-execute-cache');\nconst log = require('../util/log');\nconst Thread = require('./thread');\nconst {Map} = require('immutable');\nconst cast = require('../util/cast');\n\n/**\n * Single BlockUtility instance reused by execute for every pritimive ran.\n * @const\n */\nconst blockUtility = new BlockUtility();\n\n/**\n * Profiler frame name for block functions.\n * @const {string}\n */\nconst blockFunctionProfilerFrame = 'blockFunction';\n\n/**\n * Profiler frame ID for 'blockFunction'.\n * @type {number}\n */\nlet blockFunctionProfilerId = -1;\n\n/**\n * Utility function to determine if a value is a Promise.\n * @param {*} value Value to check for a Promise.\n * @return {boolean} True if the value appears to be a Promise.\n */\nconst isPromise = function (value) {\n return (\n value !== null &&\n typeof value === 'object' &&\n typeof value.then === 'function'\n );\n};\n\n/**\n * Handle any reported value from the primitive, either directly returned\n * or after a promise resolves.\n * @param {*} resolvedValue Value eventually returned from the primitive.\n * @param {!Sequencer} sequencer Sequencer stepping the thread for the ran\n * primitive.\n * @param {!Thread} thread Thread containing the primitive.\n * @param {!string} currentBlockId Id of the block in its thread for value from\n * the primitive.\n * @param {!string} opcode opcode used to identify a block function primitive.\n * @param {!boolean} isHat Is the current block a hat?\n */\n// @todo move this to callback attached to the thread when we have performance\n// metrics (dd)\nconst handleReport = function (resolvedValue, sequencer, thread, blockCached, lastOperation) {\n const currentBlockId = blockCached.id;\n const opcode = blockCached.opcode;\n const isHat = blockCached._isHat;\n\n thread.pushReportedValue(resolvedValue);\n if (isHat) {\n // Hat predicate was evaluated.\n if (sequencer.runtime.getIsEdgeActivatedHat(opcode)) {\n // If this is an edge-activated hat, only proceed if the value is\n // true and used to be false, or the stack was activated explicitly\n // via stack click\n if (!thread.stackClick) {\n const hasOldEdgeValue = thread.target.hasEdgeActivatedValue(currentBlockId);\n const oldEdgeValue = thread.target.updateEdgeActivatedValue(\n currentBlockId,\n resolvedValue\n );\n\n const edgeWasActivated = hasOldEdgeValue ? (!oldEdgeValue && resolvedValue) : resolvedValue;\n if (!edgeWasActivated) {\n sequencer.retireThread(thread);\n }\n }\n } else if (!resolvedValue) {\n // Not an edge-activated hat: retire the thread\n // if predicate was false.\n sequencer.retireThread(thread);\n }\n } else {\n // In a non-hat, report the value visually if necessary if\n // at the top of the thread stack.\n if (lastOperation && typeof resolvedValue !== 'undefined' && thread.atStackTop()) {\n if (thread.stackClick) {\n sequencer.runtime.visualReport(currentBlockId, resolvedValue);\n }\n if (thread.updateMonitor) {\n const targetId = sequencer.runtime.monitorBlocks.getBlock(currentBlockId).targetId;\n if (targetId && !sequencer.runtime.getTargetById(targetId)) {\n // Target no longer exists\n return;\n }\n sequencer.runtime.requestUpdateMonitor(Map({\n id: currentBlockId,\n spriteName: targetId ? sequencer.runtime.getTargetById(targetId).getName() : null,\n value: resolvedValue\n }));\n }\n }\n // Finished any yields.\n thread.status = Thread.STATUS_RUNNING;\n }\n};\n\nconst handlePromise = (primitiveReportedValue, sequencer, thread, blockCached, lastOperation) => {\n if (thread.status === Thread.STATUS_RUNNING) {\n // Primitive returned a promise; automatically yield thread.\n thread.status = Thread.STATUS_PROMISE_WAIT;\n }\n // Promise handlers\n primitiveReportedValue.then(resolvedValue => {\n handleReport(resolvedValue, sequencer, thread, blockCached, lastOperation);\n // If it's a command block or a top level reporter in a stackClick.\n if (lastOperation) {\n let stackFrame;\n let nextBlockId;\n do {\n // In the case that the promise is the last block in the current thread stack\n // We need to pop out repeatedly until we find the next block.\n const popped = thread.popStack();\n if (popped === null) {\n return;\n }\n nextBlockId = thread.target.blocks.getNextBlock(popped);\n if (nextBlockId !== null) {\n // A next block exists so break out this loop\n break;\n }\n // Investigate the next block and if not in a loop,\n // then repeat and pop the next item off the stack frame\n stackFrame = thread.peekStackFrame();\n } while (stackFrame !== null && !stackFrame.isLoop);\n\n thread.pushStack(nextBlockId);\n }\n }, rejectionReason => {\n // Promise rejected: the primitive had some error.\n // Log it and proceed.\n log.warn('Primitive rejected promise: ', rejectionReason);\n thread.status = Thread.STATUS_RUNNING;\n thread.popStack();\n });\n};\n\n/**\n * A execute.js internal representation of a block to reduce the time spent in\n * execute as the same blocks are called the most.\n *\n * With the help of the Blocks class create a mutable copy of block\n * information. The members of BlockCached derived values of block information\n * that does not need to be reevaluated until a change in Blocks. Since Blocks\n * handles where the cache instance is stored, it drops all cache versions of a\n * block when any change happens to it. This way we can quickly execute blocks\n * and keep perform the right action according to the current block information\n * in the editor.\n *\n * @param {Blocks} blockContainer the related Blocks instance\n * @param {object} cached default set of cached values\n */\nclass BlockCached {\n constructor (blockContainer, cached) {\n /**\n * Block id in its parent set of blocks.\n * @type {string}\n */\n this.id = cached.id;\n\n /**\n * Block operation code for this block.\n * @type {string}\n */\n this.opcode = cached.opcode;\n\n /**\n * Original block object containing argument values for static fields.\n * @type {object}\n */\n this.fields = cached.fields;\n\n /**\n * Original block object containing argument values for executable inputs.\n * @type {object}\n */\n this.inputs = cached.inputs;\n\n /**\n * Procedure mutation.\n * @type {?object}\n */\n this.mutation = cached.mutation;\n\n /**\n * The profiler the block is configured with.\n * @type {?Profiler}\n */\n this._profiler = null;\n\n /**\n * Profiler information frame.\n * @type {?ProfilerFrame}\n */\n this._profilerFrame = null;\n\n /**\n * Is the opcode a hat (event responder) block.\n * @type {boolean}\n */\n this._isHat = false;\n\n /**\n * The block opcode's implementation function.\n * @type {?function}\n */\n this._blockFunction = null;\n\n /**\n * Is the block function defined for this opcode?\n * @type {boolean}\n */\n this._definedBlockFunction = false;\n\n /**\n * Is this block a block with no function but a static value to return.\n * @type {boolean}\n */\n this._isShadowBlock = false;\n\n /**\n * The static value of this block if it is a shadow block.\n * @type {?any}\n */\n this._shadowValue = null;\n\n /**\n * A copy of the block's fields that may be modified.\n * @type {object}\n */\n this._fields = Object.assign({}, this.fields);\n\n /**\n * A copy of the block's inputs that may be modified.\n * @type {object}\n */\n this._inputs = Object.assign({}, this.inputs);\n\n /**\n * An arguments object for block implementations. All executions of this\n * specific block will use this objecct.\n * @type {object}\n */\n this._argValues = {\n mutation: this.mutation\n };\n\n /**\n * The inputs key the parent refers to this BlockCached by.\n * @type {string}\n */\n this._parentKey = null;\n\n /**\n * The target object where the parent wants the resulting value stored\n * with _parentKey as the key.\n * @type {object}\n */\n this._parentValues = null;\n\n /**\n * A sequence of non-shadow operations that can must be performed. This\n * list recreates the order this block and its children are executed.\n * Since the order is always the same we can safely store that order\n * and iterate over the operations instead of dynamically walking the\n * tree every time.\n * @type {Array}\n */\n this._ops = [];\n\n const {runtime} = blockUtility.sequencer;\n\n const {opcode, fields, inputs} = this;\n\n // Assign opcode isHat and blockFunction data to avoid dynamic lookups.\n this._isHat = runtime.getIsHat(opcode);\n this._blockFunction = runtime.getOpcodeFunction(opcode);\n this._definedBlockFunction = typeof this._blockFunction !== 'undefined';\n\n // Store the current shadow value if there is a shadow value.\n const fieldKeys = Object.keys(fields);\n this._isShadowBlock = (\n !this._definedBlockFunction &&\n fieldKeys.length === 1 &&\n Object.keys(inputs).length === 0\n );\n this._shadowValue = this._isShadowBlock && fields[fieldKeys[0]].value;\n\n // Store the static fields onto _argValues.\n for (const fieldName in fields) {\n if (\n fieldName === 'VARIABLE' ||\n fieldName === 'LIST' ||\n fieldName === 'BROADCAST_OPTION'\n ) {\n this._argValues[fieldName] = {\n id: fields[fieldName].id,\n name: fields[fieldName].value\n };\n } else {\n this._argValues[fieldName] = fields[fieldName].value;\n }\n }\n\n // Remove custom_block. It is not part of block execution.\n delete this._inputs.custom_block;\n\n if ('BROADCAST_INPUT' in this._inputs) {\n // BROADCAST_INPUT is called BROADCAST_OPTION in the args and is an\n // object with an unchanging shape.\n this._argValues.BROADCAST_OPTION = {\n id: null,\n name: null\n };\n\n // We can go ahead and compute BROADCAST_INPUT if it is a shadow\n // value.\n const broadcastInput = this._inputs.BROADCAST_INPUT;\n if (broadcastInput.block === broadcastInput.shadow) {\n // Shadow dropdown menu is being used.\n // Get the appropriate information out of it.\n const shadow = blockContainer.getBlock(broadcastInput.shadow);\n const broadcastField = shadow.fields.BROADCAST_OPTION;\n this._argValues.BROADCAST_OPTION.id = broadcastField.id;\n this._argValues.BROADCAST_OPTION.name = broadcastField.value;\n\n // Evaluating BROADCAST_INPUT here we do not need to do so\n // later.\n delete this._inputs.BROADCAST_INPUT;\n }\n }\n\n // Cache all input children blocks in the operation lists. The\n // operations can later be run in the order they appear in correctly\n // executing the operations quickly in a flat loop instead of needing to\n // recursivly iterate them.\n for (const inputName in this._inputs) {\n const input = this._inputs[inputName];\n if (input.block) {\n const inputCached = BlocksExecuteCache.getCached(blockContainer, input.block, BlockCached);\n\n if (inputCached._isHat) {\n continue;\n }\n\n this._ops.push(...inputCached._ops);\n inputCached._parentKey = inputName;\n inputCached._parentValues = this._argValues;\n\n // Shadow values are static and do not change, go ahead and\n // store their value on args.\n if (inputCached._isShadowBlock) {\n this._argValues[inputName] = inputCached._shadowValue;\n }\n }\n }\n\n // The final operation is this block itself. At the top most block is a\n // command block or a block that is being run as a monitor.\n if (this._definedBlockFunction) {\n this._ops.push(this);\n }\n }\n}\n\n/**\n * Initialize a BlockCached instance so its command/hat\n * block and reporters can be profiled during execution.\n * @param {Profiler} profiler - The profiler that is currently enabled.\n * @param {BlockCached} blockCached - The blockCached instance to profile.\n */\nconst _prepareBlockProfiling = function (profiler, blockCached) {\n blockCached._profiler = profiler;\n\n if (blockFunctionProfilerId === -1) {\n blockFunctionProfilerId = profiler.idByName(blockFunctionProfilerFrame);\n }\n\n const ops = blockCached._ops;\n for (let i = 0; i < ops.length; i++) {\n ops[i]._profilerFrame = profiler.frame(blockFunctionProfilerId, ops[i].opcode);\n }\n};\n\n/**\n * Execute a block.\n * @param {!Sequencer} sequencer Which sequencer is executing.\n * @param {!Thread} thread Thread which to read and execute.\n */\nconst execute = function (sequencer, thread) {\n const runtime = sequencer.runtime;\n\n // store sequencer and thread so block functions can access them through\n // convenience methods.\n blockUtility.sequencer = sequencer;\n blockUtility.thread = thread;\n\n // Current block to execute is the one on the top of the stack.\n const currentBlockId = thread.peekStack();\n const currentStackFrame = thread.peekStackFrame();\n\n let blockContainer = thread.blockContainer;\n let blockCached = BlocksExecuteCache.getCached(blockContainer, currentBlockId, BlockCached);\n if (blockCached === null) {\n blockContainer = runtime.flyoutBlocks;\n blockCached = BlocksExecuteCache.getCached(blockContainer, currentBlockId, BlockCached);\n // Stop if block or target no longer exists.\n if (blockCached === null) {\n // No block found: stop the thread; script no longer exists.\n sequencer.retireThread(thread);\n return;\n }\n }\n\n const ops = blockCached._ops;\n const length = ops.length;\n let i = 0;\n\n if (currentStackFrame.reported !== null) {\n const reported = currentStackFrame.reported;\n // Reinstate all the previous values.\n for (; i < reported.length; i++) {\n const {opCached: oldOpCached, inputValue} = reported[i];\n\n const opCached = ops.find(op => op.id === oldOpCached);\n\n if (opCached) {\n const inputName = opCached._parentKey;\n const argValues = opCached._parentValues;\n\n if (inputName === 'BROADCAST_INPUT') {\n // Something is plugged into the broadcast input.\n // Cast it to a string. We don't need an id here.\n argValues.BROADCAST_OPTION.id = null;\n argValues.BROADCAST_OPTION.name = cast.toString(inputValue);\n } else {\n argValues[inputName] = inputValue;\n }\n }\n }\n\n // Find the last reported block that is still in the set of operations.\n // This way if the last operation was removed, we'll find the next\n // candidate. If an earlier block that was performed was removed then\n // we'll find the index where the last operation is now.\n if (reported.length > 0) {\n const lastExisting = reported.reverse().find(report => ops.find(op => op.id === report.opCached));\n if (lastExisting) {\n i = ops.findIndex(opCached => opCached.id === lastExisting.opCached) + 1;\n } else {\n i = 0;\n }\n }\n\n // The reporting block must exist and must be the next one in the sequence of operations.\n if (thread.justReported !== null && ops[i] && ops[i].id === currentStackFrame.reporting) {\n const opCached = ops[i];\n const inputValue = thread.justReported;\n\n thread.justReported = null;\n\n const inputName = opCached._parentKey;\n const argValues = opCached._parentValues;\n\n if (inputName === 'BROADCAST_INPUT') {\n // Something is plugged into the broadcast input.\n // Cast it to a string. We don't need an id here.\n argValues.BROADCAST_OPTION.id = null;\n argValues.BROADCAST_OPTION.name = cast.toString(inputValue);\n } else {\n argValues[inputName] = inputValue;\n }\n\n i += 1;\n }\n\n currentStackFrame.reporting = null;\n currentStackFrame.reported = null;\n }\n\n const start = i;\n\n for (; i < length; i++) {\n const lastOperation = i === length - 1;\n const opCached = ops[i];\n\n const blockFunction = opCached._blockFunction;\n\n // Update values for arguments (inputs).\n const argValues = opCached._argValues;\n\n // Fields are set during opCached initialization.\n\n // Blocks should glow when a script is starting,\n // not after it has finished (see #1404).\n // Only blocks in blockContainers that don't forceNoGlow\n // should request a glow.\n if (!blockContainer.forceNoGlow) {\n thread.requestScriptGlowInFrame = true;\n }\n\n // Inputs are set during previous steps in the loop.\n\n const primitiveReportedValue = blockFunction(argValues, blockUtility);\n\n // If it's a promise, wait until promise resolves.\n if (isPromise(primitiveReportedValue)) {\n handlePromise(primitiveReportedValue, sequencer, thread, opCached, lastOperation);\n\n // Store the already reported values. They will be thawed into the\n // future versions of the same operations by block id. The reporting\n // operation if it is promise waiting will set its parent value at\n // that time.\n thread.justReported = null;\n currentStackFrame.reporting = ops[i].id;\n currentStackFrame.reported = ops.slice(0, i).map(reportedCached => {\n const inputName = reportedCached._parentKey;\n const reportedValues = reportedCached._parentValues;\n\n if (inputName === 'BROADCAST_INPUT') {\n return {\n opCached: reportedCached.id,\n inputValue: reportedValues[inputName].BROADCAST_OPTION.name\n };\n }\n return {\n opCached: reportedCached.id,\n inputValue: reportedValues[inputName]\n };\n });\n\n // We are waiting for a promise. Stop running this set of operations\n // and continue them later after thawing the reported values.\n break;\n } else if (thread.status === Thread.STATUS_RUNNING) {\n if (lastOperation) {\n handleReport(primitiveReportedValue, sequencer, thread, opCached, lastOperation);\n } else {\n // By definition a block that is not last in the list has a\n // parent.\n const inputName = opCached._parentKey;\n const parentValues = opCached._parentValues;\n\n if (inputName === 'BROADCAST_INPUT') {\n // Something is plugged into the broadcast input.\n // Cast it to a string. We don't need an id here.\n parentValues.BROADCAST_OPTION.id = null;\n parentValues.BROADCAST_OPTION.name = cast.toString(primitiveReportedValue);\n } else {\n parentValues[inputName] = primitiveReportedValue;\n }\n }\n }\n }\n\n if (runtime.profiler !== null) {\n if (blockCached._profiler !== runtime.profiler) {\n _prepareBlockProfiling(runtime.profiler, blockCached);\n }\n // Determine the index that is after the last executed block. `i` is\n // currently the block that was just executed. `i + 1` will be the block\n // after that. `length` with the min call makes sure we don't try to\n // reference an operation outside of the set of operations.\n const end = Math.min(i + 1, length);\n for (let p = start; p < end; p++) {\n ops[p]._profilerFrame.count += 1;\n }\n }\n};\n\nmodule.exports = execute;\n","const {Record} = require('immutable');\n\nconst MonitorRecord = Record({\n id: null, // Block Id\n /** Present only if the monitor is sprite-specific, such as x position */\n spriteName: null,\n /** Present only if the monitor is sprite-specific, such as x position */\n targetId: null,\n opcode: null,\n value: null,\n params: null,\n mode: 'default',\n sliderMin: 0,\n sliderMax: 100,\n isDiscrete: true,\n x: null, // (x: null, y: null) Indicates that the monitor should be auto-positioned\n y: null,\n width: 0,\n height: 0,\n visible: true\n});\n\nmodule.exports = MonitorRecord;\n","const html = require('htmlparser2');\nconst decodeHtml = require('decode-html');\n\n/**\n * Convert a part of a mutation DOM to a mutation VM object, recursively.\n * @param {object} dom DOM object for mutation tag.\n * @return {object} Object representing useful parts of this mutation.\n */\nconst mutatorTagToObject = function (dom) {\n const obj = Object.create(null);\n obj.tagName = dom.name;\n obj.children = [];\n for (const prop in dom.attribs) {\n if (prop === 'xmlns') continue;\n obj[prop] = decodeHtml(dom.attribs[prop]);\n // Note: the capitalization of block info in the following lines is important.\n // The lowercase is read in from xml which normalizes case. The VM uses camel case everywhere else.\n if (prop === 'blockinfo') {\n obj.blockInfo = JSON.parse(obj.blockinfo);\n delete obj.blockinfo;\n }\n }\n for (let i = 0; i < dom.children.length; i++) {\n obj.children.push(\n mutatorTagToObject(dom.children[i])\n );\n }\n return obj;\n};\n\n/**\n * Adapter between mutator XML or DOM and block representation which can be\n * used by the Scratch runtime.\n * @param {(object|string)} mutation Mutation XML string or DOM.\n * @return {object} Object representing the mutation.\n */\nconst mutationAdpater = function (mutation) {\n let mutationParsed;\n // Check if the mutation is already parsed; if not, parse it.\n if (typeof mutation === 'object') {\n mutationParsed = mutation;\n } else {\n mutationParsed = html.parseDOM(mutation)[0];\n }\n return mutatorTagToObject(mutationParsed);\n};\n\nmodule.exports = mutationAdpater;\n","/**\n * @fileoverview\n * A way to profile Scratch internal performance. Like what blocks run during a\n * step? How much time do they take? How much time is spent inbetween blocks?\n *\n * Profiler aims for to spend as little time inside its functions while\n * recording. For this it has a simple internal record structure that records a\n * series of values for each START and STOP event in a single array. This lets\n * all the values be pushed in one call for the array. This simplicity allows\n * the contents of the start() and stop() calls to be inlined in areas that are\n * called frequently enough to want even greater performance from Profiler so\n * what is recorded better reflects on the profiled code and not Profiler\n * itself.\n */\n\n/**\n * The next id returned for a new profile'd function.\n * @type {number}\n */\nlet nextId = 0;\n\n/**\n * The mapping of names to ids.\n * @const {Object.}\n */\nconst profilerNames = {};\n\n/**\n * The START event identifier in Profiler records.\n * @const {number}\n */\nconst START = 0;\n\n/**\n * The STOP event identifier in Profiler records.\n * @const {number}\n */\nconst STOP = 1;\n\n/**\n * The number of cells used in the records array by a START event.\n * @const {number}\n */\nconst START_SIZE = 4;\n\n/**\n * The number of cells used in the records array by a STOP event.\n * @const {number}\n */\nconst STOP_SIZE = 2;\n\n/**\n * Stored reference to Performance instance provided by the Browser.\n * @const {Performance}\n */\nconst performance = typeof window === 'object' && window.performance;\n\n\n/**\n * Callback handle called by Profiler for each frame it decodes from its\n * records.\n * @callback FrameCallback\n * @param {ProfilerFrame} frame\n */\n\n/**\n * A set of information about a frame of execution that was recorded.\n */\nclass ProfilerFrame {\n /**\n * @param {number} depth Depth of the frame in the recorded stack.\n */\n constructor (depth) {\n /**\n * The numeric id of a record symbol like Runtime._step or\n * blockFunction.\n * @type {number}\n */\n this.id = -1;\n\n /**\n * The amount of time spent inside the recorded frame and any deeper\n * frames.\n * @type {number}\n */\n this.totalTime = 0;\n\n /**\n * The amount of time spent only inside this record frame. Not\n * including time in any deeper frames.\n * @type {number}\n */\n this.selfTime = 0;\n\n /**\n * An arbitrary argument for the recorded frame. For example a block\n * function might record its opcode as an argument.\n * @type {*}\n */\n this.arg = null;\n\n /**\n * The depth of the recorded frame. This can help compare recursive\n * funtions that are recorded. Each level of recursion with have a\n * different depth value.\n * @type {number}\n */\n this.depth = depth;\n\n /**\n * A summarized count of the number of calls to this frame.\n * @type {number}\n */\n this.count = 0;\n }\n}\n\nclass Profiler {\n /**\n * @param {FrameCallback} onFrame a handle called for each recorded frame.\n * The passed frame value may not be stored as it'll be updated with later\n * frame information. Any information that is further stored by the handler\n * should make copies or reduce the information.\n */\n constructor (onFrame = function () {}) {\n /**\n * A series of START and STOP values followed by arguments. After\n * recording is complete the full set of records is reported back by\n * stepping through the series to connect the relative START and STOP\n * information.\n * @type {Array.<*>}\n */\n this.records = [];\n\n /**\n * An array of frames incremented on demand instead as part of start\n * and stop.\n * @type {Array.}\n */\n this.increments = [];\n\n /**\n * An array of profiler frames separated by counter argument. Generally\n * for Scratch these frames are separated by block function opcode.\n * This tracks each time an opcode is called.\n * @type {Array.}\n */\n this.counters = [];\n\n /**\n * A frame with no id or argument.\n * @type {ProfilerFrame}\n */\n this.nullFrame = new ProfilerFrame(-1);\n\n /**\n * A cache of ProfilerFrames to reuse when reporting the recorded\n * frames in records.\n * @type {Array.}\n */\n this._stack = [new ProfilerFrame(0)];\n\n /**\n * A callback handle called with each decoded frame when reporting back\n * all the recorded times.\n * @type {FrameCallback}\n */\n this.onFrame = onFrame;\n\n /**\n * A reference to the START record id constant.\n * @const {number}\n */\n this.START = START;\n\n /**\n * A reference to the STOP record id constant.\n * @const {number}\n */\n this.STOP = STOP;\n }\n\n /**\n * Start recording a frame of time for an id and optional argument.\n * @param {number} id The id returned by idByName for a name symbol like\n * Runtime._step.\n * @param {?*} arg An arbitrary argument value to store with the frame.\n */\n start (id, arg) {\n this.records.push(START, id, arg, performance.now());\n }\n\n /**\n * Stop the current frame.\n */\n stop () {\n this.records.push(STOP, performance.now());\n }\n\n /**\n * Increment the number of times this symbol is called.\n * @param {number} id The id returned by idByName for a name symbol.\n */\n increment (id) {\n if (!this.increments[id]) {\n this.increments[id] = new ProfilerFrame(-1);\n this.increments[id].id = id;\n }\n this.increments[id].count += 1;\n }\n\n /**\n * Find or create a ProfilerFrame-like object whose counter can be\n * incremented outside of the Profiler.\n * @param {number} id The id returned by idByName for a name symbol.\n * @param {*} arg The argument for a frame that identifies it in addition\n * to the id.\n * @return {{count: number}} A ProfilerFrame-like whose count should be\n * incremented for each call.\n */\n frame (id, arg) {\n for (let i = 0; i < this.counters.length; i++) {\n if (this.counters[i].id === id && this.counters[i].arg === arg) {\n return this.counters[i];\n }\n }\n\n const newCounter = new ProfilerFrame(-1);\n newCounter.id = id;\n newCounter.arg = arg;\n this.counters.push(newCounter);\n return newCounter;\n }\n\n /**\n * Decode records and report all frames to `this.onFrame`.\n */\n reportFrames () {\n const stack = this._stack;\n let depth = 1;\n\n // Step through the records and initialize Frame instances from the\n // START and STOP events. START and STOP events are separated by events\n // for deeper frames run by higher frames. Frames are stored on a stack\n // and reinitialized for each START event. When a stop event is reach\n // the Frame for the current depth has its final values stored and its\n // passed to the current onFrame callback. This way Frames are \"pushed\"\n // for each START event and \"popped\" for each STOP and handed to an\n // outside handle to any desired reduction of the collected data.\n for (let i = 0; i < this.records.length;) {\n if (this.records[i] === START) {\n if (depth >= stack.length) {\n stack.push(new ProfilerFrame(depth));\n }\n\n // Store id, arg, totalTime, and initialize selfTime.\n const frame = stack[depth++];\n frame.id = this.records[i + 1];\n frame.arg = this.records[i + 2];\n // totalTime is first set as the time recorded by this START\n // event. Once the STOP event is reached the stored start time\n // is subtracted from the recorded stop time. The resulting\n // difference is the actual totalTime, and replaces the start\n // time in frame.totalTime.\n //\n // totalTime is used this way as a convenient member to store a\n // value between the two events without needing additional\n // members on the Frame or in a shadow map.\n frame.totalTime = this.records[i + 3];\n // selfTime is decremented until we reach the STOP event for\n // this frame. totalTime will be added to it then to get the\n // time difference.\n frame.selfTime = 0;\n\n i += START_SIZE;\n } else if (this.records[i] === STOP) {\n const now = this.records[i + 1];\n\n const frame = stack[--depth];\n // totalTime is the difference between the start event time\n // stored in totalTime and the stop event time pulled from this\n // record.\n frame.totalTime = now - frame.totalTime;\n // selfTime is the difference of this frame's totalTime and the\n // sum of totalTime of deeper frames.\n frame.selfTime += frame.totalTime;\n\n // Remove this frames totalTime from the parent's selfTime.\n stack[depth - 1].selfTime -= frame.totalTime;\n\n // This frame occured once.\n frame.count = 1;\n\n this.onFrame(frame);\n\n i += STOP_SIZE;\n } else {\n this.records.length = 0;\n throw new Error('Unable to decode Profiler records.');\n }\n }\n\n for (let j = 0; j < this.increments.length; j++) {\n if (this.increments[j] && this.increments[j].count > 0) {\n this.onFrame(this.increments[j]);\n this.increments[j].count = 0;\n }\n }\n\n for (let k = 0; k < this.counters.length; k++) {\n if (this.counters[k].count > 0) {\n this.onFrame(this.counters[k]);\n this.counters[k].count = 0;\n }\n }\n\n this.records.length = 0;\n }\n\n /**\n * Lookup or create an id for a frame name.\n * @param {string} name The name to return an id for.\n * @return {number} The id for the passed name.\n */\n idByName (name) {\n return Profiler.idByName(name);\n }\n\n /**\n * Reverse lookup the name from a given frame id.\n * @param {number} id The id to search for.\n * @return {string} The name for the given id.\n */\n nameById (id) {\n return Profiler.nameById(id);\n }\n\n /**\n * Lookup or create an id for a frame name.\n * @static\n * @param {string} name The name to return an id for.\n * @return {number} The id for the passed name.\n */\n static idByName (name) {\n if (typeof profilerNames[name] !== 'number') {\n profilerNames[name] = nextId++;\n }\n return profilerNames[name];\n }\n\n /**\n * Reverse lookup the name from a given frame id.\n * @static\n * @param {number} id The id to search for.\n * @return {string} The name for the given id.\n */\n static nameById (id) {\n for (const name in profilerNames) {\n if (profilerNames[name] === id) {\n return name;\n }\n }\n return null;\n }\n\n /**\n * Profiler is only available on platforms with the Performance API.\n * @return {boolean} Can the Profiler run in this browser?\n */\n static available () {\n return (\n typeof window === 'object' &&\n typeof window.performance !== 'undefined');\n }\n}\n\n\n/**\n * A reference to the START record id constant.\n * @const {number}\n */\nProfiler.START = START;\n\n/**\n * A reference to the STOP record id constant.\n * @const {number}\n */\nProfiler.STOP = STOP;\n\nmodule.exports = Profiler;\n","const EventEmitter = require('events');\nconst {OrderedMap} = require('immutable');\nconst uuid = require('uuid');\n\nconst ArgumentType = require('../extension-support/argument-type');\nconst Blocks = require('./blocks');\nconst BlocksRuntimeCache = require('./blocks-runtime-cache');\nconst BlockType = require('../extension-support/block-type');\nconst Profiler = require('./profiler');\nconst Sequencer = require('./sequencer');\nconst execute = require('./execute.js');\nconst ScratchBlocksConstants = require('./scratch-blocks-constants');\nconst TargetType = require('../extension-support/target-type');\nconst Thread = require('./thread');\nconst log = require('../util/log');\nconst maybeFormatMessage = require('../util/maybe-format-message');\nconst StageLayering = require('./stage-layering');\nconst Variable = require('./variable');\nconst xmlEscape = require('../util/xml-escape');\nconst ScratchLinkWebSocket = require('../util/scratch-link-websocket');\nconst fetchWithTimeout = require('../util/fetch-with-timeout');\n\n// Virtual I/O devices.\nconst Clock = require('../io/clock');\nconst Cloud = require('../io/cloud');\nconst Keyboard = require('../io/keyboard');\nconst Mouse = require('../io/mouse');\nconst MouseWheel = require('../io/mouseWheel');\nconst UserData = require('../io/userData');\nconst Video = require('../io/video');\n\nconst StringUtil = require('../util/string-util');\nconst uid = require('../util/uid');\n\nconst defaultBlockPackages = {\n scratch3_control: require('../blocks/scratch3_control'),\n scratch3_event: require('../blocks/scratch3_event'),\n scratch3_looks: require('../blocks/scratch3_looks'),\n scratch3_motion: require('../blocks/scratch3_motion'),\n scratch3_operators: require('../blocks/scratch3_operators'),\n scratch3_sound: require('../blocks/scratch3_sound'),\n scratch3_sensing: require('../blocks/scratch3_sensing'),\n scratch3_data: require('../blocks/scratch3_data'),\n scratch3_procedures: require('../blocks/scratch3_procedures')\n};\n\nconst defaultExtensionColors = ['#0FBD8C', '#0DA57A', '#0B8E69'];\n\n/**\n * Information used for converting Scratch argument types into scratch-blocks data.\n * @type {object.}\n */\nconst ArgumentTypeMap = (() => {\n const map = {};\n map[ArgumentType.ANGLE] = {\n shadow: {\n type: 'math_angle',\n // We specify fieldNames here so that we can pick\n // create and populate a field with the defaultValue\n // specified in the extension.\n // When the `fieldName` property is not specified,\n // the will be left out of the XML and\n // the scratch-blocks defaults for that field will be\n // used instead (e.g. default of 0 for number fields)\n fieldName: 'NUM'\n }\n };\n map[ArgumentType.COLOR] = {\n shadow: {\n type: 'colour_picker',\n fieldName: 'COLOUR'\n }\n };\n map[ArgumentType.NUMBER] = {\n shadow: {\n type: 'math_number',\n fieldName: 'NUM'\n }\n };\n map[ArgumentType.STRING] = {\n shadow: {\n type: 'text',\n fieldName: 'TEXT'\n }\n };\n map[ArgumentType.BOOLEAN] = {\n check: 'Boolean'\n };\n map[ArgumentType.MATRIX] = {\n shadow: {\n type: 'matrix',\n fieldName: 'MATRIX'\n }\n };\n map[ArgumentType.NOTE] = {\n shadow: {\n type: 'note',\n fieldName: 'NOTE'\n }\n };\n map[ArgumentType.IMAGE] = {\n // Inline images are weird because they're not actually \"arguments\".\n // They are more analagous to the label on a block.\n fieldType: 'field_image'\n };\n return map;\n})();\n\n/**\n * A pair of functions used to manage the cloud variable limit,\n * to be used when adding (or attempting to add) or removing a cloud variable.\n * @typedef {object} CloudDataManager\n * @property {function} canAddCloudVariable A function to call to check that\n * a cloud variable can be added.\n * @property {function} addCloudVariable A function to call to track a new\n * cloud variable on the runtime.\n * @property {function} removeCloudVariable A function to call when\n * removing an existing cloud variable.\n * @property {function} hasCloudVariables A function to call to check that\n * the runtime has any cloud variables.\n */\n\n/**\n * Creates and manages cloud variable limit in a project,\n * and returns two functions to be used to add a new\n * cloud variable (while checking that it can be added)\n * and remove an existing cloud variable.\n * These are to be called whenever attempting to create or delete\n * a cloud variable.\n * @return {CloudDataManager} The functions to be used when adding or removing a\n * cloud variable.\n */\nconst cloudDataManager = () => {\n const limit = 10;\n let count = 0;\n\n const canAddCloudVariable = () => count < limit;\n\n const addCloudVariable = () => {\n count++;\n };\n\n const removeCloudVariable = () => {\n count--;\n };\n\n const hasCloudVariables = () => count > 0;\n\n return {\n canAddCloudVariable,\n addCloudVariable,\n removeCloudVariable,\n hasCloudVariables\n };\n};\n\n/**\n * Numeric ID for Runtime._step in Profiler instances.\n * @type {number}\n */\nlet stepProfilerId = -1;\n\n/**\n * Numeric ID for Sequencer.stepThreads in Profiler instances.\n * @type {number}\n */\nlet stepThreadsProfilerId = -1;\n\n/**\n * Numeric ID for RenderWebGL.draw in Profiler instances.\n * @type {number}\n */\nlet rendererDrawProfilerId = -1;\n\n/**\n * Manages targets, scripts, and the sequencer.\n * @constructor\n */\nclass Runtime extends EventEmitter {\n constructor () {\n super();\n\n /**\n * Target management and storage.\n * @type {Array.}\n */\n this.targets = [];\n\n /**\n * Targets in reverse order of execution. Shares its order with drawables.\n * @type {Array.}\n */\n this.executableTargets = [];\n\n /**\n * A list of threads that are currently running in the VM.\n * Threads are added when execution starts and pruned when execution ends.\n * @type {Array.}\n */\n this.threads = [];\n\n /** @type {!Sequencer} */\n this.sequencer = new Sequencer(this);\n\n /**\n * Storage container for flyout blocks.\n * These will execute on `_editingTarget.`\n * @type {!Blocks}\n */\n this.flyoutBlocks = new Blocks(this, true /* force no glow */);\n\n /**\n * Storage container for monitor blocks.\n * These will execute on a target maybe\n * @type {!Blocks}\n */\n this.monitorBlocks = new Blocks(this, true /* force no glow */);\n\n /**\n * Currently known editing target for the VM.\n * @type {?Target}\n */\n this._editingTarget = null;\n\n /**\n * Map to look up a block primitive's implementation function by its opcode.\n * This is a two-step lookup: package name first, then primitive name.\n * @type {Object.}\n */\n this._primitives = {};\n\n /**\n * Map to look up all block information by extended opcode.\n * @type {Array.}\n * @private\n */\n this._blockInfo = [];\n\n /**\n * Map to look up hat blocks' metadata.\n * Keys are opcode for hat, values are metadata objects.\n * @type {Object.}\n */\n this._hats = {};\n\n /**\n * A list of script block IDs that were glowing during the previous frame.\n * @type {!Array.}\n */\n this._scriptGlowsPreviousFrame = [];\n\n /**\n * Number of non-monitor threads running during the previous frame.\n * @type {number}\n */\n this._nonMonitorThreadCount = 0;\n\n /**\n * All threads that finished running and were removed from this.threads\n * by behaviour in Sequencer.stepThreads.\n * @type {Array}\n */\n this._lastStepDoneThreads = null;\n\n /**\n * Currently known number of clones, used to enforce clone limit.\n * @type {number}\n */\n this._cloneCounter = 0;\n\n /**\n * Flag to emit a targets update at the end of a step. When target data\n * changes, this flag is set to true.\n * @type {boolean}\n */\n this._refreshTargets = false;\n\n /**\n * Map to look up all monitor block information by opcode.\n * @type {object}\n * @private\n */\n this.monitorBlockInfo = {};\n\n /**\n * Ordered map of all monitors, which are MonitorReporter objects.\n */\n this._monitorState = OrderedMap({});\n\n /**\n * Monitor state from last tick\n */\n this._prevMonitorState = OrderedMap({});\n\n /**\n * Whether the project is in \"turbo mode.\"\n * @type {Boolean}\n */\n this.turboMode = false;\n\n /**\n * Whether the project is in \"compatibility mode\" (30 TPS).\n * @type {Boolean}\n */\n this.compatibilityMode = false;\n\n /**\n * A reference to the current runtime stepping interval, set\n * by a `setInterval`.\n * @type {!number}\n */\n this._steppingInterval = null;\n\n /**\n * Current length of a step.\n * Changes as mode switches, and used by the sequencer to calculate\n * WORK_TIME.\n * @type {!number}\n */\n this.currentStepTime = null;\n\n // Set an intial value for this.currentMSecs\n this.updateCurrentMSecs();\n\n /**\n * Whether any primitive has requested a redraw.\n * Affects whether `Sequencer.stepThreads` will yield\n * after stepping each thread.\n * Reset on every frame.\n * @type {boolean}\n */\n this.redrawRequested = false;\n\n // Register all given block packages.\n this._registerBlockPackages();\n\n // Register and initialize \"IO devices\", containers for processing\n // I/O related data.\n /** @type {Object.} */\n this.ioDevices = {\n clock: new Clock(this),\n cloud: new Cloud(this),\n keyboard: new Keyboard(this),\n mouse: new Mouse(this),\n mouseWheel: new MouseWheel(this),\n userData: new UserData(),\n video: new Video(this)\n };\n\n /**\n * A list of extensions, used to manage hardware connection.\n */\n this.peripheralExtensions = {};\n\n /**\n * A runtime profiler that records timed events for later playback to\n * diagnose Scratch performance.\n * @type {Profiler}\n */\n this.profiler = null;\n\n const newCloudDataManager = cloudDataManager();\n\n /**\n * Check wether the runtime has any cloud data.\n * @type {function}\n * @return {boolean} Whether or not the runtime currently has any\n * cloud variables.\n */\n this.hasCloudData = newCloudDataManager.hasCloudVariables;\n\n /**\n * A function which checks whether a new cloud variable can be added\n * to the runtime.\n * @type {function}\n * @return {boolean} Whether or not a new cloud variable can be added\n * to the runtime.\n */\n this.canAddCloudVariable = newCloudDataManager.canAddCloudVariable;\n\n /**\n * A function that tracks a new cloud variable in the runtime,\n * updating the cloud variable limit. Calling this function will\n * emit a cloud data update event if this is the first cloud variable\n * being added.\n * @type {function}\n */\n this.addCloudVariable = this._initializeAddCloudVariable(newCloudDataManager);\n\n /**\n * A function which updates the runtime's cloud variable limit\n * when removing a cloud variable and emits a cloud update event\n * if the last of the cloud variables is being removed.\n * @type {function}\n */\n this.removeCloudVariable = this._initializeRemoveCloudVariable(newCloudDataManager);\n\n /**\n * A string representing the origin of the current project from outside of the\n * Scratch community, such as CSFirst.\n * @type {?string}\n */\n this.origin = null;\n\n this._initScratchLink();\n\n this.resetRunId();\n }\n\n /**\n * Width of the stage, in pixels.\n * @const {number}\n */\n static get STAGE_WIDTH () {\n return 480;\n }\n\n /**\n * Height of the stage, in pixels.\n * @const {number}\n */\n static get STAGE_HEIGHT () {\n return 360;\n }\n\n /**\n * Event name for glowing a script.\n * @const {string}\n */\n static get SCRIPT_GLOW_ON () {\n return 'SCRIPT_GLOW_ON';\n }\n\n /**\n * Event name for unglowing a script.\n * @const {string}\n */\n static get SCRIPT_GLOW_OFF () {\n return 'SCRIPT_GLOW_OFF';\n }\n\n /**\n * Event name for glowing a block.\n * @const {string}\n */\n static get BLOCK_GLOW_ON () {\n return 'BLOCK_GLOW_ON';\n }\n\n /**\n * Event name for unglowing a block.\n * @const {string}\n */\n static get BLOCK_GLOW_OFF () {\n return 'BLOCK_GLOW_OFF';\n }\n\n /**\n * Event name for a cloud data update\n * to this project.\n * @const {string}\n */\n static get HAS_CLOUD_DATA_UPDATE () {\n return 'HAS_CLOUD_DATA_UPDATE';\n }\n\n /**\n * Event name for turning on turbo mode.\n * @const {string}\n */\n static get TURBO_MODE_ON () {\n return 'TURBO_MODE_ON';\n }\n\n /**\n * Event name for turning off turbo mode.\n * @const {string}\n */\n static get TURBO_MODE_OFF () {\n return 'TURBO_MODE_OFF';\n }\n\n /**\n * Event name when the project is started (threads may not necessarily be\n * running).\n * @const {string}\n */\n static get PROJECT_START () {\n return 'PROJECT_START';\n }\n\n /**\n * Event name when threads start running.\n * Used by the UI to indicate running status.\n * @const {string}\n */\n static get PROJECT_RUN_START () {\n return 'PROJECT_RUN_START';\n }\n\n /**\n * Event name when threads stop running\n * Used by the UI to indicate not-running status.\n * @const {string}\n */\n static get PROJECT_RUN_STOP () {\n return 'PROJECT_RUN_STOP';\n }\n\n /**\n * Event name for project being stopped or restarted by the user.\n * Used by blocks that need to reset state.\n * @const {string}\n */\n static get PROJECT_STOP_ALL () {\n return 'PROJECT_STOP_ALL';\n }\n\n /**\n * Event name for target being stopped by a stop for target call.\n * Used by blocks that need to stop individual targets.\n * @const {string}\n */\n static get STOP_FOR_TARGET () {\n return 'STOP_FOR_TARGET';\n }\n\n /**\n * Event name for visual value report.\n * @const {string}\n */\n static get VISUAL_REPORT () {\n return 'VISUAL_REPORT';\n }\n\n /**\n * Event name for project loaded report.\n * @const {string}\n */\n static get PROJECT_LOADED () {\n return 'PROJECT_LOADED';\n }\n\n /**\n * Event name for report that a change was made that can be saved\n * @const {string}\n */\n static get PROJECT_CHANGED () {\n return 'PROJECT_CHANGED';\n }\n\n /**\n * Event name for report that a change was made to an extension in the toolbox.\n * @const {string}\n */\n static get TOOLBOX_EXTENSIONS_NEED_UPDATE () {\n return 'TOOLBOX_EXTENSIONS_NEED_UPDATE';\n }\n\n /**\n * Event name for targets update report.\n * @const {string}\n */\n static get TARGETS_UPDATE () {\n return 'TARGETS_UPDATE';\n }\n\n /**\n * Event name for monitors update.\n * @const {string}\n */\n static get MONITORS_UPDATE () {\n return 'MONITORS_UPDATE';\n }\n\n /**\n * Event name for block drag update.\n * @const {string}\n */\n static get BLOCK_DRAG_UPDATE () {\n return 'BLOCK_DRAG_UPDATE';\n }\n\n /**\n * Event name for block drag end.\n * @const {string}\n */\n static get BLOCK_DRAG_END () {\n return 'BLOCK_DRAG_END';\n }\n\n /**\n * Event name for reporting that an extension was added.\n * @const {string}\n */\n static get EXTENSION_ADDED () {\n return 'EXTENSION_ADDED';\n }\n\n /**\n * Event name for reporting that an extension as asked for a custom field to be added\n * @const {string}\n */\n static get EXTENSION_FIELD_ADDED () {\n return 'EXTENSION_FIELD_ADDED';\n }\n\n /**\n * Event name for updating the available set of peripheral devices.\n * This causes the peripheral connection modal to update a list of\n * available peripherals.\n * @const {string}\n */\n static get PERIPHERAL_LIST_UPDATE () {\n return 'PERIPHERAL_LIST_UPDATE';\n }\n\n /**\n * Event name for when the user picks a bluetooth device to connect to\n * via Companion Device Manager (CDM)\n * @const {string}\n */\n static get USER_PICKED_PERIPHERAL () {\n return 'USER_PICKED_PERIPHERAL';\n }\n\n /**\n * Event name for reporting that a peripheral has connected.\n * This causes the status button in the blocks menu to indicate 'connected'.\n * @const {string}\n */\n static get PERIPHERAL_CONNECTED () {\n return 'PERIPHERAL_CONNECTED';\n }\n\n /**\n * Event name for reporting that a peripheral has been intentionally disconnected.\n * This causes the status button in the blocks menu to indicate 'disconnected'.\n * @const {string}\n */\n static get PERIPHERAL_DISCONNECTED () {\n return 'PERIPHERAL_DISCONNECTED';\n }\n\n /**\n * Event name for reporting that a peripheral has encountered a request error.\n * This causes the peripheral connection modal to switch to an error state.\n * @const {string}\n */\n static get PERIPHERAL_REQUEST_ERROR () {\n return 'PERIPHERAL_REQUEST_ERROR';\n }\n\n /**\n * Event name for reporting that a peripheral connection has been lost.\n * This causes a 'peripheral connection lost' error alert to display.\n * @const {string}\n */\n static get PERIPHERAL_CONNECTION_LOST_ERROR () {\n return 'PERIPHERAL_CONNECTION_LOST_ERROR';\n }\n\n /**\n * Event name for reporting that a peripheral has not been discovered.\n * This causes the peripheral connection modal to show a timeout state.\n * @const {string}\n */\n static get PERIPHERAL_SCAN_TIMEOUT () {\n return 'PERIPHERAL_SCAN_TIMEOUT';\n }\n\n /**\n * Event name to indicate that the microphone is being used to stream audio.\n * @const {string}\n */\n static get MIC_LISTENING () {\n return 'MIC_LISTENING';\n }\n\n /**\n * Event name for reporting that blocksInfo was updated.\n * @const {string}\n */\n static get BLOCKSINFO_UPDATE () {\n return 'BLOCKSINFO_UPDATE';\n }\n\n /**\n * Event name when the runtime tick loop has been started.\n * @const {string}\n */\n static get RUNTIME_STARTED () {\n return 'RUNTIME_STARTED';\n }\n\n /**\n * Event name when the runtime dispose has been called.\n * @const {string}\n */\n static get RUNTIME_DISPOSED () {\n return 'RUNTIME_DISPOSED';\n }\n\n /**\n * Event name for reporting that a block was updated and needs to be rerendered.\n * @const {string}\n */\n static get BLOCKS_NEED_UPDATE () {\n return 'BLOCKS_NEED_UPDATE';\n }\n\n /**\n * How rapidly we try to step threads by default, in ms.\n */\n static get THREAD_STEP_INTERVAL () {\n return 1000 / 60;\n }\n\n /**\n * In compatibility mode, how rapidly we try to step threads, in ms.\n */\n static get THREAD_STEP_INTERVAL_COMPATIBILITY () {\n return 1000 / 30;\n }\n\n /**\n * How many clones can be created at a time.\n * @const {number}\n */\n static get MAX_CLONES () {\n return 300;\n }\n\n // -----------------------------------------------------------------------------\n // -----------------------------------------------------------------------------\n\n // Helper function for initializing the addCloudVariable function\n _initializeAddCloudVariable (newCloudDataManager) {\n // The addCloudVariable function\n return (() => {\n const hadCloudVarsBefore = this.hasCloudData();\n newCloudDataManager.addCloudVariable();\n if (!hadCloudVarsBefore && this.hasCloudData()) {\n this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, true);\n }\n });\n }\n\n // Helper function for initializing the removeCloudVariable function\n _initializeRemoveCloudVariable (newCloudDataManager) {\n return (() => {\n const hadCloudVarsBefore = this.hasCloudData();\n newCloudDataManager.removeCloudVariable();\n if (hadCloudVarsBefore && !this.hasCloudData()) {\n this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, false);\n }\n });\n }\n\n /**\n * Register default block packages with this runtime.\n * @todo Prefix opcodes with package name.\n * @private\n */\n _registerBlockPackages () {\n for (const packageName in defaultBlockPackages) {\n if (Object.prototype.hasOwnProperty.call(defaultBlockPackages, packageName)) {\n // @todo pass a different runtime depending on package privilege?\n const packageObject = new (defaultBlockPackages[packageName])(this);\n // Collect primitives from package.\n if (packageObject.getPrimitives) {\n const packagePrimitives = packageObject.getPrimitives();\n for (const op in packagePrimitives) {\n if (Object.prototype.hasOwnProperty.call(packagePrimitives, op)) {\n this._primitives[op] =\n packagePrimitives[op].bind(packageObject);\n }\n }\n }\n // Collect hat metadata from package.\n if (packageObject.getHats) {\n const packageHats = packageObject.getHats();\n for (const hatName in packageHats) {\n if (Object.prototype.hasOwnProperty.call(packageHats, hatName)) {\n this._hats[hatName] = packageHats[hatName];\n }\n }\n }\n // Collect monitored from package.\n if (packageObject.getMonitored) {\n this.monitorBlockInfo = Object.assign({}, this.monitorBlockInfo, packageObject.getMonitored());\n }\n }\n }\n }\n\n getMonitorState () {\n return this._monitorState;\n }\n\n /**\n * Generate an extension-specific menu ID.\n * @param {string} menuName - the name of the menu.\n * @param {string} extensionId - the ID of the extension hosting the menu.\n * @returns {string} - the constructed ID.\n * @private\n */\n _makeExtensionMenuId (menuName, extensionId) {\n return `${extensionId}_menu_${xmlEscape(menuName)}`;\n }\n\n /**\n * Create a context (\"args\") object for use with `formatMessage` on messages which might be target-specific.\n * @param {Target} [target] - the target to use as context. If a target is not provided, default to the current\n * editing target or the stage.\n */\n makeMessageContextForTarget (target) {\n const context = {};\n target = target || this.getEditingTarget() || this.getTargetForStage();\n if (target) {\n context.targetType = (target.isStage ? TargetType.STAGE : TargetType.SPRITE);\n }\n }\n\n /**\n * Register the primitives provided by an extension.\n * @param {ExtensionMetadata} extensionInfo - information about the extension (id, blocks, etc.)\n * @private\n */\n _registerExtensionPrimitives (extensionInfo) {\n const categoryInfo = {\n id: extensionInfo.id,\n name: maybeFormatMessage(extensionInfo.name),\n showStatusButton: extensionInfo.showStatusButton,\n blockIconURI: extensionInfo.blockIconURI,\n menuIconURI: extensionInfo.menuIconURI\n };\n\n if (extensionInfo.color1) {\n categoryInfo.color1 = extensionInfo.color1;\n categoryInfo.color2 = extensionInfo.color2;\n categoryInfo.color3 = extensionInfo.color3;\n } else {\n categoryInfo.color1 = defaultExtensionColors[0];\n categoryInfo.color2 = defaultExtensionColors[1];\n categoryInfo.color3 = defaultExtensionColors[2];\n }\n\n this._blockInfo.push(categoryInfo);\n\n this._fillExtensionCategory(categoryInfo, extensionInfo);\n\n for (const fieldTypeName in categoryInfo.customFieldTypes) {\n if (Object.prototype.hasOwnProperty.call(extensionInfo.customFieldTypes, fieldTypeName)) {\n const fieldTypeInfo = categoryInfo.customFieldTypes[fieldTypeName];\n\n // Emit events for custom field types from extension\n this.emit(Runtime.EXTENSION_FIELD_ADDED, {\n name: `field_${fieldTypeInfo.extendedName}`,\n implementation: fieldTypeInfo.fieldImplementation\n });\n }\n }\n\n this.emit(Runtime.EXTENSION_ADDED, categoryInfo);\n }\n\n /**\n * Reregister the primitives for an extension\n * @param {ExtensionMetadata} extensionInfo - new info (results of running getInfo) for an extension\n * @private\n */\n _refreshExtensionPrimitives (extensionInfo) {\n const categoryInfo = this._blockInfo.find(info => info.id === extensionInfo.id);\n if (categoryInfo) {\n categoryInfo.name = maybeFormatMessage(extensionInfo.name);\n this._fillExtensionCategory(categoryInfo, extensionInfo);\n\n this.emit(Runtime.BLOCKSINFO_UPDATE, categoryInfo);\n }\n }\n\n /**\n * Read extension information, convert menus, blocks and custom field types\n * and store the results in the provided category object.\n * @param {CategoryInfo} categoryInfo - the category to be filled\n * @param {ExtensionMetadata} extensionInfo - the extension metadata to read\n * @private\n */\n _fillExtensionCategory (categoryInfo, extensionInfo) {\n categoryInfo.blocks = [];\n categoryInfo.customFieldTypes = {};\n categoryInfo.menus = [];\n categoryInfo.menuInfo = {};\n\n for (const menuName in extensionInfo.menus) {\n if (Object.prototype.hasOwnProperty.call(extensionInfo.menus, menuName)) {\n const menuInfo = extensionInfo.menus[menuName];\n const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuInfo, categoryInfo);\n categoryInfo.menus.push(convertedMenu);\n categoryInfo.menuInfo[menuName] = menuInfo;\n }\n }\n for (const fieldTypeName in extensionInfo.customFieldTypes) {\n if (Object.prototype.hasOwnProperty.call(extensionInfo.customFieldTypes, fieldTypeName)) {\n const fieldType = extensionInfo.customFieldTypes[fieldTypeName];\n const fieldTypeInfo = this._buildCustomFieldInfo(\n fieldTypeName,\n fieldType,\n extensionInfo.id,\n categoryInfo\n );\n\n categoryInfo.customFieldTypes[fieldTypeName] = fieldTypeInfo;\n }\n }\n\n for (const blockInfo of extensionInfo.blocks) {\n try {\n const convertedBlock = this._convertForScratchBlocks(blockInfo, categoryInfo);\n categoryInfo.blocks.push(convertedBlock);\n if (convertedBlock.json) {\n const opcode = convertedBlock.json.type;\n if (blockInfo.blockType !== BlockType.EVENT) {\n this._primitives[opcode] = convertedBlock.info.func;\n }\n if (blockInfo.blockType === BlockType.EVENT || blockInfo.blockType === BlockType.HAT) {\n this._hats[opcode] = {\n edgeActivated: blockInfo.isEdgeActivated,\n restartExistingThreads: blockInfo.shouldRestartExistingThreads\n };\n }\n }\n } catch (e) {\n log.error('Error parsing block: ', {block: blockInfo, error: e});\n }\n }\n }\n\n /**\n * Convert the given extension menu items into the scratch-blocks style of list of pairs.\n * If the menu is dynamic (e.g. the passed in argument is a function), return the input unmodified.\n * @param {object} menuItems - an array of menu items or a function to retrieve such an array\n * @returns {object} - an array of 2 element arrays or the original input function\n * @private\n */\n _convertMenuItems (menuItems) {\n if (typeof menuItems !== 'function') {\n const extensionMessageContext = this.makeMessageContextForTarget();\n return menuItems.map(item => {\n const formattedItem = maybeFormatMessage(item, extensionMessageContext);\n switch (typeof formattedItem) {\n case 'string':\n return [formattedItem, formattedItem];\n case 'object':\n return [maybeFormatMessage(item.text, extensionMessageContext), item.value];\n default:\n throw new Error(`Can't interpret menu item: ${JSON.stringify(item)}`);\n }\n });\n }\n return menuItems;\n }\n\n /**\n * Build the scratch-blocks JSON for a menu. Note that scratch-blocks treats menus as a special kind of block.\n * @param {string} menuName - the name of the menu\n * @param {object} menuInfo - a description of this menu and its items\n * @property {*} items - an array of menu items or a function to retrieve such an array\n * @property {boolean} [acceptReporters] - if true, allow dropping reporters onto this menu\n * @param {CategoryInfo} categoryInfo - the category for this block\n * @returns {object} - a JSON-esque object ready for scratch-blocks' consumption\n * @private\n */\n _buildMenuForScratchBlocks (menuName, menuInfo, categoryInfo) {\n const menuId = this._makeExtensionMenuId(menuName, categoryInfo.id);\n const menuItems = this._convertMenuItems(menuInfo.items);\n return {\n json: {\n message0: '%1',\n type: menuId,\n inputsInline: true,\n output: 'String',\n colour: categoryInfo.color1,\n colourSecondary: categoryInfo.color2,\n colourTertiary: categoryInfo.color3,\n outputShape: menuInfo.acceptReporters ?\n ScratchBlocksConstants.OUTPUT_SHAPE_ROUND : ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE,\n args0: [\n {\n type: 'field_dropdown',\n name: menuName,\n options: menuItems\n }\n ]\n }\n };\n }\n\n _buildCustomFieldInfo (fieldName, fieldInfo, extensionId, categoryInfo) {\n const extendedName = `${extensionId}_${fieldName}`;\n return {\n fieldName: fieldName,\n extendedName: extendedName,\n argumentTypeInfo: {\n shadow: {\n type: extendedName,\n fieldName: `field_${extendedName}`\n }\n },\n scratchBlocksDefinition: this._buildCustomFieldTypeForScratchBlocks(\n extendedName,\n fieldInfo.output,\n fieldInfo.outputShape,\n categoryInfo\n ),\n fieldImplementation: fieldInfo.implementation\n };\n }\n\n /**\n * Build the scratch-blocks JSON needed for a fieldType.\n * Custom field types need to be namespaced to the extension so that extensions can't interfere with each other\n * @param {string} fieldName - The name of the field\n * @param {string} output - The output of the field\n * @param {number} outputShape - Shape of the field (from ScratchBlocksConstants)\n * @param {object} categoryInfo - The category the field belongs to (Used to set its colors)\n * @returns {object} - Object to be inserted into scratch-blocks\n */\n _buildCustomFieldTypeForScratchBlocks (fieldName, output, outputShape, categoryInfo) {\n return {\n json: {\n type: fieldName,\n message0: '%1',\n inputsInline: true,\n output: output,\n colour: categoryInfo.color1,\n colourSecondary: categoryInfo.color2,\n colourTertiary: categoryInfo.color3,\n outputShape: outputShape,\n args0: [\n {\n name: `field_${fieldName}`,\n type: `field_${fieldName}`\n }\n ]\n }\n };\n }\n\n /**\n * Convert ExtensionBlockMetadata into data ready for scratch-blocks.\n * @param {ExtensionBlockMetadata} blockInfo - the block info to convert\n * @param {CategoryInfo} categoryInfo - the category for this block\n * @returns {ConvertedBlockInfo} - the converted & original block information\n * @private\n */\n _convertForScratchBlocks (blockInfo, categoryInfo) {\n if (blockInfo === '---') {\n return this._convertSeparatorForScratchBlocks(blockInfo);\n }\n\n if (blockInfo.blockType === BlockType.BUTTON) {\n return this._convertButtonForScratchBlocks(blockInfo);\n }\n\n return this._convertBlockForScratchBlocks(blockInfo, categoryInfo);\n }\n\n /**\n * Convert ExtensionBlockMetadata into scratch-blocks JSON & XML, and generate a proxy function.\n * @param {ExtensionBlockMetadata} blockInfo - the block to convert\n * @param {CategoryInfo} categoryInfo - the category for this block\n * @returns {ConvertedBlockInfo} - the converted & original block information\n * @private\n */\n _convertBlockForScratchBlocks (blockInfo, categoryInfo) {\n const extendedOpcode = `${categoryInfo.id}_${blockInfo.opcode}`;\n\n const blockJSON = {\n type: extendedOpcode,\n inputsInline: true,\n category: categoryInfo.name,\n colour: categoryInfo.color1,\n colourSecondary: categoryInfo.color2,\n colourTertiary: categoryInfo.color3\n };\n const context = {\n // TODO: store this somewhere so that we can map args appropriately after translation.\n // This maps an arg name to its relative position in the original (usually English) block text.\n // When displaying a block in another language we'll need to run a `replace` action similar to the one\n // below, but each `[ARG]` will need to be replaced with the number in this map.\n argsMap: {},\n blockJSON,\n categoryInfo,\n blockInfo,\n inputList: []\n };\n\n // If an icon for the extension exists, prepend it to each block, with a vertical separator.\n // We can overspecify an icon for each block, but if no icon exists on a block, fall back to\n // the category block icon.\n const iconURI = blockInfo.blockIconURI || categoryInfo.blockIconURI;\n\n if (iconURI) {\n blockJSON.extensions = ['scratch_extension'];\n blockJSON.message0 = '%1 %2';\n const iconJSON = {\n type: 'field_image',\n src: iconURI,\n width: 40,\n height: 40\n };\n const separatorJSON = {\n type: 'field_vertical_separator'\n };\n blockJSON.args0 = [\n iconJSON,\n separatorJSON\n ];\n }\n\n switch (blockInfo.blockType) {\n case BlockType.COMMAND:\n blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE;\n blockJSON.previousStatement = null; // null = available connection; undefined = hat\n if (!blockInfo.isTerminal) {\n blockJSON.nextStatement = null; // null = available connection; undefined = terminal\n }\n break;\n case BlockType.REPORTER:\n blockJSON.output = 'String'; // TODO: distinguish number & string here?\n blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_ROUND;\n break;\n case BlockType.BOOLEAN:\n blockJSON.output = 'Boolean';\n blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_HEXAGONAL;\n break;\n case BlockType.HAT:\n case BlockType.EVENT:\n if (!Object.prototype.hasOwnProperty.call(blockInfo, 'isEdgeActivated')) {\n // if absent, this property defaults to true\n blockInfo.isEdgeActivated = true;\n }\n blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE;\n blockJSON.nextStatement = null; // null = available connection; undefined = terminal\n break;\n case BlockType.CONDITIONAL:\n case BlockType.LOOP:\n blockInfo.branchCount = blockInfo.branchCount || 1;\n blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE;\n blockJSON.previousStatement = null; // null = available connection; undefined = hat\n if (!blockInfo.isTerminal) {\n blockJSON.nextStatement = null; // null = available connection; undefined = terminal\n }\n break;\n }\n\n const blockText = Array.isArray(blockInfo.text) ? blockInfo.text : [blockInfo.text];\n let inTextNum = 0; // text for the next block \"arm\" is blockText[inTextNum]\n let inBranchNum = 0; // how many branches have we placed into the JSON so far?\n let outLineNum = 0; // used for scratch-blocks `message${outLineNum}` and `args${outLineNum}`\n const convertPlaceholders = this._convertPlaceholders.bind(this, context);\n const extensionMessageContext = this.makeMessageContextForTarget();\n\n // alternate between a block \"arm\" with text on it and an open slot for a substack\n while (inTextNum < blockText.length || inBranchNum < blockInfo.branchCount) {\n if (inTextNum < blockText.length) {\n context.outLineNum = outLineNum;\n const lineText = maybeFormatMessage(blockText[inTextNum], extensionMessageContext);\n const convertedText = lineText.replace(/\\[(.+?)]/g, convertPlaceholders);\n if (blockJSON[`message${outLineNum}`]) {\n blockJSON[`message${outLineNum}`] += convertedText;\n } else {\n blockJSON[`message${outLineNum}`] = convertedText;\n }\n ++inTextNum;\n ++outLineNum;\n }\n if (inBranchNum < blockInfo.branchCount) {\n blockJSON[`message${outLineNum}`] = '%1';\n blockJSON[`args${outLineNum}`] = [{\n type: 'input_statement',\n name: `SUBSTACK${inBranchNum > 0 ? inBranchNum + 1 : ''}`\n }];\n ++inBranchNum;\n ++outLineNum;\n }\n }\n\n if (blockInfo.blockType === BlockType.REPORTER) {\n if (!blockInfo.disableMonitor && context.inputList.length === 0) {\n blockJSON.checkboxInFlyout = true;\n }\n } else if (blockInfo.blockType === BlockType.LOOP) {\n // Add icon to the bottom right of a loop block\n blockJSON[`lastDummyAlign${outLineNum}`] = 'RIGHT';\n blockJSON[`message${outLineNum}`] = '%1';\n blockJSON[`args${outLineNum}`] = [{\n type: 'field_image',\n src: './static/blocks-media/repeat.svg', // TODO: use a constant or make this configurable?\n width: 24,\n height: 24,\n alt: '*', // TODO remove this since we don't use collapsed blocks in scratch\n flip_rtl: true\n }];\n ++outLineNum;\n }\n\n const mutation = blockInfo.isDynamic ? `` : '';\n const inputs = context.inputList.join('');\n const blockXML = `${mutation}${inputs}`;\n\n return {\n info: context.blockInfo,\n json: context.blockJSON,\n xml: blockXML\n };\n }\n\n /**\n * Generate a separator between blocks categories or sub-categories.\n * @param {ExtensionBlockMetadata} blockInfo - the block to convert\n * @param {CategoryInfo} categoryInfo - the category for this block\n * @returns {ConvertedBlockInfo} - the converted & original block information\n * @private\n */\n _convertSeparatorForScratchBlocks (blockInfo) {\n return {\n info: blockInfo,\n xml: ''\n };\n }\n\n /**\n * Convert a button for scratch-blocks. A button has no opcode but specifies a callback name in the `func` field.\n * @param {ExtensionBlockMetadata} buttonInfo - the button to convert\n * @property {string} func - the callback name\n * @param {CategoryInfo} categoryInfo - the category for this button\n * @returns {ConvertedBlockInfo} - the converted & original button information\n * @private\n */\n _convertButtonForScratchBlocks (buttonInfo) {\n // for now we only support these pre-defined callbacks handled in scratch-blocks\n const supportedCallbackKeys = ['MAKE_A_LIST', 'MAKE_A_PROCEDURE', 'MAKE_A_VARIABLE'];\n if (supportedCallbackKeys.indexOf(buttonInfo.func) < 0) {\n log.error(`Custom button callbacks not supported yet: ${buttonInfo.func}`);\n }\n\n const extensionMessageContext = this.makeMessageContextForTarget();\n const buttonText = maybeFormatMessage(buttonInfo.text, extensionMessageContext);\n return {\n info: buttonInfo,\n xml: ``\n };\n }\n\n /**\n * Helper for _convertPlaceholdes which handles inline images which are a specialized case of block \"arguments\".\n * @param {object} argInfo Metadata about the inline image as specified by the extension\n * @return {object} JSON blob for a scratch-blocks image field.\n * @private\n */\n _constructInlineImageJson (argInfo) {\n if (!argInfo.dataURI) {\n log.warn('Missing data URI in extension block with argument type IMAGE');\n }\n return {\n type: 'field_image',\n src: argInfo.dataURI || '',\n // TODO these probably shouldn't be hardcoded...?\n width: 24,\n height: 24,\n // Whether or not the inline image should be flipped horizontally\n // in RTL languages. Defaults to false, indicating that the\n // image will not be flipped.\n flip_rtl: argInfo.flipRTL || false\n };\n }\n\n /**\n * Helper for _convertForScratchBlocks which handles linearization of argument placeholders. Called as a callback\n * from string#replace. In addition to the return value the JSON and XML items in the context will be filled.\n * @param {object} context - information shared with _convertForScratchBlocks about the block, etc.\n * @param {string} match - the overall string matched by the placeholder regex, including brackets: '[FOO]'.\n * @param {string} placeholder - the name of the placeholder being matched: 'FOO'.\n * @return {string} scratch-blocks placeholder for the argument: '%1'.\n * @private\n */\n _convertPlaceholders (context, match, placeholder) {\n // Sanitize the placeholder to ensure valid XML\n placeholder = placeholder.replace(/[<\"&]/, '_');\n\n // Determine whether the argument type is one of the known standard field types\n const argInfo = context.blockInfo.arguments[placeholder] || {};\n let argTypeInfo = ArgumentTypeMap[argInfo.type] || {};\n\n // Field type not a standard field type, see if extension has registered custom field type\n if (!ArgumentTypeMap[argInfo.type] && context.categoryInfo.customFieldTypes[argInfo.type]) {\n argTypeInfo = context.categoryInfo.customFieldTypes[argInfo.type].argumentTypeInfo;\n }\n\n // Start to construct the scratch-blocks style JSON defining how the block should be\n // laid out\n let argJSON;\n\n // Most field types are inputs (slots on the block that can have other blocks plugged into them)\n // check if this is not one of those cases. E.g. an inline image on a block.\n if (argTypeInfo.fieldType === 'field_image') {\n argJSON = this._constructInlineImageJson(argInfo);\n } else {\n // Construct input value\n\n // Layout a block argument (e.g. an input slot on the block)\n argJSON = {\n type: 'input_value',\n name: placeholder\n };\n\n const defaultValue =\n typeof argInfo.defaultValue === 'undefined' ? '' :\n xmlEscape(maybeFormatMessage(argInfo.defaultValue, this.makeMessageContextForTarget()).toString());\n\n if (argTypeInfo.check) {\n // Right now the only type of 'check' we have specifies that the\n // input slot on the block accepts Boolean reporters, so it should be\n // shaped like a hexagon\n argJSON.check = argTypeInfo.check;\n }\n\n let valueName;\n let shadowType;\n let fieldName;\n if (argInfo.menu) {\n const menuInfo = context.categoryInfo.menuInfo[argInfo.menu];\n if (menuInfo.acceptReporters) {\n valueName = placeholder;\n shadowType = this._makeExtensionMenuId(argInfo.menu, context.categoryInfo.id);\n fieldName = argInfo.menu;\n } else {\n argJSON.type = 'field_dropdown';\n argJSON.options = this._convertMenuItems(menuInfo.items);\n valueName = null;\n shadowType = null;\n fieldName = placeholder;\n }\n } else {\n valueName = placeholder;\n shadowType = (argTypeInfo.shadow && argTypeInfo.shadow.type) || null;\n fieldName = (argTypeInfo.shadow && argTypeInfo.shadow.fieldName) || null;\n }\n\n // is the ScratchBlocks name for a block input.\n if (valueName) {\n context.inputList.push(``);\n }\n\n // The is a placeholder for a reporter and is visible when there's no reporter in this input.\n // Boolean inputs don't need to specify a shadow in the XML.\n if (shadowType) {\n context.inputList.push(``);\n }\n\n // A displays a dynamic value: a user-editable text field, a drop-down menu, etc.\n // Leave out the field if defaultValue or fieldName are not specified\n if (defaultValue && fieldName) {\n context.inputList.push(`${defaultValue}`);\n }\n\n if (shadowType) {\n context.inputList.push('');\n }\n\n if (valueName) {\n context.inputList.push('');\n }\n }\n\n const argsName = `args${context.outLineNum}`;\n const blockArgs = (context.blockJSON[argsName] = context.blockJSON[argsName] || []);\n if (argJSON) blockArgs.push(argJSON);\n const argNum = blockArgs.length;\n context.argsMap[placeholder] = argNum;\n\n return `%${argNum}`;\n }\n\n /**\n * @returns {Array.} scratch-blocks XML for each category of extension blocks, in category order.\n * @param {?Target} [target] - the active editing target (optional)\n * @property {string} id - the category / extension ID\n * @property {string} xml - the XML text for this category, starting with `` and ending with ``\n */\n getBlocksXML (target) {\n return this._blockInfo.map(categoryInfo => {\n const {name, color1, color2} = categoryInfo;\n // Filter out blocks that aren't supposed to be shown on this target, as determined by the block info's\n // `hideFromPalette` and `filter` properties.\n const paletteBlocks = categoryInfo.blocks.filter(block => {\n let blockFilterIncludesTarget = true;\n // If an editing target is not passed, include all blocks\n // If the block info doesn't include a `filter` property, always include it\n if (target && block.info.filter) {\n blockFilterIncludesTarget = block.info.filter.includes(\n target.isStage ? TargetType.STAGE : TargetType.SPRITE\n );\n }\n // If the block info's `hideFromPalette` is true, then filter out this block\n return blockFilterIncludesTarget && !block.info.hideFromPalette;\n });\n\n const colorXML = `colour=\"${color1}\" secondaryColour=\"${color2}\"`;\n\n // Use a menu icon if there is one. Otherwise, use the block icon. If there's no icon,\n // the category menu will show its default colored circle.\n let menuIconURI = '';\n if (categoryInfo.menuIconURI) {\n menuIconURI = categoryInfo.menuIconURI;\n } else if (categoryInfo.blockIconURI) {\n menuIconURI = categoryInfo.blockIconURI;\n }\n const menuIconXML = menuIconURI ?\n `iconURI=\"${menuIconURI}\"` : '';\n\n let statusButtonXML = '';\n if (categoryInfo.showStatusButton) {\n statusButtonXML = 'showStatusButton=\"true\"';\n }\n\n return {\n id: categoryInfo.id,\n xml: `${\n paletteBlocks.map(block => block.xml).join('')}`\n };\n });\n }\n\n /**\n * @returns {Array.} - an array containing the scratch-blocks JSON information for each dynamic block.\n */\n getBlocksJSON () {\n return this._blockInfo.reduce(\n (result, categoryInfo) => result.concat(categoryInfo.blocks.map(blockInfo => blockInfo.json)), []);\n }\n\n /**\n * One-time initialization for Scratch Link support.\n */\n _initScratchLink () {\n // Check that we're actually in a real browser, not Node.js or JSDOM, and we have a valid-looking origin.\n // note that `if (self?....)` will throw if `self` is undefined, so check for that first!\n if (typeof self !== 'undefined' &&\n typeof document !== 'undefined' &&\n document.getElementById &&\n self.origin &&\n self.origin !== 'null' && // note this is a string comparison, not a null check\n self.navigator &&\n self.navigator.userAgent &&\n !(\n self.navigator.userAgent.includes('Node.js') ||\n self.navigator.userAgent.includes('jsdom')\n )\n ) {\n // Create a script tag for the Scratch Link browser extension, unless one already exists\n const scriptElement = document.getElementById('scratch-link-extension-script');\n if (!scriptElement) {\n const script = document.createElement('script');\n script.id = 'scratch-link-extension-script';\n document.body.appendChild(script);\n\n // Tell the browser extension to inject its script.\n // If the extension isn't present or isn't active, this will do nothing.\n self.postMessage('inject-scratch-link-script', self.origin);\n }\n }\n }\n\n /**\n * Get a scratch link socket.\n * @param {string} type Either BLE or BT\n * @returns {ScratchLinkSocket} The scratch link socket.\n */\n getScratchLinkSocket (type) {\n const factory = this._linkSocketFactory || this._defaultScratchLinkSocketFactory;\n return factory(type);\n }\n\n /**\n * Configure how ScratchLink sockets are created. Factory must consume a \"type\" parameter\n * either BT or BLE.\n * @param {Function} factory The new factory for creating ScratchLink sockets.\n */\n configureScratchLinkSocketFactory (factory) {\n this._linkSocketFactory = factory;\n }\n\n /**\n * The default scratch link socket creator, using websockets to the installed device manager.\n * @param {string} type Either BLE or BT\n * @returns {ScratchLinkSocket} The new scratch link socket (a WebSocket object)\n */\n _defaultScratchLinkSocketFactory (type) {\n const Scratch = self.Scratch;\n const ScratchLinkSafariSocket = Scratch && Scratch.ScratchLinkSafariSocket;\n // detect this every time in case the user turns on the extension after loading the page\n const useSafariSocket = ScratchLinkSafariSocket && ScratchLinkSafariSocket.isSafariHelperCompatible();\n return useSafariSocket ? new ScratchLinkSafariSocket(type) : new ScratchLinkWebSocket(type);\n }\n\n /**\n * Register an extension that communications with a hardware peripheral by id,\n * to have access to it and its peripheral functions in the future.\n * @param {string} extensionId - the id of the extension.\n * @param {object} extension - the extension to register.\n */\n registerPeripheralExtension (extensionId, extension) {\n this.peripheralExtensions[extensionId] = extension;\n }\n\n /**\n * Tell the specified extension to scan for a peripheral.\n * @param {string} extensionId - the id of the extension.\n */\n scanForPeripheral (extensionId) {\n if (this.peripheralExtensions[extensionId]) {\n this.peripheralExtensions[extensionId].scan();\n }\n }\n\n /**\n * Connect to the extension's specified peripheral.\n * @param {string} extensionId - the id of the extension.\n * @param {number} peripheralId - the id of the peripheral.\n */\n connectPeripheral (extensionId, peripheralId) {\n if (this.peripheralExtensions[extensionId]) {\n this.peripheralExtensions[extensionId].connect(peripheralId);\n }\n }\n\n /**\n * Disconnect from the extension's connected peripheral.\n * @param {string} extensionId - the id of the extension.\n */\n disconnectPeripheral (extensionId) {\n if (this.peripheralExtensions[extensionId]) {\n this.peripheralExtensions[extensionId].disconnect();\n }\n }\n\n /**\n * Returns whether the extension has a currently connected peripheral.\n * @param {string} extensionId - the id of the extension.\n * @return {boolean} - whether the extension has a connected peripheral.\n */\n getPeripheralIsConnected (extensionId) {\n let isConnected = false;\n if (this.peripheralExtensions[extensionId]) {\n isConnected = this.peripheralExtensions[extensionId].isConnected();\n }\n return isConnected;\n }\n\n /**\n * Emit an event to indicate that the microphone is being used to stream audio.\n * @param {boolean} listening - true if the microphone is currently listening.\n */\n emitMicListening (listening) {\n this.emit(Runtime.MIC_LISTENING, listening);\n }\n\n /**\n * Retrieve the function associated with the given opcode.\n * @param {!string} opcode The opcode to look up.\n * @return {Function} The function which implements the opcode.\n */\n getOpcodeFunction (opcode) {\n return this._primitives[opcode];\n }\n\n /**\n * Return whether an opcode represents a hat block.\n * @param {!string} opcode The opcode to look up.\n * @return {boolean} True if the op is known to be a hat.\n */\n getIsHat (opcode) {\n return Object.prototype.hasOwnProperty.call(this._hats, opcode);\n }\n\n /**\n * Return whether an opcode represents an edge-activated hat block.\n * @param {!string} opcode The opcode to look up.\n * @return {boolean} True if the op is known to be a edge-activated hat.\n */\n getIsEdgeActivatedHat (opcode) {\n return Object.prototype.hasOwnProperty.call(this._hats, opcode) &&\n this._hats[opcode].edgeActivated;\n }\n\n\n /**\n * Attach the audio engine\n * @param {!AudioEngine} audioEngine The audio engine to attach\n */\n attachAudioEngine (audioEngine) {\n this.audioEngine = audioEngine;\n }\n\n /**\n * Attach the renderer\n * @param {!RenderWebGL} renderer The renderer to attach\n */\n attachRenderer (renderer) {\n this.renderer = renderer;\n this.renderer.setLayerGroupOrdering(StageLayering.LAYER_GROUPS);\n }\n\n /**\n * Set the bitmap adapter for the VM/runtime, which converts scratch 2\n * bitmaps to scratch 3 bitmaps. (Scratch 3 bitmaps are all bitmap resolution 2)\n * @param {!function} bitmapAdapter The adapter to attach\n */\n attachV2BitmapAdapter (bitmapAdapter) {\n this.v2BitmapAdapter = bitmapAdapter;\n }\n\n /**\n * Attach the storage module\n * @param {!ScratchStorage} storage The storage module to attach\n */\n attachStorage (storage) {\n this.storage = storage;\n fetchWithTimeout.setFetch(storage.scratchFetch.scratchFetch);\n this.resetRunId();\n }\n\n // -----------------------------------------------------------------------------\n // -----------------------------------------------------------------------------\n\n /**\n * Create a thread and push it to the list of threads.\n * @param {!string} id ID of block that starts the stack.\n * @param {!Target} target Target to run thread on.\n * @param {?object} opts optional arguments\n * @param {?boolean} opts.stackClick true if the script was activated by clicking on the stack\n * @param {?boolean} opts.updateMonitor true if the script should update a monitor value\n * @return {!Thread} The newly created thread.\n */\n _pushThread (id, target, opts) {\n const thread = new Thread(id);\n thread.target = target;\n thread.stackClick = Boolean(opts && opts.stackClick);\n thread.updateMonitor = Boolean(opts && opts.updateMonitor);\n thread.blockContainer = thread.updateMonitor ?\n this.monitorBlocks :\n target.blocks;\n\n thread.pushStack(id);\n this.threads.push(thread);\n return thread;\n }\n\n /**\n * Stop a thread: stop running it immediately, and remove it from the thread list later.\n * @param {!Thread} thread Thread object to remove from actives\n */\n _stopThread (thread) {\n // Mark the thread for later removal\n thread.isKilled = true;\n // Inform sequencer to stop executing that thread.\n this.sequencer.retireThread(thread);\n }\n\n /**\n * Restart a thread in place, maintaining its position in the list of threads.\n * This is used by `startHats` to and is necessary to ensure 2.0-like execution order.\n * Test project: https://scratch.mit.edu/projects/130183108/\n * @param {!Thread} thread Thread object to restart.\n * @return {Thread} The restarted thread.\n */\n _restartThread (thread) {\n const newThread = new Thread(thread.topBlock);\n newThread.target = thread.target;\n newThread.stackClick = thread.stackClick;\n newThread.updateMonitor = thread.updateMonitor;\n newThread.blockContainer = thread.blockContainer;\n newThread.pushStack(thread.topBlock);\n const i = this.threads.indexOf(thread);\n if (i > -1) {\n this.threads[i] = newThread;\n return newThread;\n }\n this.threads.push(thread);\n return thread;\n }\n\n /**\n * Return whether a thread is currently active/running.\n * @param {?Thread} thread Thread object to check.\n * @return {boolean} True if the thread is active/running.\n */\n isActiveThread (thread) {\n return (\n (\n thread.stack.length > 0 &&\n thread.status !== Thread.STATUS_DONE) &&\n this.threads.indexOf(thread) > -1);\n }\n\n /**\n * Return whether a thread is waiting for more information or done.\n * @param {?Thread} thread Thread object to check.\n * @return {boolean} True if the thread is waiting\n */\n isWaitingThread (thread) {\n return (\n thread.status === Thread.STATUS_PROMISE_WAIT ||\n thread.status === Thread.STATUS_YIELD_TICK ||\n !this.isActiveThread(thread)\n );\n }\n\n /**\n * Toggle a script.\n * @param {!string} topBlockId ID of block that starts the script.\n * @param {?object} opts optional arguments to toggle script\n * @param {?string} opts.target target ID for target to run script on. If not supplied, uses editing target.\n * @param {?boolean} opts.stackClick true if the user activated the stack by clicking, false if not. This\n * determines whether we show a visual report when turning on the script.\n */\n toggleScript (topBlockId, opts) {\n opts = Object.assign({\n target: this._editingTarget,\n stackClick: false\n }, opts);\n // Remove any existing thread.\n for (let i = 0; i < this.threads.length; i++) {\n // Toggling a script that's already running turns it off\n if (this.threads[i].topBlock === topBlockId && this.threads[i].status !== Thread.STATUS_DONE) {\n const blockContainer = opts.target.blocks;\n const opcode = blockContainer.getOpcode(blockContainer.getBlock(topBlockId));\n\n if (this.getIsEdgeActivatedHat(opcode) && this.threads[i].stackClick !== opts.stackClick) {\n // Allow edge activated hat thread stack click to coexist with\n // edge activated hat thread that runs every frame\n continue;\n }\n this._stopThread(this.threads[i]);\n return;\n }\n }\n // Otherwise add it.\n this._pushThread(topBlockId, opts.target, opts);\n }\n\n /**\n * Enqueue a script that when finished will update the monitor for the block.\n * @param {!string} topBlockId ID of block that starts the script.\n * @param {?Target} optTarget target Target to run script on. If not supplied, uses editing target.\n */\n addMonitorScript (topBlockId, optTarget) {\n if (!optTarget) optTarget = this._editingTarget;\n for (let i = 0; i < this.threads.length; i++) {\n // Don't re-add the script if it's already running\n if (this.threads[i].topBlock === topBlockId && this.threads[i].status !== Thread.STATUS_DONE &&\n this.threads[i].updateMonitor) {\n return;\n }\n }\n // Otherwise add it.\n this._pushThread(topBlockId, optTarget, {updateMonitor: true});\n }\n\n /**\n * Run a function `f` for all scripts in a workspace.\n * `f` will be called with two parameters:\n * - the top block ID of the script.\n * - the target that owns the script.\n * @param {!Function} f Function to call for each script.\n * @param {Target=} optTarget Optionally, a target to restrict to.\n */\n allScriptsDo (f, optTarget) {\n let targets = this.executableTargets;\n if (optTarget) {\n targets = [optTarget];\n }\n for (let t = targets.length - 1; t >= 0; t--) {\n const target = targets[t];\n const scripts = target.blocks.getScripts();\n for (let j = 0; j < scripts.length; j++) {\n const topBlockId = scripts[j];\n f(topBlockId, target);\n }\n }\n }\n\n allScriptsByOpcodeDo (opcode, f, optTarget) {\n let targets = this.executableTargets;\n if (optTarget) {\n targets = [optTarget];\n }\n for (let t = targets.length - 1; t >= 0; t--) {\n const target = targets[t];\n const scripts = BlocksRuntimeCache.getScripts(target.blocks, opcode);\n for (let j = 0; j < scripts.length; j++) {\n f(scripts[j], target);\n }\n }\n }\n\n /**\n * Start all relevant hats.\n * @param {!string} requestedHatOpcode Opcode of hats to start.\n * @param {object=} optMatchFields Optionally, fields to match on the hat.\n * @param {Target=} optTarget Optionally, a target to restrict to.\n * @return {Array.} List of threads started by this function.\n */\n startHats (requestedHatOpcode,\n optMatchFields, optTarget) {\n if (!Object.prototype.hasOwnProperty.call(this._hats, requestedHatOpcode)) {\n // No known hat with this opcode.\n return;\n }\n const instance = this;\n const newThreads = [];\n // Look up metadata for the relevant hat.\n const hatMeta = instance._hats[requestedHatOpcode];\n\n for (const opts in optMatchFields) {\n if (!Object.prototype.hasOwnProperty.call(optMatchFields, opts)) continue;\n optMatchFields[opts] = optMatchFields[opts].toUpperCase();\n }\n\n // Consider all scripts, looking for hats with opcode `requestedHatOpcode`.\n this.allScriptsByOpcodeDo(requestedHatOpcode, (script, target) => {\n const {\n blockId: topBlockId,\n fieldsOfInputs: hatFields\n } = script;\n\n // Match any requested fields.\n // For example: ensures that broadcasts match.\n // This needs to happen before the block is evaluated\n // (i.e., before the predicate can be run) because \"broadcast and wait\"\n // needs to have a precise collection of started threads.\n for (const matchField in optMatchFields) {\n if (hatFields[matchField].value !== optMatchFields[matchField]) {\n // Field mismatch.\n return;\n }\n }\n\n if (hatMeta.restartExistingThreads) {\n // If `restartExistingThreads` is true, we should stop\n // any existing threads starting with the top block.\n for (let i = 0; i < this.threads.length; i++) {\n if (this.threads[i].target === target &&\n this.threads[i].topBlock === topBlockId &&\n // stack click threads and hat threads can coexist\n !this.threads[i].stackClick) {\n newThreads.push(this._restartThread(this.threads[i]));\n return;\n }\n }\n } else {\n // If `restartExistingThreads` is false, we should\n // give up if any threads with the top block are running.\n for (let j = 0; j < this.threads.length; j++) {\n if (this.threads[j].target === target &&\n this.threads[j].topBlock === topBlockId &&\n // stack click threads and hat threads can coexist\n !this.threads[j].stackClick &&\n this.threads[j].status !== Thread.STATUS_DONE) {\n // Some thread is already running.\n return;\n }\n }\n }\n // Start the thread with this top block.\n newThreads.push(this._pushThread(topBlockId, target));\n }, optTarget);\n // For compatibility with Scratch 2, edge triggered hats need to be processed before\n // threads are stepped. See ScratchRuntime.as for original implementation\n newThreads.forEach(thread => {\n execute(this.sequencer, thread);\n thread.goToNextBlock();\n });\n return newThreads;\n }\n\n\n /**\n * Dispose all targets. Return to clean state.\n */\n dispose () {\n this.stopAll();\n // Deleting each target's variable's monitors.\n this.targets.forEach(target => {\n if (target.isOriginal) target.deleteMonitors();\n });\n\n this.targets.map(this.disposeTarget, this);\n this._monitorState = OrderedMap({});\n this.emit(Runtime.RUNTIME_DISPOSED);\n this.ioDevices.clock.resetProjectTimer();\n // @todo clear out extensions? turboMode? etc.\n\n // *********** Cloud *******************\n\n // If the runtime currently has cloud data,\n // emit a has cloud data update event resetting\n // it to false\n if (this.hasCloudData()) {\n this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, false);\n }\n\n this.ioDevices.cloud.clear();\n\n // Reset runtime cloud data info\n const newCloudDataManager = cloudDataManager();\n this.hasCloudData = newCloudDataManager.hasCloudVariables;\n this.canAddCloudVariable = newCloudDataManager.canAddCloudVariable;\n this.addCloudVariable = this._initializeAddCloudVariable(newCloudDataManager);\n this.removeCloudVariable = this._initializeRemoveCloudVariable(newCloudDataManager);\n }\n\n /**\n * Add a target to the runtime. This tracks the sprite pane\n * ordering of the target. The target still needs to be put\n * into the correct execution order after calling this function.\n * @param {Target} target target to add\n */\n addTarget (target) {\n this.targets.push(target);\n this.executableTargets.push(target);\n }\n\n /**\n * Move a target in the execution order by a relative amount.\n *\n * A positve number will make the target execute earlier. A negative number\n * will make the target execute later in the order.\n *\n * @param {Target} executableTarget target to move\n * @param {number} delta number of positions to move target by\n * @returns {number} new position in execution order\n */\n moveExecutable (executableTarget, delta) {\n const oldIndex = this.executableTargets.indexOf(executableTarget);\n this.executableTargets.splice(oldIndex, 1);\n let newIndex = oldIndex + delta;\n if (newIndex > this.executableTargets.length) {\n newIndex = this.executableTargets.length;\n }\n if (newIndex <= 0) {\n if (this.executableTargets.length > 0 && this.executableTargets[0].isStage) {\n newIndex = 1;\n } else {\n newIndex = 0;\n }\n }\n this.executableTargets.splice(newIndex, 0, executableTarget);\n return newIndex;\n }\n\n /**\n * Set a target to execute at a specific position in the execution order.\n *\n * Infinity will set the target to execute first. 0 will set the target to\n * execute last (before the stage).\n *\n * @param {Target} executableTarget target to move\n * @param {number} newIndex position in execution order to place the target\n * @returns {number} new position in the execution order\n */\n setExecutablePosition (executableTarget, newIndex) {\n const oldIndex = this.executableTargets.indexOf(executableTarget);\n return this.moveExecutable(executableTarget, newIndex - oldIndex);\n }\n\n /**\n * Remove a target from the execution set.\n * @param {Target} executableTarget target to remove\n */\n removeExecutable (executableTarget) {\n const oldIndex = this.executableTargets.indexOf(executableTarget);\n if (oldIndex > -1) {\n this.executableTargets.splice(oldIndex, 1);\n }\n }\n\n /**\n * Dispose of a target.\n * @param {!Target} disposingTarget Target to dispose of.\n */\n disposeTarget (disposingTarget) {\n this.targets = this.targets.filter(target => {\n if (disposingTarget !== target) return true;\n // Allow target to do dispose actions.\n target.dispose();\n // Remove from list of targets.\n return false;\n });\n }\n\n /**\n * Stop any threads acting on the target.\n * @param {!Target} target Target to stop threads for.\n * @param {Thread=} optThreadException Optional thread to skip.\n */\n stopForTarget (target, optThreadException) {\n // Emit stop event to allow blocks to clean up any state.\n this.emit(Runtime.STOP_FOR_TARGET, target, optThreadException);\n\n // Stop any threads on the target.\n for (let i = 0; i < this.threads.length; i++) {\n if (this.threads[i] === optThreadException) {\n continue;\n }\n if (this.threads[i].target === target) {\n this._stopThread(this.threads[i]);\n }\n }\n }\n\n /**\n * Reset the Run ID. Call this any time the project logically starts, stops, or changes identity.\n */\n resetRunId () {\n if (!this.storage) {\n // see also: attachStorage\n return;\n }\n\n const newRunId = uuid.v1();\n this.storage.scratchFetch.setMetadata(this.storage.scratchFetch.RequestMetadata.RunId, newRunId);\n }\n\n /**\n * Start all threads that start with the green flag.\n */\n greenFlag () {\n this.stopAll();\n this.emit(Runtime.PROJECT_START);\n this.ioDevices.clock.resetProjectTimer();\n this.targets.forEach(target => target.clearEdgeActivatedValues());\n // Inform all targets of the green flag.\n for (let i = 0; i < this.targets.length; i++) {\n this.targets[i].onGreenFlag();\n }\n this.startHats('event_whenflagclicked');\n }\n\n /**\n * Stop \"everything.\"\n */\n stopAll () {\n // Emit stop event to allow blocks to clean up any state.\n this.emit(Runtime.PROJECT_STOP_ALL);\n\n // Dispose all clones.\n const newTargets = [];\n for (let i = 0; i < this.targets.length; i++) {\n this.targets[i].onStopAll();\n if (Object.prototype.hasOwnProperty.call(this.targets[i], 'isOriginal') &&\n !this.targets[i].isOriginal) {\n this.targets[i].dispose();\n } else {\n newTargets.push(this.targets[i]);\n }\n }\n this.targets = newTargets;\n // Dispose of the active thread.\n if (this.sequencer.activeThread !== null) {\n this._stopThread(this.sequencer.activeThread);\n }\n // Remove all remaining threads from executing in the next tick.\n this.threads = [];\n\n this.resetRunId();\n }\n\n /**\n * Repeatedly run `sequencer.stepThreads` and filter out\n * inactive threads after each iteration.\n */\n _step () {\n if (this.profiler !== null) {\n if (stepProfilerId === -1) {\n stepProfilerId = this.profiler.idByName('Runtime._step');\n }\n this.profiler.start(stepProfilerId);\n }\n\n // Clean up threads that were told to stop during or since the last step\n this.threads = this.threads.filter(thread => !thread.isKilled);\n\n // Find all edge-activated hats, and add them to threads to be evaluated.\n for (const hatType in this._hats) {\n if (!Object.prototype.hasOwnProperty.call(this._hats, hatType)) continue;\n const hat = this._hats[hatType];\n if (hat.edgeActivated) {\n this.startHats(hatType);\n }\n }\n this.redrawRequested = false;\n this._pushMonitors();\n if (this.profiler !== null) {\n if (stepThreadsProfilerId === -1) {\n stepThreadsProfilerId = this.profiler.idByName('Sequencer.stepThreads');\n }\n this.profiler.start(stepThreadsProfilerId);\n }\n const doneThreads = this.sequencer.stepThreads();\n if (this.profiler !== null) {\n this.profiler.stop();\n }\n this._updateGlows(doneThreads);\n // Add done threads so that even if a thread finishes within 1 frame, the green\n // flag will still indicate that a script ran.\n this._emitProjectRunStatus(\n this.threads.length + doneThreads.length -\n this._getMonitorThreadCount([...this.threads, ...doneThreads]));\n // Store threads that completed this iteration for testing and other\n // internal purposes.\n this._lastStepDoneThreads = doneThreads;\n if (this.renderer) {\n // @todo: Only render when this.redrawRequested or clones rendered.\n if (this.profiler !== null) {\n if (rendererDrawProfilerId === -1) {\n rendererDrawProfilerId = this.profiler.idByName('RenderWebGL.draw');\n }\n this.profiler.start(rendererDrawProfilerId);\n }\n this.renderer.draw();\n if (this.profiler !== null) {\n this.profiler.stop();\n }\n }\n\n if (this._refreshTargets) {\n this.emit(Runtime.TARGETS_UPDATE, false /* Don't emit project changed */);\n this._refreshTargets = false;\n }\n\n if (!this._prevMonitorState.equals(this._monitorState)) {\n this.emit(Runtime.MONITORS_UPDATE, this._monitorState);\n this._prevMonitorState = this._monitorState;\n }\n\n if (this.profiler !== null) {\n this.profiler.stop();\n this.profiler.reportFrames();\n }\n }\n\n /**\n * Get the number of threads in the given array that are monitor threads (threads\n * that update monitor values, and don't count as running a script).\n * @param {!Array.} threads The set of threads to look through.\n * @return {number} The number of monitor threads in threads.\n */\n _getMonitorThreadCount (threads) {\n let count = 0;\n threads.forEach(thread => {\n if (thread.updateMonitor) count++;\n });\n return count;\n }\n\n /**\n * Queue monitor blocks to sequencer to be run.\n */\n _pushMonitors () {\n this.monitorBlocks.runAllMonitored(this);\n }\n\n /**\n * Set the current editing target known by the runtime.\n * @param {!Target} editingTarget New editing target.\n */\n setEditingTarget (editingTarget) {\n const oldEditingTarget = this._editingTarget;\n this._editingTarget = editingTarget;\n // Script glows must be cleared.\n this._scriptGlowsPreviousFrame = [];\n this._updateGlows();\n\n if (oldEditingTarget !== this._editingTarget) {\n this.requestToolboxExtensionsUpdate();\n }\n }\n\n /**\n * Set whether we are in 30 TPS compatibility mode.\n * @param {boolean} compatibilityModeOn True iff in compatibility mode.\n */\n setCompatibilityMode (compatibilityModeOn) {\n this.compatibilityMode = compatibilityModeOn;\n if (this._steppingInterval) {\n clearInterval(this._steppingInterval);\n this._steppingInterval = null;\n this.start();\n }\n }\n\n /**\n * Emit glows/glow clears for scripts after a single tick.\n * Looks at `this.threads` and notices which have turned on/off new glows.\n * @param {Array.=} optExtraThreads Optional list of inactive threads.\n */\n _updateGlows (optExtraThreads) {\n const searchThreads = [];\n searchThreads.push(...this.threads);\n if (optExtraThreads) {\n searchThreads.push(...optExtraThreads);\n }\n // Set of scripts that request a glow this frame.\n const requestedGlowsThisFrame = [];\n // Final set of scripts glowing during this frame.\n const finalScriptGlows = [];\n // Find all scripts that should be glowing.\n for (let i = 0; i < searchThreads.length; i++) {\n const thread = searchThreads[i];\n const target = thread.target;\n if (target === this._editingTarget) {\n const blockForThread = thread.blockGlowInFrame;\n if (thread.requestScriptGlowInFrame || thread.stackClick) {\n let script = target.blocks.getTopLevelScript(blockForThread);\n if (!script) {\n // Attempt to find in flyout blocks.\n script = this.flyoutBlocks.getTopLevelScript(\n blockForThread\n );\n }\n if (script) {\n requestedGlowsThisFrame.push(script);\n }\n }\n }\n }\n // Compare to previous frame.\n for (let j = 0; j < this._scriptGlowsPreviousFrame.length; j++) {\n const previousFrameGlow = this._scriptGlowsPreviousFrame[j];\n if (requestedGlowsThisFrame.indexOf(previousFrameGlow) < 0) {\n // Glow turned off.\n this.glowScript(previousFrameGlow, false);\n } else {\n // Still glowing.\n finalScriptGlows.push(previousFrameGlow);\n }\n }\n for (let k = 0; k < requestedGlowsThisFrame.length; k++) {\n const currentFrameGlow = requestedGlowsThisFrame[k];\n if (this._scriptGlowsPreviousFrame.indexOf(currentFrameGlow) < 0) {\n // Glow turned on.\n this.glowScript(currentFrameGlow, true);\n finalScriptGlows.push(currentFrameGlow);\n }\n }\n this._scriptGlowsPreviousFrame = finalScriptGlows;\n }\n\n /**\n * Emit run start/stop after each tick. Emits when `this.threads.length` goes\n * between non-zero and zero\n *\n * @param {number} nonMonitorThreadCount The new nonMonitorThreadCount\n */\n _emitProjectRunStatus (nonMonitorThreadCount) {\n if (this._nonMonitorThreadCount === 0 && nonMonitorThreadCount > 0) {\n this.emit(Runtime.PROJECT_RUN_START);\n }\n if (this._nonMonitorThreadCount > 0 && nonMonitorThreadCount === 0) {\n this.emit(Runtime.PROJECT_RUN_STOP);\n }\n this._nonMonitorThreadCount = nonMonitorThreadCount;\n }\n\n /**\n * \"Quiet\" a script's glow: stop the VM from generating glow/unglow events\n * about that script. Use when a script has just been deleted, but we may\n * still be tracking glow data about it.\n * @param {!string} scriptBlockId Id of top-level block in script to quiet.\n */\n quietGlow (scriptBlockId) {\n const index = this._scriptGlowsPreviousFrame.indexOf(scriptBlockId);\n if (index > -1) {\n this._scriptGlowsPreviousFrame.splice(index, 1);\n }\n }\n\n /**\n * Emit feedback for block glowing (used in the sequencer).\n * @param {?string} blockId ID for the block to update glow\n * @param {boolean} isGlowing True to turn on glow; false to turn off.\n */\n glowBlock (blockId, isGlowing) {\n if (isGlowing) {\n this.emit(Runtime.BLOCK_GLOW_ON, {id: blockId});\n } else {\n this.emit(Runtime.BLOCK_GLOW_OFF, {id: blockId});\n }\n }\n\n /**\n * Emit feedback for script glowing.\n * @param {?string} topBlockId ID for the top block to update glow\n * @param {boolean} isGlowing True to turn on glow; false to turn off.\n */\n glowScript (topBlockId, isGlowing) {\n if (isGlowing) {\n this.emit(Runtime.SCRIPT_GLOW_ON, {id: topBlockId});\n } else {\n this.emit(Runtime.SCRIPT_GLOW_OFF, {id: topBlockId});\n }\n }\n\n /**\n * Emit whether blocks are being dragged over gui\n * @param {boolean} areBlocksOverGui True if blocks are dragged out of blocks workspace, false otherwise\n */\n emitBlockDragUpdate (areBlocksOverGui) {\n this.emit(Runtime.BLOCK_DRAG_UPDATE, areBlocksOverGui);\n }\n\n /**\n * Emit event to indicate that the block drag has ended with the blocks outside the blocks workspace\n * @param {Array.} blocks The set of blocks dragged to the GUI\n * @param {string} topBlockId The original id of the top block being dragged\n */\n emitBlockEndDrag (blocks, topBlockId) {\n this.emit(Runtime.BLOCK_DRAG_END, blocks, topBlockId);\n }\n\n /**\n * Emit value for reporter to show in the blocks.\n * @param {string} blockId ID for the block.\n * @param {string} value Value to show associated with the block.\n */\n visualReport (blockId, value) {\n this.emit(Runtime.VISUAL_REPORT, {id: blockId, value: String(value)});\n }\n\n /**\n * Add a monitor to the state. If the monitor already exists in the state,\n * updates those properties that are defined in the given monitor record.\n * @param {!MonitorRecord} monitor Monitor to add.\n */\n requestAddMonitor (monitor) {\n const id = monitor.get('id');\n if (!this.requestUpdateMonitor(monitor)) { // update monitor if it exists in the state\n // if the monitor did not exist in the state, add it\n this._monitorState = this._monitorState.set(id, monitor);\n }\n }\n\n /**\n * Update a monitor in the state and report success/failure of update.\n * @param {!Map} monitor Monitor values to update. Values on the monitor with overwrite\n * values on the old monitor with the same ID. If a value isn't defined on the new monitor,\n * the old monitor will keep its old value.\n * @return {boolean} true if monitor exists in the state and was updated, false if it did not exist.\n */\n requestUpdateMonitor (monitor) {\n const id = monitor.get('id');\n if (this._monitorState.has(id)) {\n this._monitorState =\n // Use mergeWith here to prevent undefined values from overwriting existing ones\n this._monitorState.set(id, this._monitorState.get(id).mergeWith((prev, next) => {\n if (typeof next === 'undefined' || next === null) {\n return prev;\n }\n return next;\n }, monitor));\n return true;\n }\n return false;\n }\n\n /**\n * Removes a monitor from the state. Does nothing if the monitor already does\n * not exist in the state.\n * @param {!string} monitorId ID of the monitor to remove.\n */\n requestRemoveMonitor (monitorId) {\n this._monitorState = this._monitorState.delete(monitorId);\n }\n\n /**\n * Hides a monitor and returns success/failure of action.\n * @param {!string} monitorId ID of the monitor to hide.\n * @return {boolean} true if monitor exists and was updated, false otherwise\n */\n requestHideMonitor (monitorId) {\n return this.requestUpdateMonitor(new Map([\n ['id', monitorId],\n ['visible', false]\n ]));\n }\n\n /**\n * Shows a monitor and returns success/failure of action.\n * not exist in the state.\n * @param {!string} monitorId ID of the monitor to show.\n * @return {boolean} true if monitor exists and was updated, false otherwise\n */\n requestShowMonitor (monitorId) {\n return this.requestUpdateMonitor(new Map([\n ['id', monitorId],\n ['visible', true]\n ]));\n }\n\n /**\n * Removes all monitors with the given target ID from the state. Does nothing if\n * the monitor already does not exist in the state.\n * @param {!string} targetId Remove all monitors with given target ID.\n */\n requestRemoveMonitorByTargetId (targetId) {\n this._monitorState = this._monitorState.filterNot(value => value.targetId === targetId);\n }\n\n /**\n * Get a target by its id.\n * @param {string} targetId Id of target to find.\n * @return {?Target} The target, if found.\n */\n getTargetById (targetId) {\n for (let i = 0; i < this.targets.length; i++) {\n const target = this.targets[i];\n if (target.id === targetId) {\n return target;\n }\n }\n }\n\n /**\n * Get the first original (non-clone-block-created) sprite given a name.\n * @param {string} spriteName Name of sprite to look for.\n * @return {?Target} Target representing a sprite of the given name.\n */\n getSpriteTargetByName (spriteName) {\n for (let i = 0; i < this.targets.length; i++) {\n const target = this.targets[i];\n if (target.isStage) {\n continue;\n }\n if (target.sprite && target.sprite.name === spriteName) {\n return target;\n }\n }\n }\n\n /**\n * Get a target by its drawable id.\n * @param {number} drawableID drawable id of target to find\n * @return {?Target} The target, if found\n */\n getTargetByDrawableId (drawableID) {\n for (let i = 0; i < this.targets.length; i++) {\n const target = this.targets[i];\n if (target.drawableID === drawableID) return target;\n }\n }\n\n /**\n * Update the clone counter to track how many clones are created.\n * @param {number} changeAmount How many clones have been created/destroyed.\n */\n changeCloneCounter (changeAmount) {\n this._cloneCounter += changeAmount;\n }\n\n /**\n * Return whether there are clones available.\n * @return {boolean} True until the number of clones hits Runtime.MAX_CLONES.\n */\n clonesAvailable () {\n return this._cloneCounter < Runtime.MAX_CLONES;\n }\n\n /**\n * Handle that the project has loaded in the Virtual Machine.\n */\n handleProjectLoaded () {\n this.emit(Runtime.PROJECT_LOADED);\n this.resetRunId();\n }\n\n /**\n * Report that the project has changed in a way that would affect serialization\n */\n emitProjectChanged () {\n this.emit(Runtime.PROJECT_CHANGED);\n }\n\n /**\n * Report that a new target has been created, possibly by cloning an existing target.\n * @param {Target} newTarget - the newly created target.\n * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any.\n * @fires Runtime#targetWasCreated\n */\n fireTargetWasCreated (newTarget, sourceTarget) {\n this.emit('targetWasCreated', newTarget, sourceTarget);\n }\n\n /**\n * Report that a clone target is being removed.\n * @param {Target} target - the target being removed\n * @fires Runtime#targetWasRemoved\n */\n fireTargetWasRemoved (target) {\n this.emit('targetWasRemoved', target);\n }\n\n /**\n * Get a target representing the Scratch stage, if one exists.\n * @return {?Target} The target, if found.\n */\n getTargetForStage () {\n for (let i = 0; i < this.targets.length; i++) {\n const target = this.targets[i];\n if (target.isStage) {\n return target;\n }\n }\n }\n\n /**\n * Get the editing target.\n * @return {?Target} The editing target.\n */\n getEditingTarget () {\n return this._editingTarget;\n }\n\n getAllVarNamesOfType (varType) {\n let varNames = [];\n for (const target of this.targets) {\n const targetVarNames = target.getAllVariableNamesInScopeByType(varType, true);\n varNames = varNames.concat(targetVarNames);\n }\n return varNames;\n }\n\n /**\n * Get the label or label function for an opcode\n * @param {string} extendedOpcode - the opcode you want a label for\n * @return {object} - object with label and category\n * @property {string} category - the category for this opcode\n * @property {Function} [labelFn] - function to generate the label for this opcode\n * @property {string} [label] - the label for this opcode if `labelFn` is absent\n */\n getLabelForOpcode (extendedOpcode) {\n const [category, opcode] = StringUtil.splitFirst(extendedOpcode, '_');\n if (!(category && opcode)) return;\n\n const categoryInfo = this._blockInfo.find(ci => ci.id === category);\n if (!categoryInfo) return;\n\n const block = categoryInfo.blocks.find(b => b.info.opcode === opcode);\n if (!block) return;\n\n // TODO: we may want to format the label in a locale-specific way.\n return {\n category: 'extension', // This assumes that all extensions have the same monitor color.\n label: `${categoryInfo.name}: ${block.info.text}`\n };\n }\n\n /**\n * Create a new global variable avoiding conflicts with other variable names.\n * @param {string} variableName The desired variable name for the new global variable.\n * This can be turned into a fresh name as necessary.\n * @param {string} optVarId An optional ID to use for the variable. A new one will be generated\n * if a falsey value for this parameter is provided.\n * @param {string} optVarType The type of the variable to create. Defaults to Variable.SCALAR_TYPE.\n * @return {Variable} The new variable that was created.\n */\n createNewGlobalVariable (variableName, optVarId, optVarType) {\n const varType = (typeof optVarType === 'string') ? optVarType : Variable.SCALAR_TYPE;\n const allVariableNames = this.getAllVarNamesOfType(varType);\n const newName = StringUtil.unusedName(variableName, allVariableNames);\n const variable = new Variable(optVarId || uid(), newName, varType);\n const stage = this.getTargetForStage();\n stage.variables[variable.id] = variable;\n return variable;\n }\n\n /**\n * Tell the runtime to request a redraw.\n * Use after a clone/sprite has completed some visible operation on the stage.\n */\n requestRedraw () {\n this.redrawRequested = true;\n }\n\n /**\n * Emit a targets update at the end of the step if the provided target is\n * the original sprite\n * @param {!Target} target Target requesting the targets update\n */\n requestTargetsUpdate (target) {\n if (!target.isOriginal) return;\n this._refreshTargets = true;\n }\n\n /**\n * Emit an event that indicates that the blocks on the workspace need updating.\n */\n requestBlocksUpdate () {\n this.emit(Runtime.BLOCKS_NEED_UPDATE);\n }\n\n /**\n * Emit an event that indicates that the toolbox extension blocks need updating.\n */\n requestToolboxExtensionsUpdate () {\n this.emit(Runtime.TOOLBOX_EXTENSIONS_NEED_UPDATE);\n }\n\n /**\n * Set up timers to repeatedly step in a browser.\n */\n start () {\n // Do not start if we are already running\n if (this._steppingInterval) return;\n\n let interval = Runtime.THREAD_STEP_INTERVAL;\n if (this.compatibilityMode) {\n interval = Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY;\n }\n this.currentStepTime = interval;\n this._steppingInterval = setInterval(() => {\n this._step();\n }, interval);\n this.emit(Runtime.RUNTIME_STARTED);\n }\n\n /**\n * Quit the Runtime, clearing any handles which might keep the process alive.\n * Do not use the runtime after calling this method. This method is meant for test shutdown.\n */\n quit () {\n clearInterval(this._steppingInterval);\n this._steppingInterval = null;\n }\n\n /**\n * Turn on profiling.\n * @param {Profiler/FrameCallback} onFrame A callback handle passed a\n * profiling frame when the profiler reports its collected data.\n */\n enableProfiling (onFrame) {\n if (Profiler.available()) {\n this.profiler = new Profiler(onFrame);\n }\n }\n\n /**\n * Turn off profiling.\n */\n disableProfiling () {\n this.profiler = null;\n }\n\n /**\n * Update a millisecond timestamp value that is saved on the Runtime.\n * This value is helpful in certain instances for compatibility with Scratch 2,\n * which sometimes uses a `currentMSecs` timestamp value in Interpreter.as\n */\n updateCurrentMSecs () {\n this.currentMSecs = Date.now();\n }\n}\n\n/**\n * Event fired after a new target has been created, possibly by cloning an existing target.\n *\n * @event Runtime#targetWasCreated\n * @param {Target} newTarget - the newly created target.\n * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any.\n */\n\nmodule.exports = Runtime;\n","/**\n * These constants are copied from scratch-blocks/core/constants.js\n * @TODO find a way to require() these straight from scratch-blocks... maybe make a scratch-blocks/dist/constants.js?\n * @readonly\n * @enum {int}\n */\nconst ScratchBlocksConstants = {\n /**\n * ENUM for output shape: hexagonal (booleans/predicates).\n * @const\n */\n OUTPUT_SHAPE_HEXAGONAL: 1,\n\n /**\n * ENUM for output shape: rounded (numbers).\n * @const\n */\n OUTPUT_SHAPE_ROUND: 2,\n\n /**\n * ENUM for output shape: squared (any/all values; strings).\n * @const\n */\n OUTPUT_SHAPE_SQUARE: 3\n};\n\nmodule.exports = ScratchBlocksConstants;\n","const Timer = require('../util/timer');\nconst Thread = require('./thread');\nconst execute = require('./execute.js');\n\n/**\n * Profiler frame name for stepping a single thread.\n * @const {string}\n */\nconst stepThreadProfilerFrame = 'Sequencer.stepThread';\n\n/**\n * Profiler frame name for the inner loop of stepThreads.\n * @const {string}\n */\nconst stepThreadsInnerProfilerFrame = 'Sequencer.stepThreads#inner';\n\n/**\n * Profiler frame name for execute.\n * @const {string}\n */\nconst executeProfilerFrame = 'execute';\n\n/**\n * Profiler frame ID for stepThreadProfilerFrame.\n * @type {number}\n */\nlet stepThreadProfilerId = -1;\n\n/**\n * Profiler frame ID for stepThreadsInnerProfilerFrame.\n * @type {number}\n */\nlet stepThreadsInnerProfilerId = -1;\n\n/**\n * Profiler frame ID for executeProfilerFrame.\n * @type {number}\n */\nlet executeProfilerId = -1;\n\nclass Sequencer {\n constructor (runtime) {\n /**\n * A utility timer for timing thread sequencing.\n * @type {!Timer}\n */\n this.timer = new Timer();\n\n /**\n * Reference to the runtime owning this sequencer.\n * @type {!Runtime}\n */\n this.runtime = runtime;\n\n this.activeThread = null;\n }\n\n /**\n * Time to run a warp-mode thread, in ms.\n * @type {number}\n */\n static get WARP_TIME () {\n return 500;\n }\n\n /**\n * Step through all threads in `this.runtime.threads`, running them in order.\n * @return {Array.} List of inactive threads after stepping.\n */\n stepThreads () {\n // Work time is 75% of the thread stepping interval.\n const WORK_TIME = 0.75 * this.runtime.currentStepTime;\n // For compatibility with Scatch 2, update the millisecond clock\n // on the Runtime once per step (see Interpreter.as in Scratch 2\n // for original use of `currentMSecs`)\n this.runtime.updateCurrentMSecs();\n // Start counting toward WORK_TIME.\n this.timer.start();\n // Count of active threads.\n let numActiveThreads = Infinity;\n // Whether `stepThreads` has run through a full single tick.\n let ranFirstTick = false;\n const doneThreads = [];\n // Conditions for continuing to stepping threads:\n // 1. We must have threads in the list, and some must be active.\n // 2. Time elapsed must be less than WORK_TIME.\n // 3. Either turbo mode, or no redraw has been requested by a primitive.\n while (this.runtime.threads.length > 0 &&\n numActiveThreads > 0 &&\n this.timer.timeElapsed() < WORK_TIME &&\n (this.runtime.turboMode || !this.runtime.redrawRequested)) {\n if (this.runtime.profiler !== null) {\n if (stepThreadsInnerProfilerId === -1) {\n stepThreadsInnerProfilerId = this.runtime.profiler.idByName(stepThreadsInnerProfilerFrame);\n }\n this.runtime.profiler.start(stepThreadsInnerProfilerId);\n }\n\n numActiveThreads = 0;\n let stoppedThread = false;\n // Attempt to run each thread one time.\n const threads = this.runtime.threads;\n for (let i = 0; i < threads.length; i++) {\n const activeThread = this.activeThread = threads[i];\n // Check if the thread is done so it is not executed.\n if (activeThread.stack.length === 0 ||\n activeThread.status === Thread.STATUS_DONE) {\n // Finished with this thread.\n stoppedThread = true;\n continue;\n }\n if (activeThread.status === Thread.STATUS_YIELD_TICK &&\n !ranFirstTick) {\n // Clear single-tick yield from the last call of `stepThreads`.\n activeThread.status = Thread.STATUS_RUNNING;\n }\n if (activeThread.status === Thread.STATUS_RUNNING ||\n activeThread.status === Thread.STATUS_YIELD) {\n // Normal-mode thread: step.\n if (this.runtime.profiler !== null) {\n if (stepThreadProfilerId === -1) {\n stepThreadProfilerId = this.runtime.profiler.idByName(stepThreadProfilerFrame);\n }\n\n // Increment the number of times stepThread is called.\n this.runtime.profiler.increment(stepThreadProfilerId);\n }\n this.stepThread(activeThread);\n activeThread.warpTimer = null;\n if (activeThread.isKilled) {\n i--; // if the thread is removed from the list (killed), do not increase index\n }\n }\n if (activeThread.status === Thread.STATUS_RUNNING) {\n numActiveThreads++;\n }\n // Check if the thread completed while it just stepped to make\n // sure we remove it before the next iteration of all threads.\n if (activeThread.stack.length === 0 ||\n activeThread.status === Thread.STATUS_DONE) {\n // Finished with this thread.\n stoppedThread = true;\n }\n }\n // We successfully ticked once. Prevents running STATUS_YIELD_TICK\n // threads on the next tick.\n ranFirstTick = true;\n\n if (this.runtime.profiler !== null) {\n this.runtime.profiler.stop();\n }\n\n // Filter inactive threads from `this.runtime.threads`.\n if (stoppedThread) {\n let nextActiveThread = 0;\n for (let i = 0; i < this.runtime.threads.length; i++) {\n const thread = this.runtime.threads[i];\n if (thread.stack.length !== 0 &&\n thread.status !== Thread.STATUS_DONE) {\n this.runtime.threads[nextActiveThread] = thread;\n nextActiveThread++;\n } else {\n doneThreads.push(thread);\n }\n }\n this.runtime.threads.length = nextActiveThread;\n }\n }\n\n this.activeThread = null;\n\n return doneThreads;\n }\n\n /**\n * Step the requested thread for as long as necessary.\n * @param {!Thread} thread Thread object to step.\n */\n stepThread (thread) {\n let currentBlockId = thread.peekStack();\n if (!currentBlockId) {\n // A \"null block\" - empty branch.\n thread.popStack();\n\n // Did the null follow a hat block?\n if (thread.stack.length === 0) {\n thread.status = Thread.STATUS_DONE;\n return;\n }\n }\n // Save the current block ID to notice if we did control flow.\n while ((currentBlockId = thread.peekStack())) {\n let isWarpMode = thread.peekStackFrame().warpMode;\n if (isWarpMode && !thread.warpTimer) {\n // Initialize warp-mode timer if it hasn't been already.\n // This will start counting the thread toward `Sequencer.WARP_TIME`.\n thread.warpTimer = new Timer();\n thread.warpTimer.start();\n }\n // Execute the current block.\n if (this.runtime.profiler !== null) {\n if (executeProfilerId === -1) {\n executeProfilerId = this.runtime.profiler.idByName(executeProfilerFrame);\n }\n\n // Increment the number of times execute is called.\n this.runtime.profiler.increment(executeProfilerId);\n }\n if (thread.target === null) {\n this.retireThread(thread);\n } else {\n execute(this, thread);\n }\n thread.blockGlowInFrame = currentBlockId;\n // If the thread has yielded or is waiting, yield to other threads.\n if (thread.status === Thread.STATUS_YIELD) {\n // Mark as running for next iteration.\n thread.status = Thread.STATUS_RUNNING;\n // In warp mode, yielded blocks are re-executed immediately.\n if (isWarpMode &&\n thread.warpTimer.timeElapsed() <= Sequencer.WARP_TIME) {\n continue;\n }\n return;\n } else if (thread.status === Thread.STATUS_PROMISE_WAIT) {\n // A promise was returned by the primitive. Yield the thread\n // until the promise resolves. Promise resolution should reset\n // thread.status to Thread.STATUS_RUNNING.\n return;\n } else if (thread.status === Thread.STATUS_YIELD_TICK) {\n // stepThreads will reset the thread to Thread.STATUS_RUNNING\n return;\n }\n // If no control flow has happened, switch to next block.\n if (thread.peekStack() === currentBlockId) {\n thread.goToNextBlock();\n }\n // If no next block has been found at this point, look on the stack.\n while (!thread.peekStack()) {\n thread.popStack();\n\n if (thread.stack.length === 0) {\n // No more stack to run!\n thread.status = Thread.STATUS_DONE;\n return;\n }\n\n const stackFrame = thread.peekStackFrame();\n isWarpMode = stackFrame.warpMode;\n\n if (stackFrame.isLoop) {\n // The current level of the stack is marked as a loop.\n // Return to yield for the frame/tick in general.\n // Unless we're in warp mode - then only return if the\n // warp timer is up.\n if (!isWarpMode ||\n thread.warpTimer.timeElapsed() > Sequencer.WARP_TIME) {\n // Don't do anything to the stack, since loops need\n // to be re-executed.\n return;\n }\n // Don't go to the next block for this level of the stack,\n // since loops need to be re-executed.\n continue;\n\n } else if (stackFrame.waitingReporter) {\n // This level of the stack was waiting for a value.\n // This means a reporter has just returned - so don't go\n // to the next block for this level of the stack.\n return;\n }\n // Get next block of existing block on the stack.\n thread.goToNextBlock();\n }\n }\n }\n\n /**\n * Step a thread into a block's branch.\n * @param {!Thread} thread Thread object to step to branch.\n * @param {number} branchNum Which branch to step to (i.e., 1, 2).\n * @param {boolean} isLoop Whether this block is a loop.\n */\n stepToBranch (thread, branchNum, isLoop) {\n if (!branchNum) {\n branchNum = 1;\n }\n const currentBlockId = thread.peekStack();\n const branchId = thread.target.blocks.getBranch(\n currentBlockId,\n branchNum\n );\n thread.peekStackFrame().isLoop = isLoop;\n if (branchId) {\n // Push branch ID to the thread's stack.\n thread.pushStack(branchId);\n } else {\n thread.pushStack(null);\n }\n }\n\n /**\n * Step a procedure.\n * @param {!Thread} thread Thread object to step to procedure.\n * @param {!string} procedureCode Procedure code of procedure to step to.\n */\n stepToProcedure (thread, procedureCode) {\n const definition = thread.target.blocks.getProcedureDefinition(procedureCode);\n if (!definition) {\n return;\n }\n // Check if the call is recursive.\n // If so, set the thread to yield after pushing.\n const isRecursive = thread.isRecursiveCall(procedureCode);\n // To step to a procedure, we put its definition on the stack.\n // Execution for the thread will proceed through the definition hat\n // and on to the main definition of the procedure.\n // When that set of blocks finishes executing, it will be popped\n // from the stack by the sequencer, returning control to the caller.\n thread.pushStack(definition);\n // In known warp-mode threads, only yield when time is up.\n if (thread.peekStackFrame().warpMode &&\n thread.warpTimer.timeElapsed() > Sequencer.WARP_TIME) {\n thread.status = Thread.STATUS_YIELD;\n } else {\n // Look for warp-mode flag on definition, and set the thread\n // to warp-mode if needed.\n const definitionBlock = thread.target.blocks.getBlock(definition);\n const innerBlock = thread.target.blocks.getBlock(\n definitionBlock.inputs.custom_block.block);\n let doWarp = false;\n if (innerBlock && innerBlock.mutation) {\n const warp = innerBlock.mutation.warp;\n if (typeof warp === 'boolean') {\n doWarp = warp;\n } else if (typeof warp === 'string') {\n doWarp = JSON.parse(warp);\n }\n }\n if (doWarp) {\n thread.peekStackFrame().warpMode = true;\n } else if (isRecursive) {\n // In normal-mode threads, yield any time we have a recursive call.\n thread.status = Thread.STATUS_YIELD;\n }\n }\n }\n\n /**\n * Retire a thread in the middle, without considering further blocks.\n * @param {!Thread} thread Thread object to retire.\n */\n retireThread (thread) {\n thread.stack = [];\n thread.stackFrame = [];\n thread.requestScriptGlowInFrame = false;\n thread.status = Thread.STATUS_DONE;\n }\n}\n\nmodule.exports = Sequencer;\n","class StageLayering {\n static get BACKGROUND_LAYER () {\n return 'background';\n }\n\n static get VIDEO_LAYER () {\n return 'video';\n }\n\n static get PEN_LAYER () {\n return 'pen';\n }\n\n static get SPRITE_LAYER () {\n return 'sprite';\n }\n\n // Order of layer groups relative to each other,\n static get LAYER_GROUPS () {\n return [\n StageLayering.BACKGROUND_LAYER,\n StageLayering.VIDEO_LAYER,\n StageLayering.PEN_LAYER,\n StageLayering.SPRITE_LAYER\n ];\n }\n}\n\nmodule.exports = StageLayering;\n","const EventEmitter = require('events');\n\nconst Blocks = require('./blocks');\nconst Variable = require('../engine/variable');\nconst Comment = require('../engine/comment');\nconst uid = require('../util/uid');\nconst {Map} = require('immutable');\nconst log = require('../util/log');\nconst StringUtil = require('../util/string-util');\nconst VariableUtil = require('../util/variable-util');\n\n/**\n * @fileoverview\n * A Target is an abstract \"code-running\" object for the Scratch VM.\n * Examples include sprites/clones or potentially physical-world devices.\n */\n\nclass Target extends EventEmitter {\n\n /**\n * @param {Runtime} runtime Reference to the runtime.\n * @param {?Blocks} blocks Blocks instance for the blocks owned by this target.\n * @constructor\n */\n constructor (runtime, blocks) {\n super();\n\n if (!blocks) {\n blocks = new Blocks(runtime);\n }\n\n /**\n * Reference to the runtime.\n * @type {Runtime}\n */\n this.runtime = runtime;\n /**\n * A unique ID for this target.\n * @type {string}\n */\n this.id = uid();\n /**\n * Blocks run as code for this target.\n * @type {!Blocks}\n */\n this.blocks = blocks;\n /**\n * Dictionary of variables and their values for this target.\n * Key is the variable id.\n * @type {Object.}\n */\n this.variables = {};\n /**\n * Dictionary of comments for this target.\n * Key is the comment id.\n * @type {Object.}\n */\n this.comments = {};\n /**\n * Dictionary of custom state for this target.\n * This can be used to store target-specific custom state for blocks which need it.\n * TODO: do we want to persist this in SB3 files?\n * @type {Object.}\n */\n this._customState = {};\n\n /**\n * Currently known values for edge-activated hats.\n * Keys are block ID for the hat; values are the currently known values.\n * @type {Object.}\n */\n this._edgeActivatedHatValues = {};\n }\n\n /**\n * Called when the project receives a \"green flag.\"\n * @abstract\n */\n onGreenFlag () {}\n\n /**\n * Return a human-readable name for this target.\n * Target implementations should override this.\n * @abstract\n * @returns {string} Human-readable name for the target.\n */\n getName () {\n return this.id;\n }\n\n /**\n * Update an edge-activated hat block value.\n * @param {!string} blockId ID of hat to store value for.\n * @param {*} newValue Value to store for edge-activated hat.\n * @return {*} The old value for the edge-activated hat.\n */\n updateEdgeActivatedValue (blockId, newValue) {\n const oldValue = this._edgeActivatedHatValues[blockId];\n this._edgeActivatedHatValues[blockId] = newValue;\n return oldValue;\n }\n\n hasEdgeActivatedValue (blockId) {\n return Object.prototype.hasOwnProperty.call(this._edgeActivatedHatValues, blockId);\n }\n\n /**\n * Clear all edge-activaed hat values.\n */\n clearEdgeActivatedValues () {\n this._edgeActivatedHatValues = {};\n }\n\n /**\n * Look up a variable object, first by id, and then by name if the id is not found.\n * Create a new variable if both lookups fail.\n * @param {string} id Id of the variable.\n * @param {string} name Name of the variable.\n * @return {!Variable} Variable object.\n */\n lookupOrCreateVariable (id, name) {\n let variable = this.lookupVariableById(id);\n if (variable) return variable;\n\n variable = this.lookupVariableByNameAndType(name, Variable.SCALAR_TYPE);\n if (variable) return variable;\n\n // No variable with this name exists - create it locally.\n const newVariable = new Variable(id, name, Variable.SCALAR_TYPE, false);\n this.variables[id] = newVariable;\n return newVariable;\n }\n\n /**\n * Look up a broadcast message object with the given id and return it\n * if it exists.\n * @param {string} id Id of the variable.\n * @param {string} name Name of the variable.\n * @return {?Variable} Variable object.\n */\n lookupBroadcastMsg (id, name) {\n let broadcastMsg;\n if (id) {\n broadcastMsg = this.lookupVariableById(id);\n } else if (name) {\n broadcastMsg = this.lookupBroadcastByInputValue(name);\n } else {\n log.error('Cannot find broadcast message if neither id nor name are provided.');\n }\n if (broadcastMsg) {\n if (name && (broadcastMsg.name.toLowerCase() !== name.toLowerCase())) {\n log.error(`Found broadcast message with id: ${id}, but` +\n `its name, ${broadcastMsg.name} did not match expected name ${name}.`);\n }\n if (broadcastMsg.type !== Variable.BROADCAST_MESSAGE_TYPE) {\n log.error(`Found variable with id: ${id}, but its type ${broadcastMsg.type}` +\n `did not match expected type ${Variable.BROADCAST_MESSAGE_TYPE}`);\n }\n return broadcastMsg;\n }\n }\n\n /**\n * Look up a broadcast message with the given name and return the variable\n * if it exists. Does not create a new broadcast message variable if\n * it doesn't exist.\n * @param {string} name Name of the variable.\n * @return {?Variable} Variable object.\n */\n lookupBroadcastByInputValue (name) {\n const vars = this.variables;\n for (const propName in vars) {\n if ((vars[propName].type === Variable.BROADCAST_MESSAGE_TYPE) &&\n (vars[propName].name.toLowerCase() === name.toLowerCase())) {\n return vars[propName];\n }\n }\n }\n\n /**\n * Look up a variable object.\n * Search begins for local variables; then look for globals.\n * @param {string} id Id of the variable.\n * @param {string} name Name of the variable.\n * @return {!Variable} Variable object.\n */\n lookupVariableById (id) {\n // If we have a local copy, return it.\n if (Object.prototype.hasOwnProperty.call(this.variables, id)) {\n return this.variables[id];\n }\n // If the stage has a global copy, return it.\n if (this.runtime && !this.isStage) {\n const stage = this.runtime.getTargetForStage();\n if (stage && Object.prototype.hasOwnProperty.call(stage.variables, id)) {\n return stage.variables[id];\n }\n }\n }\n\n /**\n * Look up a variable object by its name and variable type.\n * Search begins with local variables; then global variables if a local one\n * was not found.\n * @param {string} name Name of the variable.\n * @param {string} type Type of the variable. Defaults to Variable.SCALAR_TYPE.\n * @param {?bool} skipStage Optional flag to skip checking the stage\n * @return {?Variable} Variable object if found, or null if not.\n */\n lookupVariableByNameAndType (name, type, skipStage) {\n if (typeof name !== 'string') return;\n if (typeof type !== 'string') type = Variable.SCALAR_TYPE;\n skipStage = skipStage || false;\n\n for (const varId in this.variables) {\n const currVar = this.variables[varId];\n if (currVar.name === name && currVar.type === type) {\n return currVar;\n }\n }\n\n if (!skipStage && this.runtime && !this.isStage) {\n const stage = this.runtime.getTargetForStage();\n if (stage) {\n for (const varId in stage.variables) {\n const currVar = stage.variables[varId];\n if (currVar.name === name && currVar.type === type) {\n return currVar;\n }\n }\n }\n }\n\n return null;\n }\n\n /**\n * Look up a list object for this target, and create it if one doesn't exist.\n * Search begins for local lists; then look for globals.\n * @param {!string} id Id of the list.\n * @param {!string} name Name of the list.\n * @return {!Varible} Variable object representing the found/created list.\n */\n lookupOrCreateList (id, name) {\n let list = this.lookupVariableById(id);\n if (list) return list;\n\n list = this.lookupVariableByNameAndType(name, Variable.LIST_TYPE);\n if (list) return list;\n\n // No variable with this name exists - create it locally.\n const newList = new Variable(id, name, Variable.LIST_TYPE, false);\n this.variables[id] = newList;\n return newList;\n }\n\n /**\n * Creates a variable with the given id and name and adds it to the\n * dictionary of variables.\n * @param {string} id Id of variable\n * @param {string} name Name of variable.\n * @param {string} type Type of variable, '', 'broadcast_msg', or 'list'\n * @param {boolean} isCloud Whether the variable to create has the isCloud flag set.\n * Additional checks are made that the variable can be created as a cloud variable.\n */\n createVariable (id, name, type, isCloud) {\n if (!Object.prototype.hasOwnProperty.call(this.variables, id)) {\n const newVariable = new Variable(id, name, type, false);\n if (isCloud && this.isStage && this.runtime.canAddCloudVariable()) {\n newVariable.isCloud = true;\n this.runtime.addCloudVariable();\n this.runtime.ioDevices.cloud.requestCreateVariable(newVariable);\n }\n this.variables[id] = newVariable;\n }\n }\n\n /**\n * Creates a comment with the given properties.\n * @param {string} id Id of the comment.\n * @param {string} blockId Optional id of the block the comment is attached\n * to if it is a block comment.\n * @param {string} text The text the comment contains.\n * @param {number} x The x coordinate of the comment on the workspace.\n * @param {number} y The y coordinate of the comment on the workspace.\n * @param {number} width The width of the comment when it is full size\n * @param {number} height The height of the comment when it is full size\n * @param {boolean} minimized Whether the comment is minimized.\n */\n createComment (id, blockId, text, x, y, width, height, minimized) {\n if (!Object.prototype.hasOwnProperty.call(this.comments, id)) {\n const newComment = new Comment(id, text, x, y,\n width, height, minimized);\n if (blockId) {\n newComment.blockId = blockId;\n const blockWithComment = this.blocks.getBlock(blockId);\n if (blockWithComment) {\n blockWithComment.comment = id;\n } else {\n log.warn(`Could not find block with id ${blockId\n } associated with commentId: ${id}`);\n }\n }\n this.comments[id] = newComment;\n }\n }\n\n /**\n * Renames the variable with the given id to newName.\n * @param {string} id Id of variable to rename.\n * @param {string} newName New name for the variable.\n */\n renameVariable (id, newName) {\n if (Object.prototype.hasOwnProperty.call(this.variables, id)) {\n const variable = this.variables[id];\n if (variable.id === id) {\n const oldName = variable.name;\n variable.name = newName;\n\n if (this.runtime) {\n if (variable.isCloud && this.isStage) {\n this.runtime.ioDevices.cloud.requestRenameVariable(oldName, newName);\n }\n\n if (variable.type === Variable.SCALAR_TYPE) {\n // sensing__of may be referencing to this variable.\n // Change the reference.\n let blockUpdated = false;\n this.runtime.targets.forEach(t => {\n blockUpdated = t.blocks.updateSensingOfReference(\n oldName,\n newName,\n this.isStage ? '_stage_' : this.getName()\n ) || blockUpdated;\n });\n // Request workspace change only if sensing_of blocks were actually updated.\n if (blockUpdated) this.runtime.requestBlocksUpdate();\n }\n\n const blocks = this.runtime.monitorBlocks;\n blocks.changeBlock({\n id: id,\n element: 'field',\n name: variable.type === Variable.LIST_TYPE ? 'LIST' : 'VARIABLE',\n value: id\n }, this.runtime);\n const monitorBlock = blocks.getBlock(variable.id);\n if (monitorBlock) {\n this.runtime.requestUpdateMonitor(Map({\n id: id,\n params: blocks._getBlockParams(monitorBlock)\n }));\n }\n }\n\n }\n }\n }\n\n /**\n * Removes the variable with the given id from the dictionary of variables.\n * @param {string} id Id of variable to delete.\n */\n deleteVariable (id) {\n if (Object.prototype.hasOwnProperty.call(this.variables, id)) {\n // Get info about the variable before deleting it\n const deletedVariableName = this.variables[id].name;\n const deletedVariableWasCloud = this.variables[id].isCloud;\n delete this.variables[id];\n if (this.runtime) {\n if (deletedVariableWasCloud && this.isStage) {\n this.runtime.ioDevices.cloud.requestDeleteVariable(deletedVariableName);\n this.runtime.removeCloudVariable();\n }\n this.runtime.monitorBlocks.deleteBlock(id);\n this.runtime.requestRemoveMonitor(id);\n }\n }\n }\n\n /**\n * Remove this target's monitors from the runtime state and remove the\n * target-specific monitored blocks (e.g. local variables, global variables for the stage, x-position).\n * NOTE: This does not delete any of the stage monitors like backdrop name.\n */\n deleteMonitors () {\n this.runtime.requestRemoveMonitorByTargetId(this.id);\n let targetSpecificMonitorBlockIds;\n if (this.isStage) {\n // This only deletes global variables and not other stage monitors like backdrop number.\n targetSpecificMonitorBlockIds = Object.keys(this.variables);\n } else {\n targetSpecificMonitorBlockIds = Object.keys(this.runtime.monitorBlocks._blocks)\n .filter(key => this.runtime.monitorBlocks._blocks[key].targetId === this.id);\n }\n for (const blockId of targetSpecificMonitorBlockIds) {\n this.runtime.monitorBlocks.deleteBlock(blockId);\n }\n }\n\n /**\n * Create a clone of the variable with the given id from the dictionary of\n * this target's variables.\n * @param {string} id Id of variable to duplicate.\n * @param {boolean=} optKeepOriginalId Optional flag to keep the original variable ID\n * for the duplicate variable. This is necessary when cloning a sprite, for example.\n * @return {?Variable} The duplicated variable, or null if\n * the original variable was not found.\n */\n duplicateVariable (id, optKeepOriginalId) {\n if (Object.prototype.hasOwnProperty.call(this.variables, id)) {\n const originalVariable = this.variables[id];\n const newVariable = new Variable(\n optKeepOriginalId ? id : null, // conditionally keep original id or generate a new one\n originalVariable.name,\n originalVariable.type,\n originalVariable.isCloud\n );\n if (newVariable.type === Variable.LIST_TYPE) {\n newVariable.value = originalVariable.value.slice(0);\n } else {\n newVariable.value = originalVariable.value;\n }\n return newVariable;\n }\n return null;\n }\n\n /**\n * Duplicate the dictionary of this target's variables as part of duplicating.\n * this target or making a clone.\n * @param {object=} optBlocks Optional block container for the target being duplicated.\n * If provided, new variables will be generated with new UIDs and any variable references\n * in this blocks container will be updated to refer to the corresponding new IDs.\n * @return {object} The duplicated dictionary of variables\n */\n duplicateVariables (optBlocks) {\n let allVarRefs;\n if (optBlocks) {\n allVarRefs = optBlocks.getAllVariableAndListReferences();\n }\n return Object.keys(this.variables).reduce((accum, varId) => {\n const newVariable = this.duplicateVariable(varId, !optBlocks);\n accum[newVariable.id] = newVariable;\n if (optBlocks && allVarRefs) {\n const currVarRefs = allVarRefs[varId];\n if (currVarRefs) {\n this.mergeVariables(varId, newVariable.id, currVarRefs);\n }\n }\n return accum;\n }, {});\n }\n\n /**\n * Post/edit sprite info.\n * @param {object} data An object with sprite info data to set.\n * @abstract\n */\n postSpriteInfo () {}\n\n /**\n * Retrieve custom state associated with this target and the provided state ID.\n * @param {string} stateId - specify which piece of state to retrieve.\n * @returns {*} the associated state, if any was found.\n */\n getCustomState (stateId) {\n return this._customState[stateId];\n }\n\n /**\n * Store custom state associated with this target and the provided state ID.\n * @param {string} stateId - specify which piece of state to store on this target.\n * @param {*} newValue - the state value to store.\n */\n setCustomState (stateId, newValue) {\n this._customState[stateId] = newValue;\n }\n\n /**\n * Call to destroy a target.\n * @abstract\n */\n dispose () {\n this._customState = {};\n\n if (this.runtime) {\n this.runtime.removeExecutable(this);\n }\n }\n\n // Variable Conflict Resolution Helpers\n\n /**\n * Get the names of all the variables of the given type that are in scope for this target.\n * For targets that are not the stage, this includes any target-specific\n * variables as well as any stage variables unless the skipStage flag is true.\n * For the stage, this is all stage variables.\n * @param {string} type The variable type to search for; defaults to Variable.SCALAR_TYPE\n * @param {?bool} skipStage Optional flag to skip the stage.\n * @return {Array} A list of variable names\n */\n getAllVariableNamesInScopeByType (type, skipStage) {\n if (typeof type !== 'string') type = Variable.SCALAR_TYPE;\n skipStage = skipStage || false;\n const targetVariables = Object.values(this.variables)\n .filter(v => v.type === type)\n .map(variable => variable.name);\n if (skipStage || this.isStage || !this.runtime) {\n return targetVariables;\n }\n const stage = this.runtime.getTargetForStage();\n const stageVariables = stage.getAllVariableNamesInScopeByType(type);\n return targetVariables.concat(stageVariables);\n }\n\n /**\n * Merge variable references with another variable.\n * @param {string} idToBeMerged ID of the variable whose references need to be updated\n * @param {string} idToMergeWith ID of the variable that the old references should be replaced with\n * @param {?Array} optReferencesToUpdate Optional context of the change.\n * Defaults to all the blocks in this target.\n * @param {?string} optNewName New variable name to merge with. The old\n * variable name in the references being updated should be replaced with this new name.\n * If this parameter is not provided or is '', no name change occurs.\n */\n mergeVariables (idToBeMerged, idToMergeWith, optReferencesToUpdate, optNewName) {\n const referencesToChange = optReferencesToUpdate ||\n // TODO should there be a separate helper function that traverses the blocks\n // for all references for a given ID instead of doing the below..?\n this.blocks.getAllVariableAndListReferences()[idToBeMerged];\n\n VariableUtil.updateVariableIdentifiers(referencesToChange, idToMergeWith, optNewName);\n }\n\n /**\n * Share a local variable (and given references for that variable) to the stage.\n * @param {string} varId The ID of the variable to share.\n * @param {Array} varRefs The list of variable references being shared,\n * that reference the given variable ID. The names and IDs of these variable\n * references will be updated to refer to the new (or pre-existing) global variable.\n */\n shareLocalVariableToStage (varId, varRefs) {\n if (!this.runtime) return;\n const variable = this.variables[varId];\n if (!variable) {\n log.warn(`Cannot share a local variable to the stage if it's not local.`);\n return;\n }\n const stage = this.runtime.getTargetForStage();\n // If a local var is being shared with the stage,\n // sharing will make the variable global, resulting in a conflict\n // with the existing local variable. Preemptively Resolve this conflict\n // by renaming the new global variable.\n\n // First check if we've already done the local to global transition for this\n // variable. If we have, merge it with the global variable we've already created.\n const varIdForStage = `StageVarFromLocal_${varId}`;\n let stageVar = stage.lookupVariableById(varIdForStage);\n // If a global var doesn't already exist, create a new one with a fresh name.\n // Use the ID we created above so that we can lookup this new variable in the\n // future if we decide to share this same variable again.\n if (!stageVar) {\n const varName = variable.name;\n const varType = variable.type;\n\n const newStageName = `Stage: ${varName}`;\n stageVar = this.runtime.createNewGlobalVariable(newStageName, varIdForStage, varType);\n }\n // Update all variable references to use the new name and ID\n this.mergeVariables(varId, stageVar.id, varRefs, stageVar.name);\n }\n\n /**\n * Share a local variable with a sprite, merging with one of the same name and\n * type if it already exists on the sprite, or create a new one.\n * @param {string} varId Id of the variable to share\n * @param {Target} sprite The sprite to share the variable with\n * @param {Array} varRefs A list of all the variable references currently being shared.\n */\n shareLocalVariableToSprite (varId, sprite, varRefs) {\n if (!this.runtime) return;\n if (this.isStage) return;\n const variable = this.variables[varId];\n if (!variable) {\n log.warn(`Tried to call 'shareLocalVariableToSprite' with a non-local variable.`);\n return;\n }\n const varName = variable.name;\n const varType = variable.type;\n // Check if the receiving sprite already has a variable of the same name and type\n // and use the existing variable, otherwise create a new one.\n const existingLocalVar = sprite.lookupVariableByNameAndType(varName, varType);\n let newVarId;\n if (existingLocalVar) {\n newVarId = existingLocalVar.id;\n } else {\n const newVar = new Variable(null, varName, varType);\n newVarId = newVar.id;\n sprite.variables[newVarId] = newVar;\n }\n\n // Merge with the local variable on the new sprite.\n this.mergeVariables(varId, newVarId, varRefs);\n }\n\n /**\n * Given a list of variable referencing fields, shares those variables with\n * the target with the provided id, resolving any variable conflicts that arise\n * using the following rules:\n *\n * If this target is the stage, exit. There are no conflicts that arise\n * from sharing variables from the stage to another sprite. The variables\n * already exist globally, so no further action is needed.\n *\n * If a variable being referenced is a global variable, do nothing. The\n * global variable already exists so no further action is needed.\n *\n * If a variable being referenced is local, and\n * 1) The receiving target is a sprite:\n * create a new local variable or merge with an existing local variable\n * of the same name and type. Update all the referencing fields\n * for the original variable to reference the new variable.\n * 2) The receiving target is the stage:\n * Create a new global variable with a fresh name and update all the referencing\n * fields to reference the new variable.\n *\n * @param {Array} blocks The blocks containing\n * potential conflicting references to variables.\n * @param {Target} receivingTarget The target receiving the variables\n */\n resolveVariableSharingConflictsWithTarget (blocks, receivingTarget) {\n if (this.isStage) return;\n\n // Get all the variable references in the given list of blocks\n const allVarListRefs = this.blocks.getAllVariableAndListReferences(blocks);\n\n // For all the variables being referenced, check for which ones are local\n // to this target, and resolve conflicts based on whether the receiving target\n // is a sprite (with a conflicting local variable) or whether it is\n // the stage (which cannot have local variables)\n for (const varId in allVarListRefs) {\n const currVar = this.variables[varId];\n if (!currVar) continue; // The current variable is global, there shouldn't be any conflicts here, skip it.\n\n // Get the list of references for the current variable id\n const currVarListRefs = allVarListRefs[varId];\n\n if (receivingTarget.isStage) {\n this.shareLocalVariableToStage(varId, currVarListRefs);\n } else {\n this.shareLocalVariableToSprite(varId, receivingTarget, currVarListRefs);\n }\n }\n }\n\n /**\n * Fixes up variable references in this target avoiding conflicts with\n * pre-existing variables in the same scope.\n * This is used when uploading this target as a new sprite into an existing\n * project, where the new sprite may contain references\n * to variable names that already exist as global variables in the project\n * (and thus are in scope for variable references in the given sprite).\n *\n * If this target has a block that references an existing global variable and that\n * variable *does not* exist in this target (e.g. it was a global variable in the\n * project the sprite was originally exported from), merge the variables. This entails\n * fixing the variable references in this sprite to reference the id of the pre-existing global variable.\n *\n * If this target has a block that references an existing global variable and that\n * variable does exist in the target itself (e.g. it's a local variable in the sprite being uploaded),\n * then the local variable is renamed to distinguish itself from the pre-existing variable.\n * All blocks that reference the local variable will be updated to use the new name.\n */\n // TODO (#1360) This function is too long, add some helpers for the different chunks and cases...\n fixUpVariableReferences () {\n if (!this.runtime) return; // There's no runtime context to conflict with\n if (this.isStage) return; // Stage can't have variable conflicts with itself (and also can't be uploaded)\n const stage = this.runtime.getTargetForStage();\n if (!stage || !stage.variables) return;\n\n const renameConflictingLocalVar = (id, name, type) => {\n const conflict = stage.lookupVariableByNameAndType(name, type);\n if (conflict) {\n const newName = StringUtil.unusedName(\n `${this.getName()}: ${name}`,\n this.getAllVariableNamesInScopeByType(type));\n this.renameVariable(id, newName);\n return newName;\n }\n return null;\n };\n\n const allReferences = this.blocks.getAllVariableAndListReferences();\n const unreferencedLocalVarIds = [];\n if (Object.keys(this.variables).length > 0) {\n for (const localVarId in this.variables) {\n if (!Object.prototype.hasOwnProperty.call(this.variables, localVarId)) continue;\n if (!allReferences[localVarId]) unreferencedLocalVarIds.push(localVarId);\n }\n }\n const conflictIdsToReplace = Object.create(null);\n const conflictNamesToReplace = Object.create(null);\n\n // Cache the list of all variable names by type so that we don't need to\n // re-calculate this in every iteration of the following loop.\n const varNamesByType = {};\n const allVarNames = type => {\n const namesOfType = varNamesByType[type];\n if (namesOfType) return namesOfType;\n varNamesByType[type] = this.runtime.getAllVarNamesOfType(type);\n return varNamesByType[type];\n };\n\n for (const varId in allReferences) {\n // We don't care about which var ref we get, they should all have the same var info\n const varRef = allReferences[varId][0];\n const varName = varRef.referencingField.value;\n const varType = varRef.type;\n if (this.lookupVariableById(varId)) {\n // Found a variable with the id in either the target or the stage,\n // figure out which one.\n if (Object.prototype.hasOwnProperty.call(this.variables, varId)) {\n // If the target has the variable, then check whether the stage\n // has one with the same name and type. If it does, then rename\n // this target specific variable so that there is a distinction.\n const newVarName = renameConflictingLocalVar(varId, varName, varType);\n\n if (newVarName) {\n // We are not calling this.blocks.updateBlocksAfterVarRename\n // here because it will search through all the blocks. We already\n // have access to all the references for this var id.\n allReferences[varId].map(ref => {\n ref.referencingField.value = newVarName;\n return ref;\n });\n }\n }\n } else {\n // We didn't find the referenced variable id anywhere,\n // Treat it as a reference to a global variable (from the original\n // project this sprite was exported from).\n // Check for whether a global variable of the same name and type exists,\n // and if so, track it to merge with the existing global in a second pass of the blocks.\n const existingVar = stage.lookupVariableByNameAndType(varName, varType);\n if (existingVar) {\n if (!conflictIdsToReplace[varId]) {\n conflictIdsToReplace[varId] = existingVar.id;\n }\n } else {\n // A global variable with the same name did not already exist,\n // create a new one such that it does not conflict with any\n // names of local variables of the same type.\n const allNames = allVarNames(varType);\n const freshName = StringUtil.unusedName(varName, allNames);\n stage.createVariable(varId, freshName, varType);\n if (!conflictNamesToReplace[varId]) {\n conflictNamesToReplace[varId] = freshName;\n }\n }\n }\n }\n // Rename any local variables that were missed above because they aren't\n // referenced by any blocks\n for (const id in unreferencedLocalVarIds) {\n const varId = unreferencedLocalVarIds[id];\n const name = this.variables[varId].name;\n const type = this.variables[varId].type;\n renameConflictingLocalVar(varId, name, type);\n }\n // Handle global var conflicts with existing global vars (e.g. a sprite is uploaded, and has\n // blocks referencing some variable that the sprite does not own, and this\n // variable conflicts with a global var)\n // In this case, we want to merge the new variable referenes with the\n // existing global variable\n for (const conflictId in conflictIdsToReplace) {\n const existingId = conflictIdsToReplace[conflictId];\n const referencesToUpdate = allReferences[conflictId];\n this.mergeVariables(conflictId, existingId, referencesToUpdate);\n }\n\n // Handle global var conflicts existing local vars (e.g a sprite is uploaded,\n // and has blocks referencing some variable that the sprite does not own, and this\n // variable conflcits with another sprite's local var).\n // In this case, we want to go through the variable references and update\n // the name of the variable in that reference.\n for (const conflictId in conflictNamesToReplace) {\n const newName = conflictNamesToReplace[conflictId];\n const referencesToUpdate = allReferences[conflictId];\n referencesToUpdate.map(ref => {\n ref.referencingField.value = newName;\n return ref;\n });\n }\n }\n\n}\n\nmodule.exports = Target;\n","/**\n * Recycle bin for empty stackFrame objects\n * @type Array<_StackFrame>\n */\nconst _stackFrameFreeList = [];\n\n/**\n * A frame used for each level of the stack. A general purpose\n * place to store a bunch of execution context and parameters\n * @param {boolean} warpMode Whether this level of the stack is warping\n * @constructor\n * @private\n */\nclass _StackFrame {\n constructor (warpMode) {\n /**\n * Whether this level of the stack is a loop.\n * @type {boolean}\n */\n this.isLoop = false;\n\n /**\n * Whether this level is in warp mode. Is set by some legacy blocks and\n * \"turbo mode\"\n * @type {boolean}\n */\n this.warpMode = warpMode;\n\n /**\n * Reported value from just executed block.\n * @type {Any}\n */\n this.justReported = null;\n\n /**\n * The active block that is waiting on a promise.\n * @type {string}\n */\n this.reporting = '';\n\n /**\n * Persists reported inputs during async block.\n * @type {Object}\n */\n this.reported = null;\n\n /**\n * Name of waiting reporter.\n * @type {string}\n */\n this.waitingReporter = null;\n\n /**\n * Procedure parameters.\n * @type {Object}\n */\n this.params = null;\n\n /**\n * A context passed to block implementations.\n * @type {Object}\n */\n this.executionContext = null;\n }\n\n /**\n * Reset all properties of the frame to pristine null and false states.\n * Used to recycle.\n * @return {_StackFrame} this\n */\n reset () {\n\n this.isLoop = false;\n this.warpMode = false;\n this.justReported = null;\n this.reported = null;\n this.waitingReporter = null;\n this.params = null;\n this.executionContext = null;\n\n return this;\n }\n\n /**\n * Reuse an active stack frame in the stack.\n * @param {?boolean} warpMode defaults to current warpMode\n * @returns {_StackFrame} this\n */\n reuse (warpMode = this.warpMode) {\n this.reset();\n this.warpMode = Boolean(warpMode);\n return this;\n }\n\n /**\n * Create or recycle a stack frame object.\n * @param {boolean} warpMode Enable warpMode on this frame.\n * @returns {_StackFrame} The clean stack frame with correct warpMode setting.\n */\n static create (warpMode) {\n const stackFrame = _stackFrameFreeList.pop();\n if (typeof stackFrame !== 'undefined') {\n stackFrame.warpMode = Boolean(warpMode);\n return stackFrame;\n }\n return new _StackFrame(warpMode);\n }\n\n /**\n * Put a stack frame object into the recycle bin for reuse.\n * @param {_StackFrame} stackFrame The frame to reset and recycle.\n */\n static release (stackFrame) {\n if (typeof stackFrame !== 'undefined') {\n _stackFrameFreeList.push(stackFrame.reset());\n }\n }\n}\n\n/**\n * A thread is a running stack context and all the metadata needed.\n * @param {?string} firstBlock First block to execute in the thread.\n * @constructor\n */\nclass Thread {\n constructor (firstBlock) {\n /**\n * ID of top block of the thread\n * @type {!string}\n */\n this.topBlock = firstBlock;\n\n /**\n * Stack for the thread. When the sequencer enters a control structure,\n * the block is pushed onto the stack so we know where to exit.\n * @type {Array.}\n */\n this.stack = [];\n\n /**\n * Stack frames for the thread. Store metadata for the executing blocks.\n * @type {Array.<_StackFrame>}\n */\n this.stackFrames = [];\n\n /**\n * Status of the thread, one of three states (below)\n * @type {number}\n */\n this.status = 0; /* Thread.STATUS_RUNNING */\n\n /**\n * Whether the thread is killed in the middle of execution.\n * @type {boolean}\n */\n this.isKilled = false;\n\n /**\n * Target of this thread.\n * @type {?Target}\n */\n this.target = null;\n\n /**\n * The Blocks this thread will execute.\n * @type {Blocks}\n */\n this.blockContainer = null;\n\n /**\n * Whether the thread requests its script to glow during this frame.\n * @type {boolean}\n */\n this.requestScriptGlowInFrame = false;\n\n /**\n * Which block ID should glow during this frame, if any.\n * @type {?string}\n */\n this.blockGlowInFrame = null;\n\n /**\n * A timer for when the thread enters warp mode.\n * Substitutes the sequencer's count toward WORK_TIME on a per-thread basis.\n * @type {?Timer}\n */\n this.warpTimer = null;\n\n this.justReported = null;\n }\n\n /**\n * Thread status for initialized or running thread.\n * This is the default state for a thread - execution should run normally,\n * stepping from block to block.\n * @const\n */\n static get STATUS_RUNNING () {\n return 0;\n }\n\n /**\n * Threads are in this state when a primitive is waiting on a promise;\n * execution is paused until the promise changes thread status.\n * @const\n */\n static get STATUS_PROMISE_WAIT () {\n return 1;\n }\n\n /**\n * Thread status for yield.\n * @const\n */\n static get STATUS_YIELD () {\n return 2;\n }\n\n /**\n * Thread status for a single-tick yield. This will be cleared when the\n * thread is resumed.\n * @const\n */\n static get STATUS_YIELD_TICK () {\n return 3;\n }\n\n /**\n * Thread status for a finished/done thread.\n * Thread is in this state when there are no more blocks to execute.\n * @const\n */\n static get STATUS_DONE () {\n return 4;\n }\n\n /**\n * Push stack and update stack frames appropriately.\n * @param {string} blockId Block ID to push to stack.\n */\n pushStack (blockId) {\n this.stack.push(blockId);\n // Push an empty stack frame, if we need one.\n // Might not, if we just popped the stack.\n if (this.stack.length > this.stackFrames.length) {\n const parent = this.stackFrames[this.stackFrames.length - 1];\n this.stackFrames.push(_StackFrame.create(typeof parent !== 'undefined' && parent.warpMode));\n }\n }\n\n /**\n * Reset the stack frame for use by the next block.\n * (avoids popping and re-pushing a new stack frame - keeps the warpmode the same\n * @param {string} blockId Block ID to push to stack.\n */\n reuseStackForNextBlock (blockId) {\n this.stack[this.stack.length - 1] = blockId;\n this.stackFrames[this.stackFrames.length - 1].reuse();\n }\n\n /**\n * Pop last block on the stack and its stack frame.\n * @return {string} Block ID popped from the stack.\n */\n popStack () {\n _StackFrame.release(this.stackFrames.pop());\n return this.stack.pop();\n }\n\n /**\n * Pop back down the stack frame until we hit a procedure call or the stack frame is emptied\n */\n stopThisScript () {\n let blockID = this.peekStack();\n while (blockID !== null) {\n const block = this.target.blocks.getBlock(blockID);\n if (typeof block !== 'undefined' && block.opcode === 'procedures_call') {\n break;\n }\n this.popStack();\n blockID = this.peekStack();\n }\n\n if (this.stack.length === 0) {\n // Clean up!\n this.requestScriptGlowInFrame = false;\n this.status = Thread.STATUS_DONE;\n }\n }\n\n /**\n * Get top stack item.\n * @return {?string} Block ID on top of stack.\n */\n peekStack () {\n return this.stack.length > 0 ? this.stack[this.stack.length - 1] : null;\n }\n\n\n /**\n * Get top stack frame.\n * @return {?object} Last stack frame stored on this thread.\n */\n peekStackFrame () {\n return this.stackFrames.length > 0 ? this.stackFrames[this.stackFrames.length - 1] : null;\n }\n\n /**\n * Get stack frame above the current top.\n * @return {?object} Second to last stack frame stored on this thread.\n */\n peekParentStackFrame () {\n return this.stackFrames.length > 1 ? this.stackFrames[this.stackFrames.length - 2] : null;\n }\n\n /**\n * Push a reported value to the parent of the current stack frame.\n * @param {*} value Reported value to push.\n */\n pushReportedValue (value) {\n this.justReported = typeof value === 'undefined' ? null : value;\n }\n\n /**\n * Initialize procedure parameters on this stack frame.\n */\n initParams () {\n const stackFrame = this.peekStackFrame();\n if (stackFrame.params === null) {\n stackFrame.params = {};\n }\n }\n\n /**\n * Add a parameter to the stack frame.\n * Use when calling a procedure with parameter values.\n * @param {!string} paramName Name of parameter.\n * @param {*} value Value to set for parameter.\n */\n pushParam (paramName, value) {\n const stackFrame = this.peekStackFrame();\n stackFrame.params[paramName] = value;\n }\n\n /**\n * Get a parameter at the lowest possible level of the stack.\n * @param {!string} paramName Name of parameter.\n * @return {*} value Value for parameter.\n */\n getParam (paramName) {\n for (let i = this.stackFrames.length - 1; i >= 0; i--) {\n const frame = this.stackFrames[i];\n if (frame.params === null) {\n continue;\n }\n if (Object.prototype.hasOwnProperty.call(frame.params, paramName)) {\n return frame.params[paramName];\n }\n return null;\n }\n return null;\n }\n\n /**\n * Whether the current execution of a thread is at the top of the stack.\n * @return {boolean} True if execution is at top of the stack.\n */\n atStackTop () {\n return this.peekStack() === this.topBlock;\n }\n\n\n /**\n * Switch the thread to the next block at the current level of the stack.\n * For example, this is used in a standard sequence of blocks,\n * where execution proceeds from one block to the next.\n */\n goToNextBlock () {\n const nextBlockId = this.target.blocks.getNextBlock(this.peekStack());\n this.reuseStackForNextBlock(nextBlockId);\n }\n\n /**\n * Attempt to determine whether a procedure call is recursive,\n * by examining the stack.\n * @param {!string} procedureCode Procedure code of procedure being called.\n * @return {boolean} True if the call appears recursive.\n */\n isRecursiveCall (procedureCode) {\n let callCount = 5; // Max number of enclosing procedure calls to examine.\n const sp = this.stack.length - 1;\n for (let i = sp - 1; i >= 0; i--) {\n const block = this.target.blocks.getBlock(this.stack[i]);\n if (block.opcode === 'procedures_call' &&\n block.mutation.proccode === procedureCode) {\n return true;\n }\n if (--callCount < 0) return false;\n }\n return false;\n }\n}\n\nmodule.exports = Thread;\n","/**\n * @fileoverview\n * Object representing a Scratch variable.\n */\n\nconst uid = require('../util/uid');\nconst xmlEscape = require('../util/xml-escape');\n\nclass Variable {\n /**\n * @param {string} id Id of the variable.\n * @param {string} name Name of the variable.\n * @param {string} type Type of the variable, one of '' or 'list'\n * @param {boolean} isCloud Whether the variable is stored in the cloud.\n * @constructor\n */\n constructor (id, name, type, isCloud) {\n this.id = id || uid();\n this.name = name;\n this.type = type;\n this.isCloud = isCloud;\n switch (this.type) {\n case Variable.SCALAR_TYPE:\n this.value = 0;\n break;\n case Variable.LIST_TYPE:\n this.value = [];\n break;\n case Variable.BROADCAST_MESSAGE_TYPE:\n this.value = this.name;\n break;\n default:\n throw new Error(`Invalid variable type: ${this.type}`);\n }\n }\n\n toXML (isLocal) {\n isLocal = (isLocal === true);\n return `${xmlEscape(this.name)}`;\n }\n\n /**\n * Type representation for scalar variables.\n * This is currently represented as ''\n * for compatibility with blockly.\n * @const {string}\n */\n static get SCALAR_TYPE () {\n return '';\n }\n\n /**\n * Type representation for list variables.\n * @const {string}\n */\n static get LIST_TYPE () {\n return 'list';\n }\n\n /**\n * Type representation for list variables.\n * @const {string}\n */\n static get BROADCAST_MESSAGE_TYPE () {\n return 'broadcast_msg';\n }\n}\n\nmodule.exports = Variable;\n","/**\n * Block argument types\n * @enum {string}\n */\nconst ArgumentType = {\n /**\n * Numeric value with angle picker\n */\n ANGLE: 'angle',\n\n /**\n * Boolean value with hexagonal placeholder\n */\n BOOLEAN: 'Boolean',\n\n /**\n * Numeric value with color picker\n */\n COLOR: 'color',\n\n /**\n * Numeric value with text field\n */\n NUMBER: 'number',\n\n /**\n * String value with text field\n */\n STRING: 'string',\n\n /**\n * String value with matrix field\n */\n MATRIX: 'matrix',\n\n /**\n * MIDI note number with note picker (piano) field\n */\n NOTE: 'note',\n\n /**\n * Inline image on block (as part of the label)\n */\n IMAGE: 'image'\n};\n\nmodule.exports = ArgumentType;\n","/**\n * Types of block\n * @enum {string}\n */\nconst BlockType = {\n /**\n * Boolean reporter with hexagonal shape\n */\n BOOLEAN: 'Boolean',\n\n /**\n * A button (not an actual block) for some special action, like making a variable\n */\n BUTTON: 'button',\n\n /**\n * Command block\n */\n COMMAND: 'command',\n\n /**\n * Specialized command block which may or may not run a child branch\n * The thread continues with the next block whether or not a child branch ran.\n */\n CONDITIONAL: 'conditional',\n\n /**\n * Specialized hat block with no implementation function\n * This stack only runs if the corresponding event is emitted by other code.\n */\n EVENT: 'event',\n\n /**\n * Hat block which conditionally starts a block stack\n */\n HAT: 'hat',\n\n /**\n * Specialized command block which may or may not run a child branch\n * If a child branch runs, the thread evaluates the loop block again.\n */\n LOOP: 'loop',\n\n /**\n * General reporter with numeric or string value\n */\n REPORTER: 'reporter'\n};\n\nmodule.exports = BlockType;\n","const dispatch = require('../dispatch/central-dispatch');\nconst log = require('../util/log');\nconst maybeFormatMessage = require('../util/maybe-format-message');\n\nconst BlockType = require('./block-type');\n\n// These extensions are currently built into the VM repository but should not be loaded at startup.\n// TODO: move these out into a separate repository?\n// TODO: change extension spec so that library info, including extension ID, can be collected through static methods\n\nconst builtinExtensions = {\n // This is an example that isn't loaded with the other core blocks,\n // but serves as a reference for loading core blocks as extensions.\n coreExample: () => require('../blocks/scratch3_core_example'),\n // These are the non-core built-in extensions.\n pen: () => require('../extensions/scratch3_pen'),\n wedo2: () => require('../extensions/scratch3_wedo2'),\n music: () => require('../extensions/scratch3_music'),\n microbit: () => require('../extensions/scratch3_microbit'),\n text2speech: () => require('../extensions/scratch3_text2speech'),\n translate: () => require('../extensions/scratch3_translate'),\n videoSensing: () => require('../extensions/scratch3_video_sensing'),\n ev3: () => require('../extensions/scratch3_ev3'),\n makeymakey: () => require('../extensions/scratch3_makeymakey'),\n boost: () => require('../extensions/scratch3_boost'),\n gdxfor: () => require('../extensions/scratch3_gdx_for')\n};\n\n/**\n * @typedef {object} ArgumentInfo - Information about an extension block argument\n * @property {ArgumentType} type - the type of value this argument can take\n * @property {*|undefined} default - the default value of this argument (default: blank)\n */\n\n/**\n * @typedef {object} ConvertedBlockInfo - Raw extension block data paired with processed data ready for scratch-blocks\n * @property {ExtensionBlockMetadata} info - the raw block info\n * @property {object} json - the scratch-blocks JSON definition for this block\n * @property {string} xml - the scratch-blocks XML definition for this block\n */\n\n/**\n * @typedef {object} CategoryInfo - Information about a block category\n * @property {string} id - the unique ID of this category\n * @property {string} name - the human-readable name of this category\n * @property {string|undefined} blockIconURI - optional URI for the block icon image\n * @property {string} color1 - the primary color for this category, in '#rrggbb' format\n * @property {string} color2 - the secondary color for this category, in '#rrggbb' format\n * @property {string} color3 - the tertiary color for this category, in '#rrggbb' format\n * @property {Array.} blocks - the blocks, separators, etc. in this category\n * @property {Array.} menus - the menus provided by this category\n */\n\n/**\n * @typedef {object} PendingExtensionWorker - Information about an extension worker still initializing\n * @property {string} extensionURL - the URL of the extension to be loaded by this worker\n * @property {Function} resolve - function to call on successful worker startup\n * @property {Function} reject - function to call on failed worker startup\n */\n\nclass ExtensionManager {\n constructor (runtime) {\n /**\n * The ID number to provide to the next extension worker.\n * @type {int}\n */\n this.nextExtensionWorker = 0;\n\n /**\n * FIFO queue of extensions which have been requested but not yet loaded in a worker,\n * along with promise resolution functions to call once the worker is ready or failed.\n *\n * @type {Array.}\n */\n this.pendingExtensions = [];\n\n /**\n * Map of worker ID to workers which have been allocated but have not yet finished initialization.\n * @type {Array.}\n */\n this.pendingWorkers = [];\n\n /**\n * Map of loaded extension URLs/IDs (equivalent for built-in extensions) to service name.\n * @type {Map.}\n * @private\n */\n this._loadedExtensions = new Map();\n\n /**\n * Keep a reference to the runtime so we can construct internal extension objects.\n * TODO: remove this in favor of extensions accessing the runtime as a service.\n * @type {Runtime}\n */\n this.runtime = runtime;\n\n dispatch.setService('extensions', this).catch(e => {\n log.error(`ExtensionManager was unable to register extension service: ${JSON.stringify(e)}`);\n });\n }\n\n /**\n * Check whether an extension is registered or is in the process of loading. This is intended to control loading or\n * adding extensions so it may return `true` before the extension is ready to be used. Use the promise returned by\n * `loadExtensionURL` if you need to wait until the extension is truly ready.\n * @param {string} extensionID - the ID of the extension.\n * @returns {boolean} - true if loaded, false otherwise.\n */\n isExtensionLoaded (extensionID) {\n return this._loadedExtensions.has(extensionID);\n }\n\n /**\n * Synchronously load an internal extension (core or non-core) by ID. This call will\n * fail if the provided id is not does not match an internal extension.\n * @param {string} extensionId - the ID of an internal extension\n */\n loadExtensionIdSync (extensionId) {\n if (!Object.prototype.hasOwnProperty.call(builtinExtensions, extensionId)) {\n log.warn(`Could not find extension ${extensionId} in the built in extensions.`);\n return;\n }\n\n /** @TODO dupe handling for non-builtin extensions. See commit 670e51d33580e8a2e852b3b038bb3afc282f81b9 */\n if (this.isExtensionLoaded(extensionId)) {\n const message = `Rejecting attempt to load a second extension with ID ${extensionId}`;\n log.warn(message);\n return;\n }\n\n const extension = builtinExtensions[extensionId]();\n const extensionInstance = new extension(this.runtime);\n const serviceName = this._registerInternalExtension(extensionInstance);\n this._loadedExtensions.set(extensionId, serviceName);\n }\n\n /**\n * Load an extension by URL or internal extension ID\n * @param {string} extensionURL - the URL for the extension to load OR the ID of an internal extension\n * @returns {Promise} resolved once the extension is loaded and initialized or rejected on failure\n */\n loadExtensionURL (extensionURL) {\n if (Object.prototype.hasOwnProperty.call(builtinExtensions, extensionURL)) {\n /** @TODO dupe handling for non-builtin extensions. See commit 670e51d33580e8a2e852b3b038bb3afc282f81b9 */\n if (this.isExtensionLoaded(extensionURL)) {\n const message = `Rejecting attempt to load a second extension with ID ${extensionURL}`;\n log.warn(message);\n return Promise.resolve();\n }\n\n const extension = builtinExtensions[extensionURL]();\n const extensionInstance = new extension(this.runtime);\n const serviceName = this._registerInternalExtension(extensionInstance);\n this._loadedExtensions.set(extensionURL, serviceName);\n return Promise.resolve();\n }\n\n return new Promise((resolve, reject) => {\n // If we `require` this at the global level it breaks non-webpack targets, including tests\n const worker = new Worker('./extension-worker.js');\n\n this.pendingExtensions.push({extensionURL, resolve, reject});\n dispatch.addWorker(worker);\n });\n }\n\n /**\n * Regenerate blockinfo for any loaded extensions\n * @returns {Promise} resolved once all the extensions have been reinitialized\n */\n refreshBlocks () {\n const allPromises = Array.from(this._loadedExtensions.values()).map(serviceName =>\n dispatch.call(serviceName, 'getInfo')\n .then(info => {\n info = this._prepareExtensionInfo(serviceName, info);\n dispatch.call('runtime', '_refreshExtensionPrimitives', info);\n })\n .catch(e => {\n log.error(`Failed to refresh built-in extension primitives: ${JSON.stringify(e)}`);\n })\n );\n return Promise.all(allPromises);\n }\n\n allocateWorker () {\n const id = this.nextExtensionWorker++;\n const workerInfo = this.pendingExtensions.shift();\n this.pendingWorkers[id] = workerInfo;\n return [id, workerInfo.extensionURL];\n }\n\n /**\n * Synchronously collect extension metadata from the specified service and begin the extension registration process.\n * @param {string} serviceName - the name of the service hosting the extension.\n */\n registerExtensionServiceSync (serviceName) {\n const info = dispatch.callSync(serviceName, 'getInfo');\n this._registerExtensionInfo(serviceName, info);\n }\n\n /**\n * Collect extension metadata from the specified service and begin the extension registration process.\n * @param {string} serviceName - the name of the service hosting the extension.\n */\n registerExtensionService (serviceName) {\n dispatch.call(serviceName, 'getInfo').then(info => {\n this._registerExtensionInfo(serviceName, info);\n });\n }\n\n /**\n * Called by an extension worker to indicate that the worker has finished initialization.\n * @param {int} id - the worker ID.\n * @param {*?} e - the error encountered during initialization, if any.\n */\n onWorkerInit (id, e) {\n const workerInfo = this.pendingWorkers[id];\n delete this.pendingWorkers[id];\n if (e) {\n workerInfo.reject(e);\n } else {\n workerInfo.resolve(id);\n }\n }\n\n /**\n * Register an internal (non-Worker) extension object\n * @param {object} extensionObject - the extension object to register\n * @returns {string} The name of the registered extension service\n */\n _registerInternalExtension (extensionObject) {\n const extensionInfo = extensionObject.getInfo();\n const fakeWorkerId = this.nextExtensionWorker++;\n const serviceName = `extension_${fakeWorkerId}_${extensionInfo.id}`;\n dispatch.setServiceSync(serviceName, extensionObject);\n dispatch.callSync('extensions', 'registerExtensionServiceSync', serviceName);\n return serviceName;\n }\n\n /**\n * Sanitize extension info then register its primitives with the VM.\n * @param {string} serviceName - the name of the service hosting the extension\n * @param {ExtensionInfo} extensionInfo - the extension's metadata\n * @private\n */\n _registerExtensionInfo (serviceName, extensionInfo) {\n extensionInfo = this._prepareExtensionInfo(serviceName, extensionInfo);\n dispatch.call('runtime', '_registerExtensionPrimitives', extensionInfo).catch(e => {\n log.error(`Failed to register primitives for extension on service ${serviceName}:`, e);\n });\n }\n\n /**\n * Modify the provided text as necessary to ensure that it may be used as an attribute value in valid XML.\n * @param {string} text - the text to be sanitized\n * @returns {string} - the sanitized text\n * @private\n */\n _sanitizeID (text) {\n return text.toString().replace(/[<\"&]/, '_');\n }\n\n /**\n * Apply minor cleanup and defaults for optional extension fields.\n * TODO: make the ID unique in cases where two copies of the same extension are loaded.\n * @param {string} serviceName - the name of the service hosting this extension block\n * @param {ExtensionInfo} extensionInfo - the extension info to be sanitized\n * @returns {ExtensionInfo} - a new extension info object with cleaned-up values\n * @private\n */\n _prepareExtensionInfo (serviceName, extensionInfo) {\n extensionInfo = Object.assign({}, extensionInfo);\n if (!/^[a-z0-9]+$/i.test(extensionInfo.id)) {\n throw new Error('Invalid extension id');\n }\n extensionInfo.name = extensionInfo.name || extensionInfo.id;\n extensionInfo.blocks = extensionInfo.blocks || [];\n extensionInfo.targetTypes = extensionInfo.targetTypes || [];\n extensionInfo.blocks = extensionInfo.blocks.reduce((results, blockInfo) => {\n try {\n let result;\n switch (blockInfo) {\n case '---': // separator\n result = '---';\n break;\n default: // an ExtensionBlockMetadata object\n result = this._prepareBlockInfo(serviceName, blockInfo);\n break;\n }\n results.push(result);\n } catch (e) {\n // TODO: more meaningful error reporting\n log.error(`Error processing block: ${e.message}, Block:\\n${JSON.stringify(blockInfo)}`);\n }\n return results;\n }, []);\n extensionInfo.menus = extensionInfo.menus || {};\n extensionInfo.menus = this._prepareMenuInfo(serviceName, extensionInfo.menus);\n return extensionInfo;\n }\n\n /**\n * Prepare extension menus. e.g. setup binding for dynamic menu functions.\n * @param {string} serviceName - the name of the service hosting this extension block\n * @param {Array.} menus - the menu defined by the extension.\n * @returns {Array.} - a menuInfo object with all preprocessing done.\n * @private\n */\n _prepareMenuInfo (serviceName, menus) {\n const menuNames = Object.getOwnPropertyNames(menus);\n for (let i = 0; i < menuNames.length; i++) {\n const menuName = menuNames[i];\n let menuInfo = menus[menuName];\n\n // If the menu description is in short form (items only) then normalize it to general form: an object with\n // its items listed in an `items` property.\n if (!menuInfo.items) {\n menuInfo = {\n items: menuInfo\n };\n menus[menuName] = menuInfo;\n }\n // If `items` is a string, it should be the name of a function in the extension object. Calling the\n // function should return an array of items to populate the menu when it is opened.\n if (typeof menuInfo.items === 'string') {\n const menuItemFunctionName = menuInfo.items;\n const serviceObject = dispatch.services[serviceName];\n // Bind the function here so we can pass a simple item generation function to Scratch Blocks later.\n menuInfo.items = this._getExtensionMenuItems.bind(this, serviceObject, menuItemFunctionName);\n }\n }\n return menus;\n }\n\n /**\n * Fetch the items for a particular extension menu, providing the target ID for context.\n * @param {object} extensionObject - the extension object providing the menu.\n * @param {string} menuItemFunctionName - the name of the menu function to call.\n * @returns {Array} menu items ready for scratch-blocks.\n * @private\n */\n _getExtensionMenuItems (extensionObject, menuItemFunctionName) {\n // Fetch the items appropriate for the target currently being edited. This assumes that menus only\n // collect items when opened by the user while editing a particular target.\n const editingTarget = this.runtime.getEditingTarget() || this.runtime.getTargetForStage();\n const editingTargetID = editingTarget ? editingTarget.id : null;\n const extensionMessageContext = this.runtime.makeMessageContextForTarget(editingTarget);\n\n // TODO: Fix this to use dispatch.call when extensions are running in workers.\n const menuFunc = extensionObject[menuItemFunctionName];\n const menuItems = menuFunc.call(extensionObject, editingTargetID).map(\n item => {\n item = maybeFormatMessage(item, extensionMessageContext);\n switch (typeof item) {\n case 'object':\n return [\n maybeFormatMessage(item.text, extensionMessageContext),\n item.value\n ];\n case 'string':\n return [item, item];\n default:\n return item;\n }\n });\n\n if (!menuItems || menuItems.length < 1) {\n throw new Error(`Extension menu returned no items: ${menuItemFunctionName}`);\n }\n return menuItems;\n }\n\n /**\n * Apply defaults for optional block fields.\n * @param {string} serviceName - the name of the service hosting this extension block\n * @param {ExtensionBlockMetadata} blockInfo - the block info from the extension\n * @returns {ExtensionBlockMetadata} - a new block info object which has values for all relevant optional fields.\n * @private\n */\n _prepareBlockInfo (serviceName, blockInfo) {\n blockInfo = Object.assign({}, {\n blockType: BlockType.COMMAND,\n terminal: false,\n blockAllThreads: false,\n arguments: {}\n }, blockInfo);\n blockInfo.opcode = blockInfo.opcode && this._sanitizeID(blockInfo.opcode);\n blockInfo.text = blockInfo.text || blockInfo.opcode;\n\n switch (blockInfo.blockType) {\n case BlockType.EVENT:\n if (blockInfo.func) {\n log.warn(`Ignoring function \"${blockInfo.func}\" for event block ${blockInfo.opcode}`);\n }\n break;\n case BlockType.BUTTON:\n if (blockInfo.opcode) {\n log.warn(`Ignoring opcode \"${blockInfo.opcode}\" for button with text: ${blockInfo.text}`);\n }\n break;\n default: {\n if (!blockInfo.opcode) {\n throw new Error('Missing opcode for block');\n }\n\n const funcName = blockInfo.func ? this._sanitizeID(blockInfo.func) : blockInfo.opcode;\n\n const getBlockInfo = blockInfo.isDynamic ?\n args => args && args.mutation && args.mutation.blockInfo :\n () => blockInfo;\n const callBlockFunc = (() => {\n if (dispatch._isRemoteService(serviceName)) {\n return (args, util, realBlockInfo) =>\n dispatch.call(serviceName, funcName, args, util, realBlockInfo);\n }\n\n // avoid promise latency if we can call direct\n const serviceObject = dispatch.services[serviceName];\n if (!serviceObject[funcName]) {\n // The function might show up later as a dynamic property of the service object\n log.warn(`Could not find extension block function called ${funcName}`);\n }\n return (args, util, realBlockInfo) =>\n serviceObject[funcName](args, util, realBlockInfo);\n })();\n\n blockInfo.func = (args, util) => {\n const realBlockInfo = getBlockInfo(args);\n // TODO: filter args using the keys of realBlockInfo.arguments? maybe only if sandboxed?\n return callBlockFunc(args, util, realBlockInfo);\n };\n break;\n }\n }\n\n return blockInfo;\n }\n}\n\nmodule.exports = ExtensionManager;\n","/**\n * Default types of Target supported by the VM\n * @enum {string}\n */\nconst TargetType = {\n /**\n * Rendered target which can move, change costumes, etc.\n */\n SPRITE: 'sprite',\n\n /**\n * Rendered target which cannot move but can change backdrops\n */\n STAGE: 'stage'\n};\n\nmodule.exports = TargetType;\n","const ArgumentType = require('../../extension-support/argument-type');\nconst BlockType = require('../../extension-support/block-type');\nconst Cast = require('../../util/cast');\nconst formatMessage = require('format-message');\nconst color = require('../../util/color');\nconst BLE = require('../../io/ble');\nconst Base64Util = require('../../util/base64-util');\nconst MathUtil = require('../../util/math-util');\nconst RateLimiter = require('../../util/rateLimiter.js');\nconst log = require('../../util/log');\n\n/**\n * The LEGO Wireless Protocol documentation used to create this extension can be found at:\n * https://lego.github.io/lego-ble-wireless-protocol-docs/index.html\n */\n\n/**\n * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.\n * @type {string}\n */\n// eslint-disable-next-line max-len\nconst iconURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAMAAAC5zwKfAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACpQTFRF////fIel5ufolZ62/2YavsPS+YZOkJmy9/j53+Hk6+zs6N/b6dfO////tDhMHAAAAA50Uk5T/////////////////wBFwNzIAAAA6ElEQVR42uzX2w6DIBAEUGDVtlr//3dLaLwgiwUd2z7MJPJg5EQWiGhGcAxBggQJEiT436CIfqXJPTn3MKNYYMSDFpoAmp24OaYgvwKnFgL2zvVTCwHrMoMi+nUQLFthaNCCa0iwclLkDgYVsQp0mzxuqXgK1MRzoCLWgkPXNN2wI/q6Kvt7u/cX0HtejN8x2sXpnpb8J8D3b0Keuhh3X975M+i0xNVbg3s1TIasgK21bQyGO+s2PykaGMYbge8KrNrssvkOWDXkErB8UuBHETjoYLkKBA8ZfuDkbwVBggQJEiR4MC8BBgDTtMZLx2nFCQAAAABJRU5ErkJggg==';\n\n/**\n * Boost BLE UUIDs.\n * @enum {string}\n */\nconst BoostBLE = {\n service: '00001623-1212-efde-1623-785feabcd123',\n characteristic: '00001624-1212-efde-1623-785feabcd123',\n sendInterval: 100,\n sendRateMax: 20\n};\n\n/**\n * Boost Motor Max Power Add. Defines how much more power than the target speed\n * the motors may supply to reach the target speed faster.\n * Lower number == softer, slower reached target speed.\n * Higher number == harder, faster reached target speed.\n * @constant {number}\n */\nconst BoostMotorMaxPowerAdd = 10;\n\n/**\n * A time interval to wait (in milliseconds) in between battery check calls.\n * @type {number}\n */\nconst BoostPingInterval = 5000;\n\n/**\n * The number of continuous samples the color-sensor will evaluate color from.\n * @type {number}\n */\nconst BoostColorSampleSize = 5;\n\n/**\n * Enum for Boost sensor and actuator types.\n * @readonly\n * @enum {number}\n */\nconst BoostIO = {\n MOTOR_WEDO: 0x01,\n MOTOR_SYSTEM: 0x02,\n BUTTON: 0x05,\n LIGHT: 0x08,\n VOLTAGE: 0x14,\n CURRENT: 0x15,\n PIEZO: 0x16,\n LED: 0x17,\n TILT_EXTERNAL: 0x22,\n MOTION_SENSOR: 0x23,\n COLOR: 0x25,\n MOTOREXT: 0x26,\n MOTORINT: 0x27,\n TILT: 0x28\n};\n\n/**\n * Enum for ids for various output command feedback types on the Boost.\n * @readonly\n * @enum {number}\n */\nconst BoostPortFeedback = {\n IN_PROGRESS: 0x01,\n COMPLETED: 0x02,\n DISCARDED: 0x04,\n IDLE: 0x08,\n BUSY_OR_FULL: 0x10\n};\n\n/**\n * Enum for physical Boost Ports\n * @readonly\n * @enum {number}\n */\n\nconst BoostPort10000223OrOlder = {\n A: 55,\n B: 56,\n C: 1,\n D: 2\n};\n\nconst BoostPort10000224OrNewer = {\n A: 0,\n B: 1,\n C: 2,\n D: 3\n};\n\n// Set default port mapping to support the newer firmware\nlet BoostPort = BoostPort10000224OrNewer;\n\n/**\n * Ids for each color sensor value used by the extension.\n * @readonly\n * @enum {string}\n */\nconst BoostColor = {\n ANY: 'any',\n NONE: 'none',\n RED: 'red',\n BLUE: 'blue',\n GREEN: 'green',\n YELLOW: 'yellow',\n WHITE: 'white',\n BLACK: 'black'\n};\n\n/**\n * Enum for indices for each color sensed by the Boost vision sensor.\n * @readonly\n * @enum {number}\n */\nconst BoostColorIndex = {\n [BoostColor.NONE]: 255,\n [BoostColor.RED]: 9,\n [BoostColor.BLUE]: 3,\n [BoostColor.GREEN]: 5,\n [BoostColor.YELLOW]: 7,\n [BoostColor.WHITE]: 10,\n [BoostColor.BLACK]: 0\n};\n\n/**\n * Enum for Message Types\n * @readonly\n * @enum {number}\n */\nconst BoostMessage = {\n HUB_PROPERTIES: 0x01,\n HUB_ACTIONS: 0x02,\n HUB_ALERTS: 0x03,\n HUB_ATTACHED_IO: 0x04,\n ERROR: 0x05,\n PORT_INPUT_FORMAT_SETUP_SINGLE: 0x41,\n PORT_INPUT_FORMAT_SETUP_COMBINED: 0x42,\n PORT_INFORMATION: 0x43,\n PORT_MODEINFORMATION: 0x44,\n PORT_VALUE: 0x45,\n PORT_VALUE_COMBINED: 0x46,\n PORT_INPUT_FORMAT: 0x47,\n PORT_INPUT_FORMAT_COMBINED: 0x48,\n OUTPUT: 0x81,\n PORT_FEEDBACK: 0x82\n};\n\n/**\n * Enum for Hub Property Types\n * @readonly\n * @enum {number}\n */\n\nconst BoostHubProperty = {\n ADVERTISEMENT_NAME: 0x01,\n BUTTON: 0x02,\n FW_VERSION: 0x03,\n HW_VERSION: 0x04,\n RSSI: 0x05,\n BATTERY_VOLTAGE: 0x06,\n BATTERY_TYPE: 0x07,\n MANUFACTURER_NAME: 0x08,\n RADIO_FW_VERSION: 0x09,\n LEGO_WP_VERSION: 0x0A,\n SYSTEM_TYPE_ID: 0x0B,\n HW_NETWORK_ID: 0x0C,\n PRIMARY_MAC: 0x0D,\n SECONDARY_MAC: 0x0E,\n HW_NETWORK_FAMILY: 0x0F\n};\n\n/**\n * Enum for Hub Property Operations\n * @readonly\n * @enum {number}\n */\n\nconst BoostHubPropertyOperation = {\n SET: 0x01,\n ENABLE_UPDATES: 0x02,\n DISABLE_UPDATES: 0x03,\n RESET: 0x04,\n REQUEST_UPDATE: 0x05,\n UPDATE: 0x06\n};\n\n/**\n * Enum for Motor Subcommands (for 0x81)\n * @readonly\n * @enum {number}\n */\nconst BoostOutputSubCommand = {\n START_POWER: 0x01,\n START_POWER_PAIR: 0x02,\n SET_ACC_TIME: 0x05,\n SET_DEC_TIME: 0x06,\n START_SPEED: 0x07,\n START_SPEED_PAIR: 0x08,\n START_SPEED_FOR_TIME: 0x09,\n START_SPEED_FOR_TIME_PAIR: 0x0A,\n START_SPEED_FOR_DEGREES: 0x0B,\n START_SPEED_FOR_DEGREES_PAIR: 0x0C,\n GO_TO_ABS_POSITION: 0x0D,\n GO_TO_ABS_POSITION_PAIR: 0x0E,\n PRESET_ENCODER: 0x14,\n WRITE_DIRECT_MODE_DATA: 0x51\n};\n\n/**\n * Enum for Startup/Completion information for an output command.\n * Startup and completion bytes must be OR'ed to be combined to a single byte.\n * @readonly\n * @enum {number}\n */\nconst BoostOutputExecution = {\n // Startup information\n BUFFER_IF_NECESSARY: 0x00,\n EXECUTE_IMMEDIATELY: 0x10,\n // Completion information\n NO_ACTION: 0x00,\n COMMAND_FEEDBACK: 0x01\n};\n\n/**\n * Enum for Boost Motor end states\n * @readonly\n * @enum {number}\n */\nconst BoostMotorEndState = {\n FLOAT: 0,\n HOLD: 126,\n BRAKE: 127\n};\n\n/**\n * Enum for Boost Motor acceleration/deceleration profiles\n * @readyonly\n * @enum {number}\n */\nconst BoostMotorProfile = {\n DO_NOT_USE: 0x00,\n ACCELERATION: 0x01,\n DECELERATION: 0x02\n};\n\n/**\n * Enum for when Boost IO's are attached/detached\n * @readonly\n * @enum {number}\n */\nconst BoostIOEvent = {\n ATTACHED: 0x01,\n DETACHED: 0x00,\n ATTACHED_VIRTUAL: 0x02\n};\n\n/**\n * Enum for selected sensor modes.\n * @enum {number}\n */\nconst BoostMode = {\n TILT: 0, // angle (pitch/yaw)\n LED: 1, // Set LED to accept RGB values\n COLOR: 0, // Read indexed colors from Vision Sensor\n MOTOR_SENSOR: 2, // Set motors to report their position\n UNKNOWN: 0 // Anything else will use the default mode (mode 0)\n};\n\n/**\n * Enum for Boost motor states.\n * @param {number}\n */\nconst BoostMotorState = {\n OFF: 0,\n ON_FOREVER: 1,\n ON_FOR_TIME: 2,\n ON_FOR_ROTATION: 3\n};\n\n/**\n * Helper function for converting a JavaScript number to an INT32-number\n * @param {number} number - a number\n * @return {array} - a 4-byte array of Int8-values representing an INT32-number\n */\nconst numberToInt32Array = function (number) {\n const buffer = new ArrayBuffer(4);\n const dataview = new DataView(buffer);\n dataview.setInt32(0, number);\n return [\n dataview.getInt8(3),\n dataview.getInt8(2),\n dataview.getInt8(1),\n dataview.getInt8(0)\n ];\n};\n\n/**\n * Helper function for converting a regular array to a Little Endian INT32-value\n * @param {Array} array - an array containing UInt8-values\n * @return {number} - a number\n */\nconst int32ArrayToNumber = function (array) {\n const i = Uint8Array.from(array);\n const d = new DataView(i.buffer);\n return d.getInt32(0, true);\n};\n\n/**\n * Manage power, direction, position, and timers for one Boost motor.\n */\nclass BoostMotor {\n /**\n * Construct a Boost Motor instance.\n * @param {Boost} parent - the Boost peripheral which owns this motor.\n * @param {int} index - the zero-based index of this motor on its parent peripheral.\n */\n constructor (parent, index) {\n /**\n * The Boost peripheral which owns this motor.\n * @type {Boost}\n * @private\n */\n this._parent = parent;\n\n /**\n * The zero-based index of this motor on its parent peripheral.\n * @type {int}\n * @private\n */\n this._index = index;\n\n /**\n * This motor's current direction: 1 for \"this way\" or -1 for \"that way\"\n * @type {number}\n * @private\n */\n this._direction = 1;\n\n /**\n * This motor's current power level, in the range [0,100].\n * @type {number}\n * @private\n */\n this._power = 50;\n\n /**\n * This motor's current relative position\n * @type {number}\n * @private\n */\n this._position = 0;\n\n /**\n * Is this motor currently moving?\n * @type {boolean}\n * @private\n */\n this._status = BoostMotorState.OFF;\n\n /**\n * If the motor has been turned on or is actively braking for a specific duration, this is the timeout ID for\n * the end-of-action handler. Cancel this when changing plans.\n * @type {Object}\n * @private\n */\n this._pendingDurationTimeoutId = null;\n\n /**\n * The starting time for the pending duration timeout.\n * @type {number}\n * @private\n */\n this._pendingDurationTimeoutStartTime = null;\n\n /**\n * The delay/duration of the pending duration timeout.\n * @type {number}\n * @private\n */\n this._pendingDurationTimeoutDelay = null;\n\n /**\n * The target position of a turn-based command.\n * @type {number}\n * @private\n */\n this._pendingRotationDestination = null;\n\n /**\n * If the motor has been turned on run for a specific rotation, this is the function\n * that will be called once Scratch VM gets a notification from the Move Hub.\n * @type {Object}\n * @private\n */\n this._pendingRotationPromise = null;\n\n this.turnOff = this.turnOff.bind(this);\n }\n\n /**\n * @return {int} - this motor's current direction: 1 for \"this way\" or -1 for \"that way\"\n */\n get direction () {\n return this._direction;\n }\n\n /**\n * @param {int} value - this motor's new direction: 1 for \"this way\" or -1 for \"that way\"\n */\n set direction (value) {\n if (value < 0) {\n this._direction = -1;\n } else {\n this._direction = 1;\n }\n }\n\n /**\n * @return {int} - this motor's current power level, in the range [0,100].\n */\n get power () {\n return this._power;\n }\n\n /**\n * @param {int} value - this motor's new power level, in the range [10,100].\n */\n set power (value) {\n /**\n * Scale the motor power to a range between 10 and 100,\n * to make sure the motors will run with something built onto them.\n */\n if (value === 0) {\n this._power = 0;\n } else {\n this._power = MathUtil.scale(value, 1, 100, 10, 100);\n }\n }\n\n /**\n * @return {int} - this motor's current position, in the range of [-MIN_INT32,MAX_INT32]\n */\n get position () {\n return this._position;\n }\n\n /**\n * @param {int} value - set this motor's current position.\n */\n set position (value) {\n this._position = value;\n }\n\n /**\n * @return {BoostMotorState} - the motor's current state.\n */\n get status () {\n return this._status;\n }\n\n /**\n * @param {BoostMotorState} value - set this motor's state.\n */\n set status (value) {\n this._clearRotationState();\n this._clearDurationTimeout();\n this._status = value;\n }\n\n /**\n * @return {number} - time, in milliseconds, of when the pending duration timeout began.\n */\n get pendingDurationTimeoutStartTime () {\n return this._pendingDurationTimeoutStartTime;\n }\n\n /**\n * @return {number} - delay, in milliseconds, of the pending duration timeout.\n */\n get pendingDurationTimeoutDelay () {\n return this._pendingDurationTimeoutDelay;\n }\n\n /**\n * @return {number} - target position, in degrees, of the pending rotation.\n */\n get pendingRotationDestination () {\n return this._pendingRotationDestination;\n }\n\n /**\n * @return {Promise} - the Promise function for the pending rotation.\n */\n get pendingRotationPromise () {\n return this._pendingRotationPromise;\n }\n\n /**\n * @param {function} func - function to resolve pending rotation Promise\n */\n set pendingRotationPromise (func) {\n this._pendingRotationPromise = func;\n }\n\n /**\n * Turn this motor on indefinitely\n * @private\n */\n _turnOn () {\n const cmd = this._parent.generateOutputCommand(\n this._index,\n BoostOutputExecution.EXECUTE_IMMEDIATELY,\n BoostOutputSubCommand.START_SPEED,\n [\n this.power * this.direction,\n MathUtil.clamp(this.power + BoostMotorMaxPowerAdd, 0, 100),\n BoostMotorProfile.DO_NOT_USE\n ]);\n\n this._parent.send(BoostBLE.characteristic, cmd);\n }\n\n /**\n * Turn this motor on indefinitely\n */\n turnOnForever () {\n this.status = BoostMotorState.ON_FOREVER;\n this._turnOn();\n }\n\n /**\n * Turn this motor on for a specific duration.\n * @param {number} milliseconds - run the motor for this long.\n */\n turnOnFor (milliseconds) {\n milliseconds = Math.max(0, milliseconds);\n this.status = BoostMotorState.ON_FOR_TIME;\n this._turnOn();\n this._setNewDurationTimeout(this.turnOff, milliseconds);\n }\n\n /**\n * Turn this motor on for a specific rotation in degrees.\n * @param {number} degrees - run the motor for this amount of degrees.\n * @param {number} direction - rotate in this direction\n */\n turnOnForDegrees (degrees, direction) {\n degrees = Math.max(0, degrees);\n\n const cmd = this._parent.generateOutputCommand(\n this._index,\n (BoostOutputExecution.EXECUTE_IMMEDIATELY ^ BoostOutputExecution.COMMAND_FEEDBACK),\n BoostOutputSubCommand.START_SPEED_FOR_DEGREES,\n [\n ...numberToInt32Array(degrees),\n this.power * this.direction * direction,\n MathUtil.clamp(this.power + BoostMotorMaxPowerAdd, 0, 100),\n BoostMotorEndState.BRAKE,\n BoostMotorProfile.DO_NOT_USE\n ]\n );\n\n this.status = BoostMotorState.ON_FOR_ROTATION;\n this._pendingRotationDestination = this.position + (degrees * this.direction * direction);\n this._parent.send(BoostBLE.characteristic, cmd);\n }\n\n /**\n * Turn this motor off.\n * @param {boolean} [useLimiter=true] - if true, use the rate limiter\n */\n turnOff (useLimiter = true) {\n const cmd = this._parent.generateOutputCommand(\n this._index,\n BoostOutputExecution.EXECUTE_IMMEDIATELY,\n BoostOutputSubCommand.START_POWER,\n [\n BoostMotorEndState.FLOAT\n ]\n );\n\n this.status = BoostMotorState.OFF;\n this._parent.send(BoostBLE.characteristic, cmd, useLimiter);\n }\n\n /**\n * Clear the motor action timeout, if any. Safe to call even when there is no pending timeout.\n * @private\n */\n _clearDurationTimeout () {\n if (this._pendingDurationTimeoutId !== null) {\n clearTimeout(this._pendingDurationTimeoutId);\n this._pendingDurationTimeoutId = null;\n this._pendingDurationTimeoutStartTime = null;\n this._pendingDurationTimeoutDelay = null;\n }\n }\n\n /**\n * Set a new motor action timeout, after clearing an existing one if necessary.\n * @param {Function} callback - to be called at the end of the timeout.\n * @param {int} delay - wait this many milliseconds before calling the callback.\n * @private\n */\n _setNewDurationTimeout (callback, delay) {\n this._clearDurationTimeout();\n const timeoutID = setTimeout(() => {\n if (this._pendingDurationTimeoutId === timeoutID) {\n this._pendingDurationTimeoutId = null;\n this._pendingDurationTimeoutStartTime = null;\n this._pendingDurationTimeoutDelay = null;\n }\n callback();\n }, delay);\n this._pendingDurationTimeoutId = timeoutID;\n this._pendingDurationTimeoutStartTime = Date.now();\n this._pendingDurationTimeoutDelay = delay;\n }\n\n /**\n * Clear the motor states related to rotation-based commands, if any.\n * Safe to call even when there is no pending promise function.\n * @private\n */\n _clearRotationState () {\n if (this._pendingRotationPromise !== null) {\n this._pendingRotationPromise();\n this._pendingRotationPromise = null;\n }\n this._pendingRotationDestination = null;\n }\n}\n\n/**\n * Manage communication with a Boost peripheral over a Bluetooth Low Energy client socket.\n */\nclass Boost {\n\n constructor (runtime, extensionId) {\n\n /**\n * The Scratch 3.0 runtime used to trigger the green flag button.\n * @type {Runtime}\n * @private\n */\n this._runtime = runtime;\n this._runtime.on('PROJECT_STOP_ALL', this.stopAll.bind(this));\n\n /**\n * The id of the extension this peripheral belongs to.\n */\n this._extensionId = extensionId;\n\n /**\n * A list of the ids of the physical or virtual sensors.\n * @type {string[]}\n * @private\n */\n this._ports = [];\n\n /**\n * A list of motors registered by the Boost hardware.\n * @type {BoostMotor[]}\n * @private\n */\n this._motors = [];\n\n /**\n * The most recently received value for each sensor.\n * @type {Object.}\n * @private\n */\n this._sensors = {\n tiltX: 0,\n tiltY: 0,\n color: BoostColor.NONE,\n previousColor: BoostColor.NONE\n };\n\n /**\n * An array of values from the Boost Vision Sensor.\n * @type {Array}\n * @private\n */\n this._colorSamples = [];\n\n /**\n * The Bluetooth connection socket for reading/writing peripheral data.\n * @type {BLE}\n * @private\n */\n this._ble = null;\n this._runtime.registerPeripheralExtension(extensionId, this);\n\n /**\n * A rate limiter utility, to help limit the rate at which we send BLE messages\n * over the socket to Scratch Link to a maximum number of sends per second.\n * @type {RateLimiter}\n * @private\n */\n this._rateLimiter = new RateLimiter(BoostBLE.sendRateMax);\n\n /**\n * An interval id for the battery check interval.\n * @type {number}\n * @private\n */\n this._pingDeviceId = null;\n\n this.reset = this.reset.bind(this);\n this._onConnect = this._onConnect.bind(this);\n this._onMessage = this._onMessage.bind(this);\n this._pingDevice = this._pingDevice.bind(this);\n }\n\n /**\n * @return {number} - the latest value received for the tilt sensor's tilt about the X axis.\n */\n get tiltX () {\n return this._sensors.tiltX;\n }\n\n /**\n * @return {number} - the latest value received for the tilt sensor's tilt about the Y axis.\n */\n get tiltY () {\n return this._sensors.tiltY;\n }\n\n /**\n * @return {number} - the latest color value received from the vision sensor.\n */\n get color () {\n return this._sensors.color;\n }\n\n /**\n * @return {number} - the previous color value received from the vision sensor.\n */\n get previousColor () {\n return this._sensors.previousColor;\n }\n\n /**\n * Look up the color id for an index received from the vision sensor.\n * @param {number} index - the color index to look up.\n * @return {BoostColor} the color id for this index.\n */\n boostColorForIndex (index) {\n const colorForIndex = Object.keys(BoostColorIndex).find(key => BoostColorIndex[key] === index);\n return colorForIndex || BoostColor.NONE;\n }\n\n /**\n * Access a particular motor on this peripheral.\n * @param {int} index - the index of the desired motor.\n * @return {BoostMotor} - the BoostMotor instance, if any, at that index.\n */\n motor (index) {\n return this._motors[index];\n }\n\n /**\n * Stop all the motors that are currently running.\n */\n stopAllMotors () {\n this._motors.forEach(motor => {\n if (motor) {\n // Send the motor off command without using the rate limiter.\n // This allows the stop button to stop motors even if we are\n // otherwise flooded with commands.\n motor.turnOff(false);\n }\n });\n }\n\n /**\n * Set the Boost peripheral's LED to a specific color.\n * @param {int} inputRGB - a 24-bit RGB color in 0xRRGGBB format.\n * @return {Promise} - a promise of the completion of the set led send operation.\n */\n setLED (inputRGB) {\n const rgb = [\n (inputRGB >> 16) & 0x000000FF,\n (inputRGB >> 8) & 0x000000FF,\n (inputRGB) & 0x000000FF\n ];\n\n const cmd = this.generateOutputCommand(\n this._ports.indexOf(BoostIO.LED),\n BoostOutputExecution.EXECUTE_IMMEDIATELY ^ BoostOutputExecution.COMMAND_FEEDBACK,\n BoostOutputSubCommand.WRITE_DIRECT_MODE_DATA,\n [BoostMode.LED,\n ...rgb]\n );\n\n return this.send(BoostBLE.characteristic, cmd);\n }\n\n /**\n * Sets the input mode of the LED to RGB.\n * @return {Promise} - a promise returned by the send operation.\n */\n setLEDMode () {\n const cmd = this.generateInputCommand(\n this._ports.indexOf(BoostIO.LED),\n BoostMode.LED,\n 0,\n false\n );\n\n return this.send(BoostBLE.characteristic, cmd);\n }\n\n /**\n * Stop the motors on the Boost peripheral.\n */\n stopAll () {\n if (!this.isConnected()) return;\n this.stopAllMotors();\n }\n\n /**\n * Called by the runtime when user wants to scan for a Boost peripheral.\n */\n scan () {\n if (this._ble) {\n this._ble.disconnect();\n }\n this._ble = new BLE(this._runtime, this._extensionId, {\n filters: [{\n services: [BoostBLE.service],\n manufacturerData: {\n 0x0397: {\n dataPrefix: [0x00, 0x40],\n mask: [0x00, 0xFF]\n }\n }\n }],\n optionalServices: []\n }, this._onConnect, this.reset);\n }\n\n /**\n * Called by the runtime when user wants to connect to a certain Boost peripheral.\n * @param {number} id - the id of the peripheral to connect to.\n */\n connect (id) {\n if (this._ble) {\n this._ble.connectPeripheral(id);\n }\n }\n\n /**\n * Disconnects from the current BLE socket and resets state.\n */\n disconnect () {\n if (this._ble) {\n this._ble.disconnect();\n }\n\n this.reset();\n }\n\n /**\n * Reset all the state and timeout/interval ids.\n */\n reset () {\n this._ports = [];\n this._motors = [];\n this._sensors = {\n tiltX: 0,\n tiltY: 0,\n color: BoostColor.NONE,\n previousColor: BoostColor.NONE\n };\n\n if (this._pingDeviceId) {\n window.clearInterval(this._pingDeviceId);\n this._pingDeviceId = null;\n }\n }\n\n /**\n * Called by the runtime to detect whether the Boost peripheral is connected.\n * @return {boolean} - the connected state.\n */\n isConnected () {\n let connected = false;\n if (this._ble) {\n connected = this._ble.isConnected();\n }\n return connected;\n }\n\n /**\n * Write a message to the Boost peripheral BLE socket.\n * @param {number} uuid - the UUID of the characteristic to write to\n * @param {Array} message - the message to write.\n * @param {boolean} [useLimiter=true] - if true, use the rate limiter\n * @return {Promise} - a promise result of the write operation\n */\n send (uuid, message, useLimiter = true) {\n if (!this.isConnected()) return Promise.resolve();\n\n if (useLimiter) {\n if (!this._rateLimiter.okayToSend()) return Promise.resolve();\n }\n\n return this._ble.write(\n BoostBLE.service,\n uuid,\n Base64Util.uint8ArrayToBase64(message),\n 'base64'\n );\n }\n\n /**\n * Generate a Boost 'Output Command' in the byte array format\n * (COMMON HEADER, PORT ID, EXECUTION BYTE, SUBCOMMAND ID, PAYLOAD).\n *\n * Payload is accepted as an array since these vary across different subcommands.\n *\n * @param {number} portID - the port (Connect ID) to send a command to.\n * @param {number} execution - Byte containing startup/completion information\n * @param {number} subCommand - the id of the subcommand byte.\n * @param {array} payload - the list of bytes to send as subcommand payload\n * @return {array} - a generated output command.\n */\n generateOutputCommand (portID, execution, subCommand, payload) {\n const hubID = 0x00;\n const command = [hubID, BoostMessage.OUTPUT, portID, execution, subCommand, ...payload];\n command.unshift(command.length + 1); // Prepend payload with length byte;\n\n return command;\n }\n\n /**\n * Generate a Boost 'Input Command' in the byte array format\n * (COMMAND ID, COMMAND TYPE, CONNECT ID, TYPE ID, MODE, DELTA INTERVAL (4 BYTES),\n * UNIT, NOTIFICATIONS ENABLED).\n *\n * This sends a command to the Boost that sets that input format\n * of the specified inputs and sets value change notifications.\n *\n * @param {number} portID - the port (Connect ID) to send a command to.\n * @param {number} mode - the mode of the input sensor.\n * @param {number} delta - the delta change needed to trigger notification.\n * @param {boolean} enableNotifications - whether to enable notifications.\n * @return {array} - a generated input command.\n */\n generateInputCommand (portID, mode, delta, enableNotifications) {\n const command = [\n 0x00, // Hub ID\n BoostMessage.PORT_INPUT_FORMAT_SETUP_SINGLE,\n portID,\n mode\n ].concat(numberToInt32Array(delta)).concat([\n enableNotifications\n ]);\n command.unshift(command.length + 1); // Prepend payload with length byte;\n\n return command;\n }\n\n /**\n * Starts reading data from peripheral after BLE has connected.\n * @private\n */\n _onConnect () {\n this._ble.startNotifications(\n BoostBLE.service,\n BoostBLE.characteristic,\n this._onMessage\n );\n this._pingDeviceId = window.setInterval(this._pingDevice, BoostPingInterval);\n\n // Send a request for firmware version.\n setTimeout(() => {\n const command = [\n 0x00, // Hub ID\n BoostMessage.HUB_PROPERTIES,\n BoostHubProperty.FW_VERSION,\n BoostHubPropertyOperation.REQUEST_UPDATE\n ];\n command.unshift(command.length + 1);\n this.send(BoostBLE.characteristic, command, false);\n }, 500);\n\n }\n\n /**\n * Process the sensor data from the incoming BLE characteristic.\n * @param {object} base64 - the incoming BLE data.\n * @private\n */\n _onMessage (base64) {\n const data = Base64Util.base64ToUint8Array(base64);\n\n /**\n * First three bytes are the common header:\n * 0: Length of message\n * 1: Hub ID (always 0x00 at the moment, unused)\n * 2: Message Type\n * 3: Port ID\n * We base our switch-case on Message Type\n */\n\n const messageType = data[2];\n const portID = data[3];\n\n switch (messageType) {\n \n case BoostMessage.HUB_PROPERTIES: {\n const property = data[3];\n switch (property) {\n case BoostHubProperty.FW_VERSION: {\n // Establish firmware version 1.0.00.0224 as a 32-bit signed integer (little endian)\n const fwVersion10000224 = int32ArrayToNumber([0x24, 0x02, 0x00, 0x10]);\n const fwHub = int32ArrayToNumber(data.slice(5, data.length));\n if (fwHub < fwVersion10000224) {\n BoostPort = BoostPort10000223OrOlder;\n log.info('Move Hub firmware older than version 1.0.00.0224 detected. Using old port mapping.');\n } else {\n BoostPort = BoostPort10000224OrNewer;\n }\n break;\n }\n }\n break;\n }\n case BoostMessage.HUB_ATTACHED_IO: { // IO Attach/Detach events\n const event = data[4];\n const typeId = data[5];\n\n switch (event) {\n case BoostIOEvent.ATTACHED:\n this._registerSensorOrMotor(portID, typeId);\n break;\n case BoostIOEvent.DETACHED:\n this._clearPort(portID);\n break;\n case BoostIOEvent.ATTACHED_VIRTUAL:\n default:\n }\n break;\n }\n case BoostMessage.PORT_VALUE: {\n const type = this._ports[portID];\n\n switch (type) {\n case BoostIO.TILT:\n this._sensors.tiltX = data[4];\n this._sensors.tiltY = data[5];\n break;\n case BoostIO.COLOR:\n this._colorSamples.unshift(data[4]);\n if (this._colorSamples.length > BoostColorSampleSize) {\n this._colorSamples.pop();\n if (this._colorSamples.every((v, i, arr) => v === arr[0])) {\n this._sensors.previousColor = this._sensors.color;\n this._sensors.color = this.boostColorForIndex(this._colorSamples[0]);\n } else {\n this._sensors.color = BoostColor.NONE;\n }\n } else {\n this._sensors.color = BoostColor.NONE;\n }\n break;\n case BoostIO.MOTOREXT:\n case BoostIO.MOTORINT:\n this.motor(portID).position = int32ArrayToNumber(data.slice(4, 8));\n break;\n case BoostIO.CURRENT:\n case BoostIO.VOLTAGE:\n case BoostIO.LED:\n break;\n default:\n log.warn(`Unknown sensor value! Type: ${type}`);\n }\n break;\n }\n case BoostMessage.PORT_FEEDBACK: {\n const feedback = data[4];\n const motor = this.motor(portID);\n if (motor) {\n // Makes sure that commands resolve both when they actually complete and when they fail\n const isBusy = feedback & BoostPortFeedback.IN_PROGRESS;\n const commandCompleted = feedback & (BoostPortFeedback.COMPLETED ^ BoostPortFeedback.DISCARDED);\n if (!isBusy && commandCompleted) {\n if (motor.status === BoostMotorState.ON_FOR_ROTATION) {\n motor.status = BoostMotorState.OFF;\n }\n }\n }\n break;\n }\n case BoostMessage.ERROR:\n log.warn(`Error reported by hub: ${data}`);\n break;\n }\n }\n\n /**\n * Ping the Boost hub. If the Boost hub has disconnected\n * for some reason, the BLE socket will get an error back and automatically\n * close the socket.\n * @private\n */\n _pingDevice () {\n this._ble.read(\n BoostBLE.service,\n BoostBLE.characteristic,\n false\n );\n }\n\n /**\n * Register a new sensor or motor connected at a port. Store the type of\n * sensor or motor internally, and then register for notifications on input\n * values if it is a sensor.\n * @param {number} portID - the port to register a sensor or motor on.\n * @param {number} type - the type ID of the sensor or motor\n * @private\n */\n _registerSensorOrMotor (portID, type) {\n // Record which port is connected to what type of device\n this._ports[portID] = type;\n\n // Record motor port\n if (type === BoostIO.MOTORINT || type === BoostIO.MOTOREXT) {\n this._motors[portID] = new BoostMotor(this, portID);\n }\n\n // Set input format for tilt or distance sensor\n let mode = null;\n let delta = 1;\n\n switch (type) {\n case BoostIO.MOTORINT:\n case BoostIO.MOTOREXT:\n mode = BoostMode.MOTOR_SENSOR;\n break;\n case BoostIO.COLOR:\n mode = BoostMode.COLOR;\n delta = 0;\n break;\n case BoostIO.LED:\n mode = BoostMode.LED;\n /**\n * Sets the LED to blue to give an indication on the hub\n * that it has connected successfully.\n */\n this.setLEDMode();\n this.setLED(0x0000FF);\n break;\n case BoostIO.TILT:\n mode = BoostMode.TILT;\n break;\n default:\n mode = BoostMode.UNKNOWN;\n }\n\n const cmd = this.generateInputCommand(\n portID,\n mode,\n delta,\n true // Receive feedback\n );\n\n this.send(BoostBLE.characteristic, cmd);\n }\n\n /**\n * Clear the sensors or motors present on the ports.\n * @param {number} portID - the port to clear.\n * @private\n */\n _clearPort (portID) {\n const type = this._ports[portID];\n if (type === BoostIO.TILT) {\n this._sensors.tiltX = this._sensors.tiltY = 0;\n }\n if (type === BoostIO.COLOR) {\n this._sensors.color = BoostColor.NONE;\n }\n this._ports[portID] = 'none';\n this._motors[portID] = null;\n }\n}\n\n/**\n * Enum for motor specification.\n * @readonly\n * @enum {string}\n */\nconst BoostMotorLabel = {\n A: 'A',\n B: 'B',\n C: 'C',\n D: 'D',\n AB: 'AB',\n ALL: 'ABCD'\n};\n\n/**\n * Enum for motor direction specification.\n * @readonly\n * @enum {string}\n */\nconst BoostMotorDirection = {\n FORWARD: 'this way',\n BACKWARD: 'that way',\n REVERSE: 'reverse'\n};\n\n/**\n * Enum for tilt sensor direction.\n * @readonly\n * @enum {string}\n */\nconst BoostTiltDirection = {\n UP: 'up',\n DOWN: 'down',\n LEFT: 'left',\n RIGHT: 'right',\n ANY: 'any'\n};\n\n/**\n * Scratch 3.0 blocks to interact with a LEGO Boost peripheral.\n */\nclass Scratch3BoostBlocks {\n\n /**\n * @return {string} - the ID of this extension.\n */\n static get EXTENSION_ID () {\n return 'boost';\n }\n\n /**\n * @return {number} - the tilt sensor counts as \"tilted\" if its tilt angle meets or exceeds this threshold.\n */\n static get TILT_THRESHOLD () {\n return 15;\n }\n\n /**\n * Construct a set of Boost blocks.\n * @param {Runtime} runtime - the Scratch 3.0 runtime.\n */\n constructor (runtime) {\n /**\n * The Scratch 3.0 runtime.\n * @type {Runtime}\n */\n this.runtime = runtime;\n\n // Create a new Boost peripheral instance\n this._peripheral = new Boost(this.runtime, Scratch3BoostBlocks.EXTENSION_ID);\n }\n\n /**\n * @returns {object} metadata for this extension and its blocks.\n */\n getInfo () {\n return {\n id: Scratch3BoostBlocks.EXTENSION_ID,\n name: 'BOOST',\n blockIconURI: iconURI,\n showStatusButton: true,\n blocks: [\n {\n opcode: 'motorOnFor',\n text: formatMessage({\n id: 'boost.motorOnFor',\n default: 'turn motor [MOTOR_ID] for [DURATION] seconds',\n description: 'turn a motor on for some time'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n MOTOR_ID: {\n type: ArgumentType.STRING,\n menu: 'MOTOR_ID',\n defaultValue: BoostMotorLabel.A\n },\n DURATION: {\n type: ArgumentType.NUMBER,\n defaultValue: 1\n }\n }\n },\n {\n opcode: 'motorOnForRotation',\n text: formatMessage({\n id: 'boost.motorOnForRotation',\n default: 'turn motor [MOTOR_ID] for [ROTATION] rotations',\n description: 'turn a motor on for rotation'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n MOTOR_ID: {\n type: ArgumentType.STRING,\n menu: 'MOTOR_ID',\n defaultValue: BoostMotorLabel.A\n },\n ROTATION: {\n type: ArgumentType.NUMBER,\n defaultValue: 1\n }\n }\n },\n {\n opcode: 'motorOn',\n text: formatMessage({\n id: 'boost.motorOn',\n default: 'turn motor [MOTOR_ID] on',\n description: 'turn a motor on indefinitely'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n MOTOR_ID: {\n type: ArgumentType.STRING,\n menu: 'MOTOR_ID',\n defaultValue: BoostMotorLabel.A\n }\n }\n },\n {\n opcode: 'motorOff',\n text: formatMessage({\n id: 'boost.motorOff',\n default: 'turn motor [MOTOR_ID] off',\n description: 'turn a motor off'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n MOTOR_ID: {\n type: ArgumentType.STRING,\n menu: 'MOTOR_ID',\n defaultValue: BoostMotorLabel.A\n }\n }\n },\n {\n opcode: 'setMotorPower',\n text: formatMessage({\n id: 'boost.setMotorPower',\n default: 'set motor [MOTOR_ID] speed to [POWER] %',\n description: 'set the motor\\'s speed without turning it on'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n MOTOR_ID: {\n type: ArgumentType.STRING,\n menu: 'MOTOR_ID',\n defaultValue: BoostMotorLabel.ALL\n },\n POWER: {\n type: ArgumentType.NUMBER,\n defaultValue: 100\n }\n }\n },\n {\n opcode: 'setMotorDirection',\n text: formatMessage({\n id: 'boost.setMotorDirection',\n default: 'set motor [MOTOR_ID] direction [MOTOR_DIRECTION]',\n description: 'set the motor\\'s turn direction without turning it on'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n MOTOR_ID: {\n type: ArgumentType.STRING,\n menu: 'MOTOR_ID',\n defaultValue: BoostMotorLabel.A\n },\n MOTOR_DIRECTION: {\n type: ArgumentType.STRING,\n menu: 'MOTOR_DIRECTION',\n defaultValue: BoostMotorDirection.FORWARD\n }\n }\n },\n {\n opcode: 'getMotorPosition',\n text: formatMessage({\n id: 'boost.getMotorPosition',\n default: 'motor [MOTOR_REPORTER_ID] position',\n description: 'the position returned by the motor'\n }),\n blockType: BlockType.REPORTER,\n arguments: {\n MOTOR_REPORTER_ID: {\n type: ArgumentType.STRING,\n menu: 'MOTOR_REPORTER_ID',\n defaultValue: BoostMotorLabel.A\n }\n }\n },\n {\n opcode: 'whenColor',\n text: formatMessage({\n id: 'boost.whenColor',\n default: 'when [COLOR] brick seen',\n description: 'check for when color'\n }),\n blockType: BlockType.HAT,\n arguments: {\n COLOR: {\n type: ArgumentType.STRING,\n menu: 'COLOR',\n defaultValue: BoostColor.ANY\n }\n }\n },\n {\n opcode: 'seeingColor',\n text: formatMessage({\n id: 'boost.seeingColor',\n default: 'seeing [COLOR] brick?',\n description: 'is the color sensor seeing a certain color?'\n }),\n blockType: BlockType.BOOLEAN,\n arguments: {\n COLOR: {\n type: ArgumentType.STRING,\n menu: 'COLOR',\n defaultValue: BoostColor.ANY\n }\n }\n },\n {\n opcode: 'whenTilted',\n text: formatMessage({\n id: 'boost.whenTilted',\n default: 'when tilted [TILT_DIRECTION_ANY]',\n description: 'check when tilted in a certain direction'\n }),\n func: 'isTilted',\n blockType: BlockType.HAT,\n arguments: {\n TILT_DIRECTION_ANY: {\n type: ArgumentType.STRING,\n menu: 'TILT_DIRECTION_ANY',\n defaultValue: BoostTiltDirection.ANY\n }\n }\n },\n {\n opcode: 'getTiltAngle',\n text: formatMessage({\n id: 'boost.getTiltAngle',\n default: 'tilt angle [TILT_DIRECTION]',\n description: 'the angle returned by the tilt sensor'\n }),\n blockType: BlockType.REPORTER,\n arguments: {\n TILT_DIRECTION: {\n type: ArgumentType.STRING,\n menu: 'TILT_DIRECTION',\n defaultValue: BoostTiltDirection.UP\n }\n }\n },\n {\n opcode: 'setLightHue',\n text: formatMessage({\n id: 'boost.setLightHue',\n default: 'set light color to [HUE]',\n description: 'set the LED color'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n HUE: {\n type: ArgumentType.NUMBER,\n defaultValue: 50\n }\n }\n }\n ],\n menus: {\n MOTOR_ID: {\n acceptReporters: true,\n items: [\n {\n text: 'A',\n value: BoostMotorLabel.A\n },\n {\n text: 'B',\n value: BoostMotorLabel.B\n },\n {\n text: 'C',\n value: BoostMotorLabel.C\n },\n {\n text: 'D',\n value: BoostMotorLabel.D\n },\n {\n text: 'AB',\n value: BoostMotorLabel.AB\n },\n {\n text: 'ABCD',\n value: BoostMotorLabel.ALL\n }\n ]\n },\n MOTOR_REPORTER_ID: {\n acceptReporters: true,\n items: [\n {\n text: 'A',\n value: BoostMotorLabel.A\n },\n {\n text: 'B',\n value: BoostMotorLabel.B\n },\n {\n text: 'C',\n value: BoostMotorLabel.C\n },\n {\n text: 'D',\n value: BoostMotorLabel.D\n }\n ]\n },\n MOTOR_DIRECTION: {\n acceptReporters: true,\n items: [\n {\n text: formatMessage({\n id: 'boost.motorDirection.forward',\n default: 'this way',\n description:\n 'label for forward element in motor direction menu for LEGO Boost extension'\n }),\n value: BoostMotorDirection.FORWARD\n },\n {\n text: formatMessage({\n id: 'boost.motorDirection.backward',\n default: 'that way',\n description:\n 'label for backward element in motor direction menu for LEGO Boost extension'\n }),\n value: BoostMotorDirection.BACKWARD\n },\n {\n text: formatMessage({\n id: 'boost.motorDirection.reverse',\n default: 'reverse',\n description:\n 'label for reverse element in motor direction menu for LEGO Boost extension'\n }),\n value: BoostMotorDirection.REVERSE\n }\n ]\n },\n TILT_DIRECTION: {\n acceptReporters: true,\n items: [\n {\n text: formatMessage({\n id: 'boost.tiltDirection.up',\n default: 'up',\n description: 'label for up element in tilt direction menu for LEGO Boost extension'\n }),\n value: BoostTiltDirection.UP\n },\n {\n text: formatMessage({\n id: 'boost.tiltDirection.down',\n default: 'down',\n description: 'label for down element in tilt direction menu for LEGO Boost extension'\n }),\n value: BoostTiltDirection.DOWN\n },\n {\n text: formatMessage({\n id: 'boost.tiltDirection.left',\n default: 'left',\n description: 'label for left element in tilt direction menu for LEGO Boost extension'\n }),\n value: BoostTiltDirection.LEFT\n },\n {\n text: formatMessage({\n id: 'boost.tiltDirection.right',\n default: 'right',\n description: 'label for right element in tilt direction menu for LEGO Boost extension'\n }),\n value: BoostTiltDirection.RIGHT\n }\n ]\n },\n TILT_DIRECTION_ANY: {\n acceptReporters: true,\n items: [\n {\n text: formatMessage({\n id: 'boost.tiltDirection.up',\n default: 'up'\n }),\n value: BoostTiltDirection.UP\n },\n {\n text: formatMessage({\n id: 'boost.tiltDirection.down',\n default: 'down'\n }),\n value: BoostTiltDirection.DOWN\n },\n {\n text: formatMessage({\n id: 'boost.tiltDirection.left',\n default: 'left'\n }),\n value: BoostTiltDirection.LEFT\n },\n {\n text: formatMessage({\n id: 'boost.tiltDirection.right',\n default: 'right'\n }),\n value: BoostTiltDirection.RIGHT\n },\n {\n text: formatMessage({\n id: 'boost.tiltDirection.any',\n default: 'any',\n description: 'label for any element in tilt direction menu for LEGO Boost extension'\n }),\n value: BoostTiltDirection.ANY\n }\n ]\n },\n COLOR: {\n acceptReporters: true,\n items: [\n {\n text: formatMessage({\n id: 'boost.color.red',\n default: 'red',\n description: 'the color red'\n }),\n value: BoostColor.RED\n },\n {\n text: formatMessage({\n id: 'boost.color.blue',\n default: 'blue',\n description: 'the color blue'\n }),\n value: BoostColor.BLUE\n },\n {\n text: formatMessage({\n id: 'boost.color.green',\n default: 'green',\n description: 'the color green'\n }),\n value: BoostColor.GREEN\n },\n {\n text: formatMessage({\n id: 'boost.color.yellow',\n default: 'yellow',\n description: 'the color yellow'\n }),\n value: BoostColor.YELLOW\n },\n {\n text: formatMessage({\n id: 'boost.color.white',\n default: 'white',\n desription: 'the color white'\n }),\n value: BoostColor.WHITE\n },\n {\n text: formatMessage({\n id: 'boost.color.black',\n default: 'black',\n description: 'the color black'\n }),\n value: BoostColor.BLACK\n },\n {\n text: formatMessage({\n id: 'boost.color.any',\n default: 'any color',\n description: 'any color'\n }),\n value: BoostColor.ANY\n }\n ]\n }\n }\n };\n }\n\n /**\n * Turn specified motor(s) on for a specified duration.\n * @param {object} args - the block's arguments.\n * @property {MotorID} MOTOR_ID - the motor(s) to activate.\n * @property {int} DURATION - the amount of time to run the motors.\n * @return {Promise} - a promise which will resolve at the end of the duration.\n */\n motorOnFor (args) {\n // TODO: cast args.MOTOR_ID?\n let durationMS = Cast.toNumber(args.DURATION) * 1000;\n durationMS = MathUtil.clamp(durationMS, 0, 15000);\n return new Promise(resolve => {\n this._forEachMotor(args.MOTOR_ID, motorIndex => {\n const motor = this._peripheral.motor(motorIndex);\n if (motor) motor.turnOnFor(durationMS);\n });\n\n // Run for some time even when no motor is connected\n setTimeout(resolve, durationMS);\n });\n }\n\n /**\n * Turn specified motor(s) on for a specified rotation in full rotations.\n * @param {object} args - the block's arguments.\n * @property {MotorID} MOTOR_ID - the motor(s) to activate.\n * @property {int} ROTATION - the amount of full rotations to turn the motors.\n * @return {Promise} - a promise which will resolve at the end of the duration.\n */\n motorOnForRotation (args) {\n // TODO: cast args.MOTOR_ID?\n let degrees = Cast.toNumber(args.ROTATION) * 360;\n // TODO: Clamps to 100 rotations. Consider changing.\n const sign = Math.sign(degrees);\n degrees = Math.abs(MathUtil.clamp(degrees, -360000, 360000));\n\n const motors = [];\n this._forEachMotor(args.MOTOR_ID, motorIndex => {\n motors.push(motorIndex);\n });\n\n /**\n * Checks that the motors given in args.MOTOR_ID exist,\n * and maps a promise for each of the motor-commands to an array.\n */\n const promises = motors.map(portID => {\n const motor = this._peripheral.motor(portID);\n if (motor) {\n // to avoid a hanging block if power is 0, return an immediately resolving promise.\n if (motor.power === 0) return Promise.resolve();\n return new Promise(resolve => {\n motor.turnOnForDegrees(degrees, sign);\n motor.pendingRotationPromise = resolve;\n });\n }\n return null;\n });\n /**\n * Make sure all promises are resolved, i.e. all motor-commands have completed.\n * To prevent the block from returning a value, an empty function is added to the .then\n */\n return Promise.all(promises).then(() => {});\n }\n\n /**\n * Turn specified motor(s) on indefinitely.\n * @param {object} args - the block's arguments.\n * @property {MotorID} MOTOR_ID - the motor(s) to activate.\n * @return {Promise} - a Promise that resolves after some delay.\n */\n motorOn (args) {\n // TODO: cast args.MOTOR_ID?\n this._forEachMotor(args.MOTOR_ID, motorIndex => {\n const motor = this._peripheral.motor(motorIndex);\n if (motor) motor.turnOnForever();\n });\n\n return new Promise(resolve => {\n window.setTimeout(() => {\n resolve();\n }, BoostBLE.sendInterval);\n });\n }\n\n /**\n * Turn specified motor(s) off.\n * @param {object} args - the block's arguments.\n * @property {MotorID} MOTOR_ID - the motor(s) to deactivate.\n * @return {Promise} - a Promise that resolves after some delay.\n */\n motorOff (args) {\n // TODO: cast args.MOTOR_ID?\n this._forEachMotor(args.MOTOR_ID, motorIndex => {\n const motor = this._peripheral.motor(motorIndex);\n if (motor) motor.turnOff();\n });\n\n return new Promise(resolve => {\n window.setTimeout(() => {\n resolve();\n }, BoostBLE.sendInterval);\n });\n }\n\n /**\n * Set the power level of the specified motor(s).\n * @param {object} args - the block's arguments.\n * @property {MotorID} MOTOR_ID - the motor(s) to be affected.\n * @property {int} POWER - the new power level for the motor(s).\n * @return {Promise} - returns a promise to make sure the block yields.\n */\n setMotorPower (args) {\n // TODO: cast args.MOTOR_ID?\n this._forEachMotor(args.MOTOR_ID, motorIndex => {\n const motor = this._peripheral.motor(motorIndex);\n if (motor) {\n motor.power = MathUtil.clamp(Cast.toNumber(args.POWER), 0, 100);\n switch (motor.status) {\n case BoostMotorState.ON_FOREVER:\n motor.turnOnForever();\n break;\n case BoostMotorState.ON_FOR_TIME:\n motor.turnOnFor(motor.pendingDurationTimeoutStartTime +\n motor.pendingDurationTimeoutDelay - Date.now());\n break;\n }\n }\n });\n return new Promise(resolve => {\n window.setTimeout(() => {\n resolve();\n }, BoostBLE.sendInterval);\n });\n }\n\n /**\n * Set the direction of rotation for specified motor(s).\n * If the direction is 'reverse' the motor(s) will be reversed individually.\n * @param {object} args - the block's arguments.\n * @property {MotorID} MOTOR_ID - the motor(s) to be affected.\n * @property {MotorDirection} MOTOR_DIRECTION - the new direction for the motor(s).\n * @return {Promise} - returns a promise to make sure the block yields.\n */\n setMotorDirection (args) {\n // TODO: cast args.MOTOR_ID?\n this._forEachMotor(args.MOTOR_ID, motorIndex => {\n const motor = this._peripheral.motor(motorIndex);\n if (motor) {\n switch (args.MOTOR_DIRECTION) {\n case BoostMotorDirection.FORWARD:\n motor.direction = 1;\n break;\n case BoostMotorDirection.BACKWARD:\n motor.direction = -1;\n break;\n case BoostMotorDirection.REVERSE:\n motor.direction = -motor.direction;\n break;\n default:\n log.warn(`Unknown motor direction in setMotorDirection: ${args.DIRECTION}`);\n break;\n }\n // keep the motor on if it's running, and update the pending timeout if needed\n if (motor) {\n switch (motor.status) {\n case BoostMotorState.ON_FOREVER:\n motor.turnOnForever();\n break;\n case BoostMotorState.ON_FOR_TIME:\n motor.turnOnFor(motor.pendingDurationTimeoutStartTime +\n motor.pendingDurationTimeoutDelay - Date.now());\n break;\n }\n }\n }\n });\n return new Promise(resolve => {\n window.setTimeout(() => {\n resolve();\n }, BoostBLE.sendInterval);\n });\n }\n\n /**\n * @param {object} args - the block's arguments.\n * @return {number} - returns the motor's position.\n */\n getMotorPosition (args) {\n let portID = null;\n switch (args.MOTOR_REPORTER_ID) {\n\n case BoostMotorLabel.A:\n portID = BoostPort.A;\n break;\n case BoostMotorLabel.B:\n portID = BoostPort.B;\n break;\n case BoostMotorLabel.C:\n portID = BoostPort.C;\n break;\n case BoostMotorLabel.D:\n portID = BoostPort.D;\n break;\n default:\n log.warn('Asked for a motor position that doesnt exist!');\n return false;\n }\n if (portID !== null && this._peripheral.motor(portID)) {\n let val = this._peripheral.motor(portID).position;\n // Boost motor A position direction is reversed by design\n // so we have to reverse the position here\n if (portID === BoostPort.A) {\n val *= -1;\n }\n return MathUtil.wrapClamp(val, 0, 360);\n }\n return 0;\n }\n\n /**\n * Call a callback for each motor indexed by the provided motor ID.\n * @param {MotorID} motorID - the ID specifier.\n * @param {Function} callback - the function to call with the numeric motor index for each motor.\n * @private\n */\n _forEachMotor (motorID, callback) {\n let motors;\n switch (motorID) {\n case BoostMotorLabel.A:\n motors = [BoostPort.A];\n break;\n case BoostMotorLabel.B:\n motors = [BoostPort.B];\n break;\n case BoostMotorLabel.C:\n motors = [BoostPort.C];\n break;\n case BoostMotorLabel.D:\n motors = [BoostPort.D];\n break;\n case BoostMotorLabel.AB:\n motors = [BoostPort.A, BoostPort.B];\n break;\n case BoostMotorLabel.ALL:\n motors = [BoostPort.A, BoostPort.B, BoostPort.C, BoostPort.D];\n break;\n default:\n log.warn(`Invalid motor ID: ${motorID}`);\n motors = [];\n break;\n }\n for (const index of motors) {\n callback(index);\n }\n }\n\n /**\n * Test whether the tilt sensor is currently tilted.\n * @param {object} args - the block's arguments.\n * @property {TiltDirection} TILT_DIRECTION_ANY - the tilt direction to test (up, down, left, right, or any).\n * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction.\n */\n whenTilted (args) {\n return this._isTilted(args.TILT_DIRECTION_ANY);\n }\n\n /**\n * Test whether the tilt sensor is currently tilted.\n * @param {object} args - the block's arguments.\n * @property {TiltDirection} TILT_DIRECTION_ANY - the tilt direction to test (up, down, left, right, or any).\n * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction.\n */\n isTilted (args) {\n return this._isTilted(args.TILT_DIRECTION_ANY);\n }\n\n /**\n * @param {object} args - the block's arguments.\n * @property {TiltDirection} TILT_DIRECTION - the direction (up, down, left, right) to check.\n * @return {number} - the tilt sensor's angle in the specified direction.\n * Note that getTiltAngle(up) = -getTiltAngle(down) and getTiltAngle(left) = -getTiltAngle(right).\n */\n getTiltAngle (args) {\n return this._getTiltAngle(args.TILT_DIRECTION);\n }\n\n /**\n * Test whether the tilt sensor is currently tilted.\n * @param {TiltDirection} direction - the tilt direction to test (up, down, left, right, or any).\n * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction.\n * @private\n */\n _isTilted (direction) {\n switch (direction) {\n case BoostTiltDirection.ANY:\n return (Math.abs(this._peripheral.tiltX) >= Scratch3BoostBlocks.TILT_THRESHOLD) ||\n (Math.abs(this._peripheral.tiltY) >= Scratch3BoostBlocks.TILT_THRESHOLD);\n default:\n return this._getTiltAngle(direction) >= Scratch3BoostBlocks.TILT_THRESHOLD;\n }\n }\n\n /**\n * @param {TiltDirection} direction - the direction (up, down, left, right) to check.\n * @return {number} - the tilt sensor's angle in the specified direction.\n * Note that getTiltAngle(up) = -getTiltAngle(down) and getTiltAngle(left) = -getTiltAngle(right).\n * @private\n */\n _getTiltAngle (direction) {\n switch (direction) {\n case BoostTiltDirection.UP:\n return this._peripheral.tiltY > 90 ? 256 - this._peripheral.tiltY : -this._peripheral.tiltY;\n case BoostTiltDirection.DOWN:\n return this._peripheral.tiltY > 90 ? this._peripheral.tiltY - 256 : this._peripheral.tiltY;\n case BoostTiltDirection.LEFT:\n return this._peripheral.tiltX > 90 ? this._peripheral.tiltX - 256 : this._peripheral.tiltX;\n case BoostTiltDirection.RIGHT:\n return this._peripheral.tiltX > 90 ? 256 - this._peripheral.tiltX : -this._peripheral.tiltX;\n default:\n log.warn(`Unknown tilt direction in _getTiltAngle: ${direction}`);\n }\n }\n\n /**\n * Edge-triggering hat function, for when the vision sensor is detecting\n * a certain color.\n * @param {object} args - the block's arguments.\n * @return {boolean} - true when the color sensor senses the specified color.\n */\n whenColor (args) {\n if (args.COLOR === BoostColor.ANY) {\n // For \"any\" color, return true if the color is not \"none\", and\n // the color is different from the previous color detected. This\n // allows the hat to trigger when the color changes from one color\n // to another.\n return this._peripheral.color !== BoostColor.NONE &&\n this._peripheral.color !== this._peripheral.previousColor;\n }\n\n return args.COLOR === this._peripheral.color;\n }\n\n /**\n * A boolean reporter function, for whether the vision sensor is detecting\n * a certain color.\n * @param {object} args - the block's arguments.\n * @return {boolean} - true when the color sensor senses the specified color.\n */\n seeingColor (args) {\n if (args.COLOR === BoostColor.ANY) {\n return this._peripheral.color !== BoostColor.NONE;\n }\n\n return args.COLOR === this._peripheral.color;\n }\n\n /**\n * Set the LED's hue.\n * @param {object} args - the block's arguments.\n * @property {number} HUE - the hue to set, in the range [0,100].\n * @return {Promise} - a Promise that resolves after some delay.\n */\n setLightHue (args) {\n // Convert from [0,100] to [0,360]\n let inputHue = Cast.toNumber(args.HUE);\n inputHue = MathUtil.wrapClamp(inputHue, 0, 100);\n const hue = inputHue * 360 / 100;\n\n const rgbObject = color.hsvToRgb({h: hue, s: 1, v: 1});\n\n const rgbDecimal = color.rgbToDecimal(rgbObject);\n\n this._peripheral._led = inputHue;\n this._peripheral.setLED(rgbDecimal);\n\n return new Promise(resolve => {\n window.setTimeout(() => {\n resolve();\n }, BoostBLE.sendInterval);\n });\n }\n}\n\nmodule.exports = Scratch3BoostBlocks;\n","const ArgumentType = require('../../extension-support/argument-type');\nconst BlockType = require('../../extension-support/block-type');\nconst Cast = require('../../util/cast');\nconst formatMessage = require('format-message');\nconst uid = require('../../util/uid');\nconst BT = require('../../io/bt');\nconst Base64Util = require('../../util/base64-util');\nconst MathUtil = require('../../util/math-util');\nconst RateLimiter = require('../../util/rateLimiter.js');\nconst log = require('../../util/log');\n\n/**\n * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.\n * @type {string}\n */\n// eslint-disable-next-line max-len\nconst blockIconURI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSIwIDAgNDAgNDAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUwLjIgKDU1MDQ3KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5ldjMtYmxvY2staWNvbjwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxkZWZzPjwvZGVmcz4KICAgIDxnIGlkPSJldjMtYmxvY2staWNvbiIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9ImV2MyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNS41MDAwMDAsIDMuNTAwMDAwKSIgZmlsbC1ydWxlPSJub256ZXJvIj4KICAgICAgICAgICAgPHJlY3QgaWQ9IlJlY3RhbmdsZS1wYXRoIiBzdHJva2U9IiM3Qzg3QTUiIGZpbGw9IiNGRkZGRkYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgeD0iMC41IiB5PSIzLjU5IiB3aWR0aD0iMjgiIGhlaWdodD0iMjUuODEiIHJ4PSIxIj48L3JlY3Q+CiAgICAgICAgICAgIDxyZWN0IGlkPSJSZWN0YW5nbGUtcGF0aCIgc3Ryb2tlPSIjN0M4N0E1IiBmaWxsPSIjRTZFN0U4IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHg9IjIuNSIgeT0iMC41IiB3aWR0aD0iMjQiIGhlaWdodD0iMzIiIHJ4PSIxIj48L3JlY3Q+CiAgICAgICAgICAgIDxyZWN0IGlkPSJSZWN0YW5nbGUtcGF0aCIgc3Ryb2tlPSIjN0M4N0E1IiBmaWxsPSIjRkZGRkZGIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHg9IjIuNSIgeT0iMTQuNSIgd2lkdGg9IjI0IiBoZWlnaHQ9IjEzIj48L3JlY3Q+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNC41LDEwLjUgTDE0LjUsMTQuNSIgaWQ9IlNoYXBlIiBzdHJva2U9IiM3Qzg3QTUiIGZpbGw9IiNFNkU3RTgiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PC9wYXRoPgogICAgICAgICAgICA8cmVjdCBpZD0iUmVjdGFuZ2xlLXBhdGgiIGZpbGw9IiM0MTQ3NTciIHg9IjQuNSIgeT0iMi41IiB3aWR0aD0iMjAiIGhlaWdodD0iMTAiIHJ4PSIxIj48L3JlY3Q+CiAgICAgICAgICAgIDxyZWN0IGlkPSJSZWN0YW5nbGUtcGF0aCIgZmlsbD0iIzdDODdBNSIgb3BhY2l0eT0iMC41IiB4PSIxMy41IiB5PSIyMC4xMyIgd2lkdGg9IjIiIGhlaWdodD0iMiIgcng9IjAuNSI+PC9yZWN0PgogICAgICAgICAgICA8cGF0aCBkPSJNOS4wNiwyMC4xMyBMMTAuNTYsMjAuMTMgQzEwLjgzNjE0MjQsMjAuMTMgMTEuMDYsMjAuMzUzODU3NiAxMS4wNiwyMC42MyBMMTEuMDYsMjEuNjMgQzExLjA2LDIxLjkwNjE0MjQgMTAuODM2MTQyNCwyMi4xMyAxMC41NiwyMi4xMyBMOS4wNiwyMi4xMyBDOC41MDc3MTUyNSwyMi4xMyA4LjA2LDIxLjY4MjI4NDcgOC4wNiwyMS4xMyBDOC4wNiwyMC41Nzc3MTUzIDguNTA3NzE1MjUsMjAuMTMgOS4wNiwyMC4xMyBaIiBpZD0iU2hhcGUiIGZpbGw9IiM3Qzg3QTUiIG9wYWNpdHk9IjAuNSI+PC9wYXRoPgogICAgICAgICAgICA8cGF0aCBkPSJNMTguOTEsMjAuMTMgTDIwLjQyLDIwLjEzIEMyMC42OTYxNDI0LDIwLjEzIDIwLjkyLDIwLjM1Mzg1NzYgMjAuOTIsMjAuNjMgTDIwLjkyLDIxLjYzIEMyMC45MiwyMS45MDYxNDI0IDIwLjY5NjE0MjQsMjIuMTMgMjAuNDIsMjIuMTMgTDE4LjkyLDIyLjEzIEMxOC4zNjc3MTUzLDIyLjEzIDE3LjkyLDIxLjY4MjI4NDcgMTcuOTIsMjEuMTMgQzE3LjkxOTk3MjYsMjAuNTgxNTk3IDE4LjM2MTYyNDUsMjAuMTM1NDg0IDE4LjkxLDIwLjEzIFoiIGlkPSJTaGFwZSIgZmlsbD0iIzdDODdBNSIgb3BhY2l0eT0iMC41IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxOS40MjAwMDAsIDIxLjEzMDAwMCkgcm90YXRlKC0xODAuMDAwMDAwKSB0cmFuc2xhdGUoLTE5LjQyMDAwMCwgLTIxLjEzMDAwMCkgIj48L3BhdGg+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik04LjIzLDE3LjUgTDUsMTcuNSBDNC43MjM4NTc2MywxNy41IDQuNSwxNy4yNzYxNDI0IDQuNSwxNyBMNC41LDE0LjUgTDEwLjUsMTQuNSBMOC42NSwxNy4yOCBDOC41NTQ2Njk2MSwxNy40MTc5MDgyIDguMzk3NjUwMDYsMTcuNTAwMTU2NiA4LjIzLDE3LjUgWiIgaWQ9IlNoYXBlIiBmaWxsPSIjN0M4N0E1IiBvcGFjaXR5PSIwLjUiPjwvcGF0aD4KICAgICAgICAgICAgPHBhdGggZD0iTTE4LjE1LDE4Ljg1IEwxNy42NSwxOS4zNSBDMTcuNTUyMzQxNiwxOS40NDQwNzU2IDE3LjQ5ODAzMzksMTkuNTc0NDE0MiAxNy41LDE5LjcxIEwxNy41LDIwIEMxNy41LDIwLjI3NjE0MjQgMTcuMjc2MTQyNCwyMC41IDE3LDIwLjUgTDE2LjUsMjAuNSBDMTYuMjIzODU3NiwyMC41IDE2LDIwLjI3NjE0MjQgMTYsMjAgQzE2LDE5LjcyMzg1NzYgMTUuNzc2MTQyNCwxOS41IDE1LjUsMTkuNSBMMTMuNSwxOS41IEMxMy4yMjM4NTc2LDE5LjUgMTMsMTkuNzIzODU3NiAxMywyMCBDMTMsMjAuMjc2MTQyNCAxMi43NzYxNDI0LDIwLjUgMTIuNSwyMC41IEwxMiwyMC41IEMxMS43MjM4NTc2LDIwLjUgMTEuNSwyMC4yNzYxNDI0IDExLjUsMjAgTDExLjUsMTkuNzEgQzExLjUwMTk2NjEsMTkuNTc0NDE0MiAxMS40NDc2NTg0LDE5LjQ0NDA3NTYgMTEuMzUsMTkuMzUgTDEwLjg1LDE4Ljg1IEMxMC42NTgyMTY3LDE4LjY1MjE4NjMgMTAuNjU4MjE2NywxOC4zMzc4MTM3IDEwLjg1LDE4LjE0IEwxMi4zNiwxNi42NSBDMTIuNDUwMjgwMywxNi41NTI4NjE3IDEyLjU3NzM5NjEsMTYuNDk4MzgzNSAxMi43MSwxNi41IEwxNi4yOSwxNi41IEMxNi40MjI2MDM5LDE2LjQ5ODM4MzUgMTYuNTQ5NzE5NywxNi41NTI4NjE3IDE2LjY0LDE2LjY1IEwxOC4xNSwxOC4xNCBDMTguMzQxNzgzMywxOC4zMzc4MTM3IDE4LjM0MTc4MzMsMTguNjUyMTg2MyAxOC4xNSwxOC44NSBaIiBpZD0iU2hhcGUiIGZpbGw9IiM3Qzg3QTUiIG9wYWNpdHk9IjAuNSI+PC9wYXRoPgogICAgICAgICAgICA8cGF0aCBkPSJNMTAuODUsMjMuNDUgTDExLjM1LDIyLjk1IEMxMS40NDc2NTg0LDIyLjg1NTkyNDQgMTEuNTAxOTY2MSwyMi43MjU1ODU4IDExLjUsMjIuNTkgTDExLjUsMjIuMyBDMTEuNSwyMi4wMjM4NTc2IDExLjcyMzg1NzYsMjEuOCAxMiwyMS44IEwxMi41LDIxLjggQzEyLjc3NjE0MjQsMjEuOCAxMywyMi4wMjM4NTc2IDEzLDIyLjMgQzEzLDIyLjU3NjE0MjQgMTMuMjIzODU3NiwyMi44IDEzLjUsMjIuOCBMMTUuNSwyMi44IEMxNS43NzYxNDI0LDIyLjggMTYsMjIuNTc2MTQyNCAxNiwyMi4zIEMxNiwyMi4wMjM4NTc2IDE2LjIyMzg1NzYsMjEuOCAxNi41LDIxLjggTDE3LDIxLjggQzE3LjI3NjE0MjQsMjEuOCAxNy41LDIyLjAyMzg1NzYgMTcuNSwyMi4zIEwxNy41LDIyLjU5IEMxNy40OTgwMzM5LDIyLjcyNTU4NTggMTcuNTUyMzQxNiwyMi44NTU5MjQ0IDE3LjY1LDIyLjk1IEwxOC4xNSwyMy40NSBDMTguMzQwNTcxNCwyMy42NDQ0MjE4IDE4LjM0MDU3MTQsMjMuOTU1NTc4MiAxOC4xNSwyNC4xNSBMMTYuNjQsMjUuNjUgQzE2LjU0OTcxOTcsMjUuNzQ3MTM4MyAxNi40MjI2MDM5LDI1LjgwMTYxNjUgMTYuMjksMjUuOCBMMTIuNzEsMjUuOCBDMTIuNTc3Mzk2MSwyNS44MDE2MTY1IDEyLjQ1MDI4MDMsMjUuNzQ3MTM4MyAxMi4zNiwyNS42NSBMMTAuODUsMjQuMTUgQzEwLjY1OTQyODYsMjMuOTU1NTc4MiAxMC42NTk0Mjg2LDIzLjY0NDQyMTggMTAuODUsMjMuNDUgWiIgaWQ9IlNoYXBlIiBmaWxsPSIjN0M4N0E1IiBvcGFjaXR5PSIwLjUiPjwvcGF0aD4KICAgICAgICAgICAgPHBhdGggZD0iTTIxLjUsMjcuNSBMMjYuNSwyNy41IEwyNi41LDMxLjUgQzI2LjUsMzIuMDUyMjg0NyAyNi4wNTIyODQ3LDMyLjUgMjUuNSwzMi41IEwyMS41LDMyLjUgTDIxLjUsMjcuNSBaIiBpZD0iU2hhcGUiIHN0cm9rZT0iI0NDNEMyMyIgZmlsbD0iI0YxNUEyOSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48L3BhdGg+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4=';\n\n/**\n * String with Ev3 expected pairing pin.\n * @readonly\n */\nconst Ev3PairingPin = '1234';\n\n/**\n * A maximum number of BT message sends per second, to be enforced by the rate limiter.\n * @type {number}\n */\nconst BTSendRateMax = 40;\n\n/**\n * Enum for Ev3 parameter encodings of various argument and return values.\n * Found in the 'EV3 Firmware Developer Kit', section4, page 9, at\n * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits.\n *\n * The format for these values is:\n * 0xxxxxxx for Short Format\n * 1ttt-bbb for Long Format\n *\n * @readonly\n * @enum {number}\n */\nconst Ev3Encoding = {\n ONE_BYTE: 0x81, // = 0b1000-001, \"1 byte to follow\"\n TWO_BYTES: 0x82, // = 0b1000-010, \"2 bytes to follow\"\n FOUR_BYTES: 0x83, // = 0b1000-011, \"4 bytes to follow\"\n GLOBAL_VARIABLE_ONE_BYTE: 0xE1, // = 0b1110-001, \"1 byte to follow\"\n GLOBAL_CONSTANT_INDEX_0: 0x20, // = 0b00100000\n GLOBAL_VARIABLE_INDEX_0: 0x60 // = 0b01100000\n};\n\n/**\n * Enum for Ev3 direct command types.\n * Found in the 'EV3 Communication Developer Kit', section 4, page 24, at\n * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits.\n * @readonly\n * @enum {number}\n */\nconst Ev3Command = {\n DIRECT_COMMAND_REPLY: 0x00,\n DIRECT_COMMAND_NO_REPLY: 0x80,\n DIRECT_REPLY: 0x02\n};\n\n/**\n * Enum for Ev3 commands opcodes.\n * Found in the 'EV3 Firmware Developer Kit', section 4, page 10, at\n * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits.\n * @readonly\n * @enum {number}\n */\nconst Ev3Opcode = {\n OPOUTPUT_STEP_SPEED: 0xAE,\n OPOUTPUT_TIME_SPEED: 0xAF,\n OPOUTPUT_STOP: 0xA3,\n OPOUTPUT_RESET: 0xA2,\n OPOUTPUT_STEP_SYNC: 0xB0,\n OPOUTPUT_TIME_SYNC: 0xB1,\n OPOUTPUT_GET_COUNT: 0xB3,\n OPSOUND: 0x94,\n OPSOUND_CMD_TONE: 1,\n OPSOUND_CMD_STOP: 0,\n OPINPUT_DEVICE_LIST: 0x98,\n OPINPUT_READSI: 0x9D\n};\n\n/**\n * Enum for Ev3 values used as arguments to various opcodes.\n * Found in the 'EV3 Firmware Developer Kit', section4, page 10-onwards, at\n * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits.\n * @readonly\n * @enum {number}\n */\nconst Ev3Args = {\n LAYER: 0, // always 0, chained EV3s not supported\n COAST: 0,\n BRAKE: 1,\n RAMP: 50, // time in milliseconds\n DO_NOT_CHANGE_TYPE: 0,\n MAX_DEVICES: 32 // 'Normally 32' from pg. 46\n};\n\n/**\n * Enum for Ev3 device type numbers.\n * Found in the 'EV3 Firmware Developer Kit', section 5, page 100, at\n * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits.\n * @readonly\n * @enum {string}\n */\nconst Ev3Device = {\n 29: 'color',\n 30: 'ultrasonic',\n 32: 'gyro',\n 16: 'touch',\n 8: 'mediumMotor',\n 7: 'largeMotor',\n 126: 'none',\n 125: 'none'\n};\n\n/**\n * Enum for Ev3 device modes.\n * Found in the 'EV3 Firmware Developer Kit', section 5, page 100, at\n * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits.\n * @readonly\n * @enum {number}\n */\nconst Ev3Mode = {\n touch: 0, // touch\n color: 1, // ambient\n ultrasonic: 1, // inch\n none: 0\n};\n\n/**\n * Enum for Ev3 device labels used in the Scratch blocks/UI.\n * @readonly\n * @enum {string}\n */\nconst Ev3Label = {\n touch: 'button',\n color: 'brightness',\n ultrasonic: 'distance'\n};\n\n/**\n * Manage power, direction, and timers for one EV3 motor.\n */\nclass EV3Motor {\n\n /**\n * Construct a EV3 Motor instance, which could be of type 'largeMotor' or\n * 'mediumMotor'.\n *\n * @param {EV3} parent - the EV3 peripheral which owns this motor.\n * @param {int} index - the zero-based index of this motor on its parent peripheral.\n * @param {string} type - the type of motor (i.e. 'largeMotor' or 'mediumMotor').\n */\n constructor (parent, index, type) {\n /**\n * The EV3 peripheral which owns this motor.\n * @type {EV3}\n * @private\n */\n this._parent = parent;\n\n /**\n * The zero-based index of this motor on its parent peripheral.\n * @type {int}\n * @private\n */\n this._index = index;\n\n /**\n * The type of EV3 motor this could be: 'largeMotor' or 'mediumMotor'.\n * @type {string}\n * @private\n */\n this._type = type;\n\n /**\n * This motor's current direction: 1 for \"clockwise\" or -1 for \"counterclockwise\"\n * @type {number}\n * @private\n */\n this._direction = 1;\n\n /**\n * This motor's current power level, in the range [0,100].\n * @type {number}\n * @private\n */\n this._power = 50;\n\n /**\n * This motor's current position, in the range [0,360].\n * @type {number}\n * @private\n */\n this._position = 0;\n\n /**\n * An ID for the current coast command, to help override multiple coast\n * commands sent in succession.\n * @type {number}\n * @private\n */\n this._commandID = null;\n\n /**\n * A delay, in milliseconds, to add to coasting, to make sure that a brake\n * first takes effect if one was sent.\n * @type {number}\n * @private\n */\n this._coastDelay = 1000;\n }\n\n /**\n * @return {string} - this motor's type: 'largeMotor' or 'mediumMotor'\n */\n get type () {\n return this._type;\n }\n\n /**\n * @param {string} value - this motor's new type: 'largeMotor' or 'mediumMotor'\n */\n set type (value) {\n this._type = value;\n }\n\n /**\n * @return {int} - this motor's current direction: 1 for \"clockwise\" or -1 for \"counterclockwise\"\n */\n get direction () {\n return this._direction;\n }\n\n /**\n * @param {int} value - this motor's new direction: 1 for \"clockwise\" or -1 for \"counterclockwise\"\n */\n set direction (value) {\n if (value < 0) {\n this._direction = -1;\n } else {\n this._direction = 1;\n }\n }\n\n /**\n * @return {int} - this motor's current power level, in the range [0,100].\n */\n get power () {\n return this._power;\n }\n\n /**\n * @param {int} value - this motor's new power level, in the range [0,100].\n */\n set power (value) {\n this._power = value;\n }\n\n /**\n * @return {int} - this motor's current position, in the range [-inf,inf].\n */\n get position () {\n return this._position;\n }\n\n /**\n * @param {int} array - this motor's new position, in the range [0,360].\n */\n set position (array) {\n // tachoValue from Paula\n let value = array[0] + (array[1] * 256) + (array[2] * 256 * 256) + (array[3] * 256 * 256 * 256);\n if (value > 0x7fffffff) {\n value = value - 0x100000000;\n }\n this._position = value;\n }\n\n /**\n * Turn this motor on for a specific duration.\n * Found in the 'EV3 Firmware Developer Kit', page 56, at\n * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits.\n *\n * Opcode arguments:\n * (Data8) LAYER – Specify chain layer number [0 - 3]\n * (Data8) NOS – Output bit field [0x00 – 0x0F]\n * (Data8) SPEED – Power level, [-100 – 100]\n * (Data32) STEP1 – Time in milliseconds for ramp up\n * (Data32) STEP2 – Time in milliseconds for continues run\n * (Data32) STEP3 – Time in milliseconds for ramp down\n * (Data8) BRAKE - Specify break level [0: Float, 1: Break]\n *\n * @param {number} milliseconds - run the motor for this long.\n */\n turnOnFor (milliseconds) {\n if (this._power === 0) return;\n\n const port = this._portMask(this._index);\n let n = milliseconds;\n let speed = this._power * this._direction;\n const ramp = Ev3Args.RAMP;\n\n let byteCommand = [];\n byteCommand[0] = Ev3Opcode.OPOUTPUT_TIME_SPEED;\n\n // If speed is less than zero, make it positive and multiply the input\n // value by -1\n if (speed < 0) {\n speed = -1 * speed;\n n = -1 * n;\n }\n // If the input value is less than 0\n const dir = (n < 0) ? 0x100 - speed : speed; // step negative or positive\n n = Math.abs(n);\n // Setup motor run duration and ramping behavior\n let rampup = ramp;\n let rampdown = ramp;\n let run = n - (ramp * 2);\n if (run < 0) {\n rampup = Math.floor(n / 2);\n run = 0;\n rampdown = n - rampup;\n }\n // Generate motor command values\n const runcmd = this._runValues(run);\n byteCommand = byteCommand.concat([\n Ev3Args.LAYER,\n port,\n Ev3Encoding.ONE_BYTE,\n dir & 0xff,\n Ev3Encoding.ONE_BYTE,\n rampup\n ]).concat(runcmd.concat([\n Ev3Encoding.ONE_BYTE,\n rampdown,\n Ev3Args.BRAKE\n ]));\n\n const cmd = this._parent.generateCommand(\n Ev3Command.DIRECT_COMMAND_NO_REPLY,\n byteCommand\n );\n\n this._parent.send(cmd);\n\n this.coastAfter(milliseconds);\n }\n\n /**\n * Set the motor to coast after a specified amount of time.\n * @param {number} time - the time in milliseconds.\n */\n coastAfter (time) {\n if (this._power === 0) return;\n\n // Set the motor command id to check before starting coast\n const commandId = uid();\n this._commandID = commandId;\n\n // Send coast message\n setTimeout(() => {\n // Do not send coast if another motor command changed the command id.\n if (this._commandID === commandId) {\n this.coast();\n this._commandID = null;\n }\n }, time + this._coastDelay); // add a delay so the brake takes effect\n }\n\n /**\n * Set the motor to coast.\n */\n coast () {\n if (this._power === 0) return;\n\n const cmd = this._parent.generateCommand(\n Ev3Command.DIRECT_COMMAND_NO_REPLY,\n [\n Ev3Opcode.OPOUTPUT_STOP,\n Ev3Args.LAYER,\n this._portMask(this._index), // port output bit field\n Ev3Args.COAST\n ]\n );\n\n this._parent.send(cmd, false); // don't use rate limiter to ensure motor stops\n }\n\n /**\n * Generate motor run values for a given input.\n * @param {number} run - run input.\n * @return {array} - run values as a byte array.\n */\n _runValues (run) {\n // If run duration is less than max 16-bit integer\n if (run < 0x7fff) {\n return [\n Ev3Encoding.TWO_BYTES,\n run & 0xff,\n (run >> 8) & 0xff\n ];\n }\n\n // Run forever\n return [\n Ev3Encoding.FOUR_BYTES,\n run & 0xff,\n (run >> 8) & 0xff,\n (run >> 16) & 0xff,\n (run >> 24) & 0xff\n ];\n }\n\n /**\n * Return a port value for the EV3 that is in the format for 'output bit field'\n * as 1/2/4/8, generally needed for motor ports, instead of the typical 0/1/2/3.\n * The documentation in the 'EV3 Firmware Developer Kit' for motor port arguments\n * is sometimes mistaken, but we believe motor ports are mostly addressed this way.\n * @param {number} port - the port number to convert to an 'output bit field'.\n * @return {number} - the converted port number.\n */\n _portMask (port) {\n return Math.pow(2, port);\n }\n}\n\nclass EV3 {\n\n constructor (runtime, extensionId) {\n\n /**\n * The Scratch 3.0 runtime used to trigger the green flag button.\n * @type {Runtime}\n * @private\n */\n this._runtime = runtime;\n this._runtime.on('PROJECT_STOP_ALL', this.stopAll.bind(this));\n\n /**\n * The id of the extension this peripheral belongs to.\n */\n this._extensionId = extensionId;\n\n /**\n * A list of the names of the sensors connected in ports 1,2,3,4.\n * @type {string[]}\n * @private\n */\n this._sensorPorts = [];\n\n /**\n * A list of the names of the motors connected in ports A,B,C,D.\n * @type {string[]}\n * @private\n */\n this._motorPorts = [];\n\n /**\n * The state of all sensor values.\n * @type {string[]}\n * @private\n */\n this._sensors = {\n distance: 0,\n brightness: 0,\n buttons: [0, 0, 0, 0]\n };\n\n /**\n * The motors which this EV3 could possibly have connected.\n * @type {string[]}\n * @private\n */\n this._motors = [null, null, null, null];\n\n /**\n * The polling interval, in milliseconds.\n * @type {number}\n * @private\n */\n this._pollingInterval = 150;\n\n /**\n * The polling interval ID.\n * @type {number}\n * @private\n */\n this._pollingIntervalID = null;\n\n /**\n * The counter keeping track of polling cycles.\n * @type {string[]}\n * @private\n */\n this._pollingCounter = 0;\n\n /**\n * The Bluetooth socket connection for reading/writing peripheral data.\n * @type {BT}\n * @private\n */\n this._bt = null;\n this._runtime.registerPeripheralExtension(extensionId, this);\n\n /**\n * A rate limiter utility, to help limit the rate at which we send BT messages\n * over the socket to Scratch Link to a maximum number of sends per second.\n * @type {RateLimiter}\n * @private\n */\n this._rateLimiter = new RateLimiter(BTSendRateMax);\n\n this.reset = this.reset.bind(this);\n this._onConnect = this._onConnect.bind(this);\n this._onMessage = this._onMessage.bind(this);\n this._pollValues = this._pollValues.bind(this);\n }\n\n get distance () {\n let value = this._sensors.distance > 100 ? 100 : this._sensors.distance;\n value = value < 0 ? 0 : value;\n value = Math.round(100 * value) / 100;\n\n return value;\n }\n\n get brightness () {\n return this._sensors.brightness;\n }\n\n /**\n * Access a particular motor on this peripheral.\n * @param {int} index - the zero-based index of the desired motor.\n * @return {EV3Motor} - the EV3Motor instance, if any, at that index.\n */\n motor (index) {\n return this._motors[index];\n }\n\n isButtonPressed (port) {\n return this._sensors.buttons[port] === 1;\n }\n\n beep (freq, time) {\n const cmd = this.generateCommand(\n Ev3Command.DIRECT_COMMAND_NO_REPLY,\n [\n Ev3Opcode.OPSOUND,\n Ev3Opcode.OPSOUND_CMD_TONE,\n Ev3Encoding.ONE_BYTE,\n 2,\n Ev3Encoding.TWO_BYTES,\n freq,\n freq >> 8,\n Ev3Encoding.TWO_BYTES,\n time,\n time >> 8\n ]\n );\n\n this.send(cmd);\n }\n\n stopAll () {\n this.stopAllMotors();\n this.stopSound();\n }\n\n stopSound () {\n const cmd = this.generateCommand(\n Ev3Command.DIRECT_COMMAND_NO_REPLY,\n [\n Ev3Opcode.OPSOUND,\n Ev3Opcode.OPSOUND_CMD_STOP\n ]\n );\n\n this.send(cmd, false); // don't use rate limiter to ensure sound stops\n }\n\n stopAllMotors () {\n this._motors.forEach(motor => {\n if (motor) {\n motor.coast();\n }\n });\n }\n\n /**\n * Called by the runtime when user wants to scan for an EV3 peripheral.\n */\n scan () {\n if (this._bt) {\n this._bt.disconnect();\n }\n this._bt = new BT(this._runtime, this._extensionId, {\n majorDeviceClass: 8,\n minorDeviceClass: 1\n }, this._onConnect, this.reset, this._onMessage);\n }\n\n /**\n * Called by the runtime when user wants to connect to a certain EV3 peripheral.\n * @param {number} id - the id of the peripheral to connect to.\n */\n connect (id) {\n if (this._bt) {\n this._bt.connectPeripheral(id, Ev3PairingPin);\n }\n }\n\n /**\n * Called by the runtime when user wants to disconnect from the EV3 peripheral.\n */\n disconnect () {\n if (this._bt) {\n this._bt.disconnect();\n }\n\n this.reset();\n }\n\n /**\n * Reset all the state and timeout/interval ids.\n */\n reset () {\n this._sensorPorts = [];\n this._motorPorts = [];\n this._sensors = {\n distance: 0,\n brightness: 0,\n buttons: [0, 0, 0, 0]\n };\n this._motors = [null, null, null, null];\n\n if (this._pollingIntervalID) {\n window.clearInterval(this._pollingIntervalID);\n this._pollingIntervalID = null;\n }\n }\n\n /**\n * Called by the runtime to detect whether the EV3 peripheral is connected.\n * @return {boolean} - the connected state.\n */\n isConnected () {\n let connected = false;\n if (this._bt) {\n connected = this._bt.isConnected();\n }\n return connected;\n }\n\n /**\n * Send a message to the peripheral BT socket.\n * @param {Uint8Array} message - the message to send.\n * @param {boolean} [useLimiter=true] - if true, use the rate limiter\n * @return {Promise} - a promise result of the send operation.\n */\n send (message, useLimiter = true) {\n if (!this.isConnected()) return Promise.resolve();\n\n if (useLimiter) {\n if (!this._rateLimiter.okayToSend()) return Promise.resolve();\n }\n\n return this._bt.sendMessage({\n message: Base64Util.uint8ArrayToBase64(message),\n encoding: 'base64'\n });\n }\n\n /**\n * Genrates direct commands that are sent to the EV3 as a single or compounded byte arrays.\n * See 'EV3 Communication Developer Kit', section 4, page 24 at\n * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits.\n *\n * Direct commands are one of two types:\n * DIRECT_COMMAND_NO_REPLY = a direct command where no reply is expected\n * DIRECT_COMMAND_REPLY = a direct command where a reply is expected, and the\n * number and length of returned values needs to be specified.\n *\n * The direct command byte array sent takes the following format:\n * Byte 0 - 1: Command size, Little Endian. Command size not including these 2 bytes\n * Byte 2 - 3: Message counter, Little Endian. Forth running counter\n * Byte 4: Command type. Either DIRECT_COMMAND_REPLY or DIRECT_COMMAND_NO_REPLY\n * Byte 5 - 6: Reservation (allocation) of global and local variables using a compressed format\n * (globals reserved in byte 5 and the 2 lsb of byte 6, locals reserved in the upper\n * 6 bits of byte 6) – see documentation for more details.\n * Byte 7 - n: Byte codes as a single command or compound commands (I.e. more commands composed\n * as a small program)\n *\n * @param {number} type - the direct command type.\n * @param {string} byteCommands - a compound array of EV3 Opcode + arguments.\n * @param {number} allocation - the allocation of global and local vars needed for replies.\n * @return {array} - generated complete command byte array, with header and compounded commands.\n */\n generateCommand (type, byteCommands, allocation = 0) {\n\n // Header (Bytes 0 - 6)\n let command = [];\n command[2] = 0; // Message counter unused for now\n command[3] = 0; // Message counter unused for now\n command[4] = type;\n command[5] = allocation & 0xFF;\n command[6] = allocation >> 8 && 0xFF;\n\n // Bytecodes (Bytes 7 - n)\n command = command.concat(byteCommands);\n\n // Calculate command length minus first two header bytes\n const len = command.length - 2;\n command[0] = len & 0xFF;\n command[1] = len >> 8 && 0xFF;\n\n return command;\n }\n\n /**\n * When the EV3 peripheral connects, start polling for sensor and motor values.\n * @private\n */\n _onConnect () {\n this._pollingIntervalID = window.setInterval(this._pollValues, this._pollingInterval);\n }\n\n /**\n * Poll the EV3 for sensor and motor input values, based on the list of\n * known connected sensors and motors. This is sent as many compound commands\n * in a direct command, with a reply expected.\n *\n * See 'EV3 Firmware Developer Kit', section 4.8, page 46, at\n * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits\n * for a list of polling/input device commands and their arguments.\n *\n * @private\n */\n _pollValues () {\n if (!this.isConnected()) {\n window.clearInterval(this._pollingIntervalID);\n return;\n }\n\n const cmds = []; // compound command\n let allocation = 0;\n let sensorCount = 0;\n\n // Reset the list of devices every 20 counts\n if (this._pollingCounter % 20 === 0) {\n // GET DEVICE LIST\n cmds[0] = Ev3Opcode.OPINPUT_DEVICE_LIST;\n cmds[1] = Ev3Encoding.ONE_BYTE;\n cmds[2] = Ev3Args.MAX_DEVICES;\n cmds[3] = Ev3Encoding.GLOBAL_VARIABLE_INDEX_0;\n cmds[4] = Ev3Encoding.GLOBAL_VARIABLE_ONE_BYTE;\n cmds[5] = Ev3Encoding.GLOBAL_CONSTANT_INDEX_0;\n\n // Command and payload lengths\n allocation = 33;\n\n this._updateDevices = true;\n } else {\n // GET SENSOR VALUES FOR CONNECTED SENSORS\n let index = 0;\n for (let i = 0; i < 4; i++) {\n if (this._sensorPorts[i] !== 'none') {\n cmds[index + 0] = Ev3Opcode.OPINPUT_READSI;\n cmds[index + 1] = Ev3Args.LAYER;\n cmds[index + 2] = i; // PORT\n cmds[index + 3] = Ev3Args.DO_NOT_CHANGE_TYPE;\n cmds[index + 4] = Ev3Mode[this._sensorPorts[i]];\n cmds[index + 5] = Ev3Encoding.GLOBAL_VARIABLE_ONE_BYTE;\n cmds[index + 6] = sensorCount * 4; // GLOBAL INDEX\n index += 7;\n }\n sensorCount++;\n }\n\n // GET MOTOR POSITION VALUES, EVEN IF NO MOTOR PRESENT\n for (let i = 0; i < 4; i++) {\n cmds[index + 0] = Ev3Opcode.OPOUTPUT_GET_COUNT;\n cmds[index + 1] = Ev3Args.LAYER;\n cmds[index + 2] = i; // PORT (incorrectly specified as 'Output bit field' in LEGO docs)\n cmds[index + 3] = Ev3Encoding.GLOBAL_VARIABLE_ONE_BYTE;\n cmds[index + 4] = sensorCount * 4; // GLOBAL INDEX\n index += 5;\n sensorCount++;\n }\n\n // Command and payload lengths\n allocation = sensorCount * 4;\n }\n\n const cmd = this.generateCommand(\n Ev3Command.DIRECT_COMMAND_REPLY,\n cmds,\n allocation\n );\n\n this.send(cmd);\n\n this._pollingCounter++;\n }\n\n /**\n * Message handler for incoming EV3 reply messages, either a list of connected\n * devices (sensors and motors) or the values of the connected sensors and motors.\n *\n * See 'EV3 Communication Developer Kit', section 4.1, page 24 at\n * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits\n * for more details on direct reply formats.\n *\n * The direct reply byte array sent takes the following format:\n * Byte 0 – 1: Reply size, Little Endian. Reply size not including these 2 bytes\n * Byte 2 – 3: Message counter, Little Endian. Equals the Direct Command\n * Byte 4: Reply type. Either DIRECT_REPLY or DIRECT_REPLY_ERROR\n * Byte 5 - n: Resonse buffer. I.e. the content of the by the Command reserved global variables.\n * I.e. if the command reserved 64 bytes, these bytes will be placed in the reply\n * packet as the bytes 5 to 68.\n *\n * See 'EV3 Firmware Developer Kit', section 4.8, page 56 at\n * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits\n * for direct response buffer formats for various commands.\n *\n * @param {object} params - incoming message parameters\n * @private\n */\n _onMessage (params) {\n const message = params.message;\n const data = Base64Util.base64ToUint8Array(message);\n\n if (data[4] !== Ev3Command.DIRECT_REPLY) {\n return;\n }\n\n if (this._updateDevices) {\n\n // PARSE DEVICE LIST\n for (let i = 0; i < 4; i++) {\n const deviceType = Ev3Device[data[i + 5]];\n // if returned device type is null, use 'none'\n this._sensorPorts[i] = deviceType ? deviceType : 'none';\n }\n for (let i = 0; i < 4; i++) {\n const deviceType = Ev3Device[data[i + 21]];\n // if returned device type is null, use 'none'\n this._motorPorts[i] = deviceType ? deviceType : 'none';\n }\n for (let m = 0; m < 4; m++) {\n const type = this._motorPorts[m];\n if (type !== 'none' && !this._motors[m]) {\n // add new motor if don't already have one\n this._motors[m] = new EV3Motor(this, m, type);\n }\n if (type === 'none' && this._motors[m]) {\n // clear old motor\n this._motors[m] = null;\n }\n }\n this._updateDevices = false;\n\n // eslint-disable-next-line no-undefined\n } else if (!this._sensorPorts.includes(undefined) && !this._motorPorts.includes(undefined)) {\n\n // PARSE SENSOR VALUES\n let offset = 5; // start reading sensor values at byte 5\n for (let i = 0; i < 4; i++) {\n // array 2 float\n const buffer = new Uint8Array([\n data[offset],\n data[offset + 1],\n data[offset + 2],\n data[offset + 3]\n ]).buffer;\n const view = new DataView(buffer);\n const value = view.getFloat32(0, true);\n\n if (Ev3Label[this._sensorPorts[i]] === 'button') {\n // Read a button value per port\n this._sensors.buttons[i] = value ? value : 0;\n } else if (Ev3Label[this._sensorPorts[i]]) { // if valid\n // Read brightness / distance values and set to 0 if null\n this._sensors[Ev3Label[this._sensorPorts[i]]] = value ? value : 0;\n }\n offset += 4;\n }\n\n // PARSE MOTOR POSITION VALUES, EVEN IF NO MOTOR PRESENT\n for (let i = 0; i < 4; i++) {\n const positionArray = [\n data[offset],\n data[offset + 1],\n data[offset + 2],\n data[offset + 3]\n ];\n if (this._motors[i]) {\n this._motors[i].position = positionArray;\n }\n offset += 4;\n }\n\n }\n }\n}\n\n/**\n * Enum for motor port names.\n * Note: if changed, will break compatibility with previously saved projects.\n * @readonly\n * @enum {string}\n */\nconst Ev3MotorMenu = ['A', 'B', 'C', 'D'];\n\n/**\n * Enum for sensor port names.\n * Note: if changed, will break compatibility with previously saved projects.\n * @readonly\n * @enum {string}\n */\nconst Ev3SensorMenu = ['1', '2', '3', '4'];\n\nclass Scratch3Ev3Blocks {\n\n /**\n * The ID of the extension.\n * @return {string} the id\n */\n static get EXTENSION_ID () {\n return 'ev3';\n }\n\n /**\n * Creates a new instance of the EV3 extension.\n * @param {object} runtime VM runtime\n * @constructor\n */\n constructor (runtime) {\n /**\n * The Scratch 3.0 runtime.\n * @type {Runtime}\n */\n this.runtime = runtime;\n\n // Create a new EV3 peripheral instance\n this._peripheral = new EV3(this.runtime, Scratch3Ev3Blocks.EXTENSION_ID);\n\n this._playNoteForPicker = this._playNoteForPicker.bind(this);\n this.runtime.on('PLAY_NOTE', this._playNoteForPicker);\n }\n\n /**\n * Define the EV3 extension.\n * @return {object} Extension description.\n */\n getInfo () {\n return {\n id: Scratch3Ev3Blocks.EXTENSION_ID,\n name: 'LEGO EV3',\n blockIconURI: blockIconURI,\n showStatusButton: true,\n blocks: [\n {\n opcode: 'motorTurnClockwise',\n text: formatMessage({\n id: 'ev3.motorTurnClockwise',\n default: 'motor [PORT] turn this way for [TIME] seconds',\n description: 'turn a motor clockwise for some time'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n PORT: {\n type: ArgumentType.STRING,\n menu: 'motorPorts',\n defaultValue: 0\n },\n TIME: {\n type: ArgumentType.NUMBER,\n defaultValue: 1\n }\n }\n },\n {\n opcode: 'motorTurnCounterClockwise',\n text: formatMessage({\n id: 'ev3.motorTurnCounterClockwise',\n default: 'motor [PORT] turn that way for [TIME] seconds',\n description: 'turn a motor counter-clockwise for some time'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n PORT: {\n type: ArgumentType.STRING,\n menu: 'motorPorts',\n defaultValue: 0\n },\n TIME: {\n type: ArgumentType.NUMBER,\n defaultValue: 1\n }\n }\n },\n {\n opcode: 'motorSetPower',\n text: formatMessage({\n id: 'ev3.motorSetPower',\n default: 'motor [PORT] set power [POWER] %',\n description: 'set a motor\\'s power to some value'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n PORT: {\n type: ArgumentType.STRING,\n menu: 'motorPorts',\n defaultValue: 0\n },\n POWER: {\n type: ArgumentType.NUMBER,\n defaultValue: 100\n }\n }\n },\n {\n opcode: 'getMotorPosition',\n text: formatMessage({\n id: 'ev3.getMotorPosition',\n default: 'motor [PORT] position',\n description: 'get the measured degrees a motor has turned'\n }),\n blockType: BlockType.REPORTER,\n arguments: {\n PORT: {\n type: ArgumentType.STRING,\n menu: 'motorPorts',\n defaultValue: 0\n }\n }\n },\n {\n opcode: 'whenButtonPressed',\n text: formatMessage({\n id: 'ev3.whenButtonPressed',\n default: 'when button [PORT] pressed',\n description: 'when a button connected to a port is pressed'\n }),\n blockType: BlockType.HAT,\n arguments: {\n PORT: {\n type: ArgumentType.STRING,\n menu: 'sensorPorts',\n defaultValue: 0\n }\n }\n },\n {\n opcode: 'whenDistanceLessThan',\n text: formatMessage({\n id: 'ev3.whenDistanceLessThan',\n default: 'when distance < [DISTANCE]',\n description: 'when the value measured by the distance sensor is less than some value'\n }),\n blockType: BlockType.HAT,\n arguments: {\n DISTANCE: {\n type: ArgumentType.NUMBER,\n defaultValue: 5\n }\n }\n },\n {\n opcode: 'whenBrightnessLessThan',\n text: formatMessage({\n id: 'ev3.whenBrightnessLessThan',\n default: 'when brightness < [DISTANCE]',\n description: 'when value measured by brightness sensor is less than some value'\n }),\n blockType: BlockType.HAT,\n arguments: {\n DISTANCE: {\n type: ArgumentType.NUMBER,\n defaultValue: 50\n }\n }\n },\n {\n opcode: 'buttonPressed',\n text: formatMessage({\n id: 'ev3.buttonPressed',\n default: 'button [PORT] pressed?',\n description: 'is a button on some port pressed?'\n }),\n blockType: BlockType.BOOLEAN,\n arguments: {\n PORT: {\n type: ArgumentType.STRING,\n menu: 'sensorPorts',\n defaultValue: 0\n }\n }\n },\n {\n opcode: 'getDistance',\n text: formatMessage({\n id: 'ev3.getDistance',\n default: 'distance',\n description: 'gets measured distance'\n }),\n blockType: BlockType.REPORTER\n },\n {\n opcode: 'getBrightness',\n text: formatMessage({\n id: 'ev3.getBrightness',\n default: 'brightness',\n description: 'gets measured brightness'\n }),\n blockType: BlockType.REPORTER\n },\n {\n opcode: 'beep',\n text: formatMessage({\n id: 'ev3.beepNote',\n default: 'beep note [NOTE] for [TIME] secs',\n description: 'play some note on EV3 for some time'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n NOTE: {\n type: ArgumentType.NOTE,\n defaultValue: 60\n },\n TIME: {\n type: ArgumentType.NUMBER,\n defaultValue: 0.5\n }\n }\n }\n ],\n menus: {\n motorPorts: {\n acceptReporters: true,\n items: this._formatMenu(Ev3MotorMenu)\n },\n sensorPorts: {\n acceptReporters: true,\n items: this._formatMenu(Ev3SensorMenu)\n }\n }\n };\n }\n\n motorTurnClockwise (args) {\n const port = Cast.toNumber(args.PORT);\n let time = Cast.toNumber(args.TIME) * 1000;\n time = MathUtil.clamp(time, 0, 15000);\n\n return new Promise(resolve => {\n this._forEachMotor(port, motorIndex => {\n const motor = this._peripheral.motor(motorIndex);\n if (motor) {\n motor.direction = 1;\n motor.turnOnFor(time);\n }\n });\n\n // Run for some time even when no motor is connected\n setTimeout(resolve, time);\n });\n }\n\n motorTurnCounterClockwise (args) {\n const port = Cast.toNumber(args.PORT);\n let time = Cast.toNumber(args.TIME) * 1000;\n time = MathUtil.clamp(time, 0, 15000);\n\n return new Promise(resolve => {\n this._forEachMotor(port, motorIndex => {\n const motor = this._peripheral.motor(motorIndex);\n if (motor) {\n motor.direction = -1;\n motor.turnOnFor(time);\n }\n });\n\n // Run for some time even when no motor is connected\n setTimeout(resolve, time);\n });\n }\n\n motorSetPower (args) {\n const port = Cast.toNumber(args.PORT);\n const power = MathUtil.clamp(Cast.toNumber(args.POWER), 0, 100);\n\n this._forEachMotor(port, motorIndex => {\n const motor = this._peripheral.motor(motorIndex);\n if (motor) {\n motor.power = power;\n }\n });\n }\n\n getMotorPosition (args) {\n const port = Cast.toNumber(args.PORT);\n\n if (![0, 1, 2, 3].includes(port)) {\n return;\n }\n\n const motor = this._peripheral.motor(port);\n let position = 0;\n if (motor) {\n position = MathUtil.wrapClamp(motor.position, 0, 360);\n }\n\n return position;\n }\n\n whenButtonPressed (args) {\n const port = Cast.toNumber(args.PORT);\n\n if (![0, 1, 2, 3].includes(port)) {\n return;\n }\n\n return this._peripheral.isButtonPressed(port);\n }\n\n whenDistanceLessThan (args) {\n const distance = MathUtil.clamp(Cast.toNumber(args.DISTANCE), 0, 100);\n\n return this._peripheral.distance < distance;\n }\n\n whenBrightnessLessThan (args) {\n const brightness = MathUtil.clamp(Cast.toNumber(args.DISTANCE), 0, 100);\n\n return this._peripheral.brightness < brightness;\n }\n\n buttonPressed (args) {\n const port = Cast.toNumber(args.PORT);\n\n if (![0, 1, 2, 3].includes(port)) {\n return;\n }\n\n return this._peripheral.isButtonPressed(port);\n }\n\n getDistance () {\n return this._peripheral.distance;\n }\n\n getBrightness () {\n return this._peripheral.brightness;\n }\n\n _playNoteForPicker (note, category) {\n if (category !== this.getInfo().name) return;\n this.beep({\n NOTE: note,\n TIME: 0.25\n });\n }\n\n beep (args) {\n const note = MathUtil.clamp(Cast.toNumber(args.NOTE), 47, 99); // valid EV3 sounds\n let time = Cast.toNumber(args.TIME) * 1000;\n time = MathUtil.clamp(time, 0, 3000);\n\n if (time === 0) {\n return; // don't send a beep time of 0\n }\n\n return new Promise(resolve => {\n // https://en.wikipedia.org/wiki/MIDI_tuning_standard#Frequency_values\n const freq = Math.pow(2, ((note - 69 + 12) / 12)) * 440;\n this._peripheral.beep(freq, time);\n\n // Run for some time even when no piezo is connected.\n setTimeout(resolve, time);\n });\n }\n\n /**\n * Call a callback for each motor indexed by the provided motor ID.\n *\n * Note: This way of looping through motors is currently unnecessary, but could be\n * useful if an 'all motors' option is added in the future (see WeDo2 extension).\n *\n * @param {MotorID} motorID - the ID specifier.\n * @param {Function} callback - the function to call with the numeric motor index for each motor.\n * @private\n */\n _forEachMotor (motorID, callback) {\n let motors;\n switch (motorID) {\n case 0:\n motors = [0];\n break;\n case 1:\n motors = [1];\n break;\n case 2:\n motors = [2];\n break;\n case 3:\n motors = [3];\n break;\n default:\n log.warn(`Invalid motor ID: ${motorID}`);\n motors = [];\n break;\n }\n for (const index of motors) {\n callback(index);\n }\n }\n\n /**\n * Formats menus into a format suitable for block menus, and loading previously\n * saved projects:\n * [\n * {\n * text: label,\n * value: index\n * },\n * {\n * text: label,\n * value: index\n * },\n * etc...\n * ]\n *\n * @param {array} menu - a menu to format.\n * @return {object} - a formatted menu as an object.\n * @private\n */\n _formatMenu (menu) {\n const m = [];\n for (let i = 0; i < menu.length; i++) {\n const obj = {};\n obj.text = menu[i];\n obj.value = i.toString();\n m.push(obj);\n }\n return m;\n }\n}\n\nmodule.exports = Scratch3Ev3Blocks;\n","const ArgumentType = require('../../extension-support/argument-type');\nconst BlockType = require('../../extension-support/block-type');\nconst log = require('../../util/log');\nconst formatMessage = require('format-message');\nconst MathUtil = require('../../util/math-util');\nconst BLE = require('../../io/ble');\nconst godirect = require('@vernier/godirect');\nconst ScratchLinkDeviceAdapter = require('./scratch-link-device-adapter');\n\n/**\n * Icon png to be displayed at the left edge of each extension block, encoded as a data URI.\n * @type {string}\n */\n// eslint-disable-next-line max-len\nconst blockIconURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAABGdBTUEAALGPC/xhBQAACCNJREFUeAHtnGtsFFUUgM+dfXbbbbcWaKHSFgrlkWgkJCb6A4kmJfiHIBYBpcFfRg1GEkmEVAvhFYw/TExMxGoICAECiZEIIUQCiiT4gh+KILRQCi2ENIV2t/ue6zl3u2Upu4XuzO4csCe587iPmXO/OWfunTszV4ABWfflQU+0p+9bTcLzEmS5gUPlvagAcVMXcMpnK1u+evW8QLYKaNkWpHKxnt6dQsqFjxo80p10Jt1vx7t30n62Ys+2IJUTUpDlqUNomgYutwsjhZFD5r6slBAOhUHX9YTe6D1GTmrIAhFeBZ2c4JFCpBiggmwlBR7pTGLUewxZYBIUWV7yqgb7g8lotuukt5ihqyELHCSEbusk931ExMxbjSkWSNxEyr3vysxZLFHWnDuT0CtFV6OKmmOBRrV4hMubZoGmMZA6lHTfgsLeHnBEIiCxUY86XRDw+sBfOgZ0m820U5lxIFYAncF+GNvVDo5QaLBu1ClyYTyF4tvd8lZltQgXFA6mW73BxoVt0ShUXG2VCp4QQdDEFqez4Bm7p7gaO0of422r3x4Ji/KrbdIexu4SE2FjgWO6OkCLx6gt6gxOiNV92tiY+ni1Ye1nu7dpQfk35ikru9EBN6unsEDIwgLJPQv8dwCfT3WPt+iFIfAUqM3vL7vpjmuz0KX1gkAfOMN33dxKkjwA9vsTDIS8uubdBZcyAWlqWtohQbRSuru/L1O2vMazAGiLxRKVFqDgDEdAaHCN0kU8Ply2vKWxABhzJZ5ipC6qHlRzfJxVz99S49GdYQEw7PYkuAmokZJ6fumlQUqiNpVSQ56i9JnyHMsCYMRdADGHk0ZyHM1b976XicH0rXtWYR57FPNSGQ7CAiCBCJQ8oXhI0FdmBiPfVnl9ZZmz5DmFDcA+HwIUOEYMcjL2+e57PbBp04HxONI4ifIEKC8TYQMwhs+7IU+hwBFOYQvB5qF8grbwJnRfQXnIhbkIG4AExF+ScE00w0X3AZLwisrDyH1JH1YAA8UlIG029FRZsu6TPfVJiIltWYIjMTLgLUlGs1izeRYmGtS383t9wnu7G2J6fH/Tln2LNUdExGLxvZSOQ1qCS/+P9CFhBZAUuj12PHgCvRJHZ7w4EnhYjya6hXGHQ2Jaxj4ilbVC2AFEUNBVXSdKb3WC29+rmISKiqFn7ARBadyEHUACFHM64VZlDTdWafVh1Yik1ZB5JEsLJGaVtosw37ld4TscWQHX4+oRWO1zWrAEWCR6oMnTCEXijmI1234MVvsPgV+WcmKndGHpwlNtZwbhkZYEkuI4CkuAXfpk0HGAPym0TXEchaUL39Br4JvQeljk+lwxOxBeCRQ3UrFHI+AMBsEV6gcnhlwIS4BU0RORV1V42EqnwnLgSyo3AsM3eA9bPOt8bAEOV6NUWGRZ9FYvHSx6R0pfYgkMmk2DCH1+Z7KwB5gKazjLGgpLgUOAuRZWALnDSncxLAOYCmskbqjhe02h5d6y0sFKF5cXgI8LrLwB9PTeGew6POwNnptlpYOVLi4nFjjuWts957rnBk8tomoZ+bjhPcqOcCcnAG34EaTqOjxmsNKxzQnAkX5wronsOry6zIn66ThljLNcg+W1a2Gi55+MCg6XcKl3NuxrbxouS87TLAcY1V0QV5+8jLyuEekeeSGTS1gOcM/lZpOrlN/DsRzOyi8CY2fLuwUum/wR1BT+ZUzrDKUv9D4LB9rXZEjNTfRjZYFS5r86ebfA3W0bcmMKFh01/5fMoorm6rSjAA2SNc2F8dvmQVWCgdy8fxg8gcEN0pWez80QUyyQFAqn/N9mhmK5PAYN7adecCPnMsUCCZ7U8ari4IGb87wJeKFDA/MlmHXBDVkgTR1CV4/gaThKzBoeKYpuSzqSrqSzEiFuJDayWxqyQJp3RUhYSKfWUSEz5iDIrhrZl8I5b37JvrTBT3wdpd43cOqT/WiJhq6ikQpkW5a8BxuS/X219uXZHoPKmdMUGdEgpWzTll3Kr95Z8VJK7N3NL7b/qHY2rnmdjd6G7oF3q/b/3RoFaPDajwIcBWiQgMHioxZoEKChfqDBc2csnmxtM2ZglMDKArFvduhBbLDv9sOD8oymA0xBCHVtl6+c7ey6Ibdt+3ox7WOoxMCmD4i68PrZkBQaEDUe1tnVqSyyfl79+vr6evz1C2jKogkYWEEc0JnViiZRqKuoqJiZtEJcn0GIsykewzhW2jJVZjzBamxsfK79ase/5MoXL106TnEDwfq36qgIF6HGjKyqFsNkDGMwUNxEDEmIHQTxyNGjH1AchvumBcC4vAuXVpiA+TDYMFDXiiZFoN+SrmMI7tixo/v3337diNtQUzNpPq1RChIra5ccAFKDUEwYLra2fnXu3PmtA0gojqbaVUNl23ft+pPiPW73U7RGYdGH5QCQYCg93C73075S34I5c+ZQa0s/B1Njou51tVVVatJAXcrED3Q4EI5plgsHgAQiSiRCoRD9ECeam9fPo32UJzFQYwJLlix9mdZ9fb1naY2iyiQ2rVtyAEi199Pi5M8/tdB62vRpzceOH3+toaHBh61w2clTp96sqq5ehUnxw0eO7KA8KKpMYtO6JZcOKTUeNRhsp0+ffmtilYI1VLf4+Qvn1784d+5ezEfW144hMR05blglpDgHSbqxt6Wl5Y8ZM6afKq8oL7LZHd54PH7H7w+cOPj9dx8uXbLk+ICynbhm4cJDr7LVMKmhoP5dphaWoFGrHMTAQrgBJCjkFdQHpPntqCUmiWCge14PBsvdFnUYlP8AMAKfKIKmYukAAAAASUVORK5CYII=';\n\n/**\n * Icon png to be displayed in the blocks category menu, encoded as a data URI.\n * @type {string}\n */\n// eslint-disable-next-line max-len\nconst menuIconURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABGdBTUEAALGPC/xhBQAAA9dJREFUWAnNmE2IFEcUgF/9dE/v7LoaM9kkK4JBRA0EFBIPRm85hBAvEXHXwyo5eFE87GFcReMkObgJiQnkkJzEg9n8HIJixKNe1IMKihgiCbviwV11V3d0d3pmuqsqr5ppcEnb3TNVggVFVVe9eu+r97qqq4tASqp8/fsboQgmU0TMugi571K29bPy9ovPU8Sf16HbpQj3EkYFBcJcr5Am2nZfs94AIWVfqMQeHNwhICUBZ4ypUIA/X2sbIm2AW8AJK0lkEP6TJpfqwXgg4QxmF/fB7Gtvxk1G5ZKHU1CqTgPJoSUXYJYeohSUJu+qrqdVUGh2/pVX4VFffx77WaqBZkrkEFj271+qWH0sXcU3FBzyQe/Mg7B//LbKMTRTxNiDbsMHHjTJlyM7HEJIBHXs2KXFj+oTNSdoQOCYLS5jD9IwBMm5H8NplwwPb/QV4yEIcycaAza9IuA76B38fuz1OF5RXUkmHCdu6rg0BpSMgV/sAe7DdzGFrvvdi0D3mSZjQA0wt7REQsY+iWF0XbfFzyal8SLRxuteD+Du4h4Z/flbqaBHibAQtZmQtcZaAZSMwtTylaR/4vaw1ju5YhWG10pwwAqghmp2FeHO2+t11WqyM80W0m7vAOhsM1kD7CGz8L57Jsq6bitZC/GcWgLf1H6KuHT92cTDAFy/BgXMXm0OCpgV50Bo9kK3BqiBboabQMMU/WoL5im4jToeq/AIgXsiRx5KKCjcwPEsiAv/BQMu9EwyDHXd/3kqCOSzDk6t5/YglQKKeJwq+PNRmJI8kwSTaj1HZy5AhSHqnXkIvU9mMUwEw4Q5wTM57LUtkg8QPw/cdcBJ+PhvKJ0Gj80nGq6JXrg6/XFiX97GXIBpyqTieKpKViOl+WEhWXMaUavvvdIZ8Giy5+Lh3bwKm/t+Be3JazMfxc1tldY26rastiHcsQevTG9pw0znovkAcRWHzSDKnZtaOJLSfMFLB5RqtRBS4LbCurqLCy0YPkU3C0IIPEimMqR2ei7ZX2+KQdRi/WahNT/GmfOD4Vyzhx/66pcjp85dUvcmp6J8+txldXh07PPskdkS+V6EbD0vTOKlB0x9B/O6BS8ULly9PgE6x4kDPR/XX5pyYKj8xcCucsUmkNUQE0JvKKm2VioVK5HRE7UKOHbi6B94RzP+93jtpC0vWgXUF0hr3ipuw8uadwd3jXxoA9IK4Pah8t6BneV9GgjD28Svw1mlxFobgFbeFTz13cKbth93fDryp2CEq0a4hTA+aAPQ/ESJFDdvXLzzzrqNjlTqOP6uDeFf0uhvJ0ZP2QD8D6ZzU6u8YIbBAAAAAElFTkSuQmCC';\n\n/**\n * Enum for Vernier godirect protocol.\n * @readonly\n * @enum {string}\n */\nconst BLEUUID = {\n service: 'd91714ef-28b9-4f91-ba16-f0d9a604f112',\n commandChar: 'f4bf14a6-c7d5-4b6d-8aa8-df1a7c83adcb',\n responseChar: 'b41e6675-a329-40e0-aa01-44d2f444babe'\n};\n\n/**\n * A time interval to wait (in milliseconds) before reporting to the BLE socket\n * that data has stopped coming from the peripheral.\n */\nconst BLETimeout = 4500;\n\n/**\n * A string to report to the BLE socket when the GdxFor has stopped receiving data.\n * @type {string}\n */\nconst BLEDataStoppedError = 'Force and Acceleration extension stopped receiving data';\n\n/**\n * Sensor ID numbers for the GDX-FOR.\n */\nconst GDXFOR_SENSOR = {\n FORCE: 1,\n ACCELERATION_X: 2,\n ACCELERATION_Y: 3,\n ACCELERATION_Z: 4,\n SPIN_SPEED_X: 5,\n SPIN_SPEED_Y: 6,\n SPIN_SPEED_Z: 7\n};\n\n/**\n * The update rate, in milliseconds, for sensor data input from the peripheral.\n */\nconst GDXFOR_UPDATE_RATE = 80;\n\n/**\n * Threshold for pushing and pulling force, for the whenForcePushedOrPulled hat block.\n * @type {number}\n */\nconst FORCE_THRESHOLD = 5;\n\n/**\n * Threshold for acceleration magnitude, for the \"shaken\" gesture.\n * @type {number}\n */\nconst SHAKEN_THRESHOLD = 30;\n\n/**\n * Threshold for acceleration magnitude, to check if we are facing up.\n * @type {number}\n */\nconst FACING_THRESHOLD = 9;\n\n/**\n * An offset for the facing threshold, used to check that we are no longer facing up.\n * @type {number}\n */\nconst FACING_THRESHOLD_OFFSET = 5;\n\n/**\n * Threshold for acceleration magnitude, below which we are in freefall.\n * @type {number}\n */\nconst FREEFALL_THRESHOLD = 0.5;\n\n/**\n * Factor used to account for influence of rotation during freefall.\n * @type {number}\n */\nconst FREEFALL_ROTATION_FACTOR = 0.3;\n\n/**\n * Threshold in degrees for reporting that the sensor is tilted.\n * @type {number}\n */\nconst TILT_THRESHOLD = 15;\n\n/**\n * Acceleration due to gravity, in m/s^2.\n * @type {number}\n */\nconst GRAVITY = 9.8;\n\n/**\n * Manage communication with a GDX-FOR peripheral over a Scratch Link client socket.\n */\nclass GdxFor {\n\n /**\n * Construct a GDX-FOR communication object.\n * @param {Runtime} runtime - the Scratch 3.0 runtime\n * @param {string} extensionId - the id of the extension\n */\n constructor (runtime, extensionId) {\n\n /**\n * The Scratch 3.0 runtime used to trigger the green flag button.\n * @type {Runtime}\n * @private\n */\n this._runtime = runtime;\n\n /**\n * The BluetoothLowEnergy connection socket for reading/writing peripheral data.\n * @type {BLE}\n * @private\n */\n this._ble = null;\n\n /**\n * An @vernier/godirect Device\n * @type {Device}\n * @private\n */\n this._device = null;\n\n this._runtime.registerPeripheralExtension(extensionId, this);\n\n /**\n * The id of the extension this peripheral belongs to.\n */\n this._extensionId = extensionId;\n\n /**\n * The most recently received value for each sensor.\n * @type {Object.}\n * @private\n */\n this._sensors = {\n force: 0,\n accelerationX: 0,\n accelerationY: 0,\n accelerationZ: 0,\n spinSpeedX: 0,\n spinSpeedY: 0,\n spinSpeedZ: 0\n };\n\n /**\n * Interval ID for data reading timeout.\n * @type {number}\n * @private\n */\n this._timeoutID = null;\n\n this.reset = this.reset.bind(this);\n this._onConnect = this._onConnect.bind(this);\n }\n\n\n /**\n * Called by the runtime when user wants to scan for a peripheral.\n */\n scan () {\n if (this._ble) {\n this._ble.disconnect();\n }\n\n this._ble = new BLE(this._runtime, this._extensionId, {\n filters: [\n {namePrefix: 'GDX-FOR'}\n ],\n optionalServices: [\n BLEUUID.service\n ]\n }, this._onConnect, this.reset);\n }\n\n /**\n * Called by the runtime when user wants to connect to a certain peripheral.\n * @param {number} id - the id of the peripheral to connect to.\n */\n connect (id) {\n if (this._ble) {\n this._ble.connectPeripheral(id);\n }\n }\n\n /**\n * Called by the runtime when a user exits the connection popup.\n * Disconnect from the GDX FOR.\n */\n disconnect () {\n if (this._ble) {\n this._ble.disconnect();\n }\n\n this.reset();\n }\n\n /**\n * Reset all the state and timeout/interval ids.\n */\n reset () {\n this._sensors = {\n force: 0,\n accelerationX: 0,\n accelerationY: 0,\n accelerationZ: 0,\n spinSpeedX: 0,\n spinSpeedY: 0,\n spinSpeedZ: 0\n };\n\n if (this._timeoutID) {\n window.clearInterval(this._timeoutID);\n this._timeoutID = null;\n }\n }\n\n /**\n * Return true if connected to the goforce device.\n * @return {boolean} - whether the goforce is connected.\n */\n isConnected () {\n let connected = false;\n if (this._ble) {\n connected = this._ble.isConnected();\n }\n return connected;\n }\n\n /**\n * Starts reading data from peripheral after BLE has connected to it.\n * @private\n */\n _onConnect () {\n const adapter = new ScratchLinkDeviceAdapter(this._ble, BLEUUID);\n godirect.createDevice(adapter, {open: true, startMeasurements: false}).then(device => {\n // Setup device\n this._device = device;\n this._device.keepValues = false; // todo: possibly remove after updating Vernier godirect module\n\n // Enable sensors\n this._device.sensors.forEach(sensor => {\n sensor.setEnabled(true);\n });\n\n // Set sensor value-update behavior\n this._device.on('measurements-started', () => {\n const enabledSensors = this._device.sensors.filter(s => s.enabled);\n enabledSensors.forEach(sensor => {\n sensor.on('value-changed', s => {\n this._onSensorValueChanged(s);\n });\n });\n this._timeoutID = window.setInterval(\n () => this._ble.handleDisconnectError(BLEDataStoppedError),\n BLETimeout\n );\n });\n\n // Start device\n this._device.start(GDXFOR_UPDATE_RATE);\n });\n }\n\n /**\n * Handler for sensor value changes from the goforce device.\n * @param {object} sensor - goforce device sensor whose value has changed\n * @private\n */\n _onSensorValueChanged (sensor) {\n switch (sensor.number) {\n case GDXFOR_SENSOR.FORCE:\n // Normalize the force, which can be measured between -50 and 50 N,\n // to be a value between -100 and 100.\n this._sensors.force = MathUtil.clamp(sensor.value * 2, -100, 100);\n break;\n case GDXFOR_SENSOR.ACCELERATION_X:\n this._sensors.accelerationX = sensor.value;\n break;\n case GDXFOR_SENSOR.ACCELERATION_Y:\n this._sensors.accelerationY = sensor.value;\n break;\n case GDXFOR_SENSOR.ACCELERATION_Z:\n this._sensors.accelerationZ = sensor.value;\n break;\n case GDXFOR_SENSOR.SPIN_SPEED_X:\n this._sensors.spinSpeedX = this._spinSpeedFromGyro(sensor.value);\n break;\n case GDXFOR_SENSOR.SPIN_SPEED_Y:\n this._sensors.spinSpeedY = this._spinSpeedFromGyro(sensor.value);\n break;\n case GDXFOR_SENSOR.SPIN_SPEED_Z:\n this._sensors.spinSpeedZ = this._spinSpeedFromGyro(sensor.value);\n break;\n }\n // cancel disconnect timeout and start a new one\n window.clearInterval(this._timeoutID);\n this._timeoutID = window.setInterval(\n () => this._ble.handleDisconnectError(BLEDataStoppedError),\n BLETimeout\n );\n }\n\n _spinSpeedFromGyro (val) {\n const framesPerSec = 1000 / this._runtime.currentStepTime;\n val = MathUtil.radToDeg(val);\n val = val / framesPerSec; // convert to from degrees per sec to degrees per frame\n val = val * -1;\n return val;\n }\n\n getForce () {\n return this._sensors.force;\n }\n\n getTiltFrontBack (back = false) {\n const x = this.getAccelerationX();\n const y = this.getAccelerationY();\n const z = this.getAccelerationZ();\n\n // Compute the yz unit vector\n const y2 = y * y;\n const z2 = z * z;\n let value = y2 + z2;\n value = Math.sqrt(value);\n\n // For sufficiently small zy vector values we are essentially at 90 degrees.\n // The following snaps to 90 and avoids divide-by-zero errors.\n // The snap factor was derived through observation -- just enough to\n // still allow single degree steps up to 90 (..., 87, 88, 89, 90).\n if (value < 0.35) {\n value = (x < 0) ? 90 : -90;\n } else {\n value = x / value;\n value = Math.atan(value);\n value = MathUtil.radToDeg(value) * -1;\n }\n\n // Back is the inverse of front\n if (back) value *= -1;\n\n return value;\n }\n\n getTiltLeftRight (right = false) {\n const x = this.getAccelerationX();\n const y = this.getAccelerationY();\n const z = this.getAccelerationZ();\n\n // Compute the yz unit vector\n const x2 = x * x;\n const z2 = z * z;\n let value = x2 + z2;\n value = Math.sqrt(value);\n\n // For sufficiently small zy vector values we are essentially at 90 degrees.\n // The following snaps to 90 and avoids divide-by-zero errors.\n // The snap factor was derived through observation -- just enough to\n // still allow single degree steps up to 90 (..., 87, 88, 89, 90).\n if (value < 0.35) {\n value = (y < 0) ? 90 : -90;\n } else {\n value = y / value;\n value = Math.atan(value);\n value = MathUtil.radToDeg(value) * -1;\n }\n\n // Right is the inverse of left\n if (right) value *= -1;\n\n return value;\n }\n\n getAccelerationX () {\n return this._sensors.accelerationX;\n }\n\n getAccelerationY () {\n return this._sensors.accelerationY;\n }\n\n getAccelerationZ () {\n return this._sensors.accelerationZ;\n }\n\n getSpinSpeedX () {\n return this._sensors.spinSpeedX;\n }\n\n getSpinSpeedY () {\n return this._sensors.spinSpeedY;\n }\n\n getSpinSpeedZ () {\n return this._sensors.spinSpeedZ;\n }\n}\n\n/**\n * Enum for pushed and pulled menu options.\n * @readonly\n * @enum {string}\n */\nconst PushPullValues = {\n PUSHED: 'pushed',\n PULLED: 'pulled'\n};\n\n/**\n * Enum for motion gesture menu options.\n * @readonly\n * @enum {string}\n */\nconst GestureValues = {\n SHAKEN: 'shaken',\n STARTED_FALLING: 'started falling',\n TURNED_FACE_UP: 'turned face up',\n TURNED_FACE_DOWN: 'turned face down'\n};\n\n/**\n * Enum for tilt axis menu options.\n * @readonly\n * @enum {string}\n */\nconst TiltAxisValues = {\n FRONT: 'front',\n BACK: 'back',\n LEFT: 'left',\n RIGHT: 'right',\n ANY: 'any'\n};\n\n/**\n * Enum for axis menu options.\n * @readonly\n * @enum {string}\n */\nconst AxisValues = {\n X: 'x',\n Y: 'y',\n Z: 'z'\n};\n\n/**\n * Scratch 3.0 blocks to interact with a GDX-FOR peripheral.\n */\nclass Scratch3GdxForBlocks {\n\n /**\n * @return {string} - the name of this extension.\n */\n static get EXTENSION_NAME () {\n return 'Force and Acceleration';\n }\n\n /**\n * @return {string} - the ID of this extension.\n */\n static get EXTENSION_ID () {\n return 'gdxfor';\n }\n\n get AXIS_MENU () {\n return [\n {\n text: 'x',\n value: AxisValues.X\n },\n {\n text: 'y',\n value: AxisValues.Y\n },\n {\n text: 'z',\n value: AxisValues.Z\n }\n ];\n }\n\n get TILT_MENU () {\n return [\n {\n text: formatMessage({\n id: 'gdxfor.tiltDirectionMenu.front',\n default: 'front',\n description: 'label for front element in tilt direction picker for gdxfor extension'\n }),\n value: TiltAxisValues.FRONT\n },\n {\n text: formatMessage({\n id: 'gdxfor.tiltDirectionMenu.back',\n default: 'back',\n description: 'label for back element in tilt direction picker for gdxfor extension'\n }),\n value: TiltAxisValues.BACK\n },\n {\n text: formatMessage({\n id: 'gdxfor.tiltDirectionMenu.left',\n default: 'left',\n description: 'label for left element in tilt direction picker for gdxfor extension'\n }),\n value: TiltAxisValues.LEFT\n },\n {\n text: formatMessage({\n id: 'gdxfor.tiltDirectionMenu.right',\n default: 'right',\n description: 'label for right element in tilt direction picker for gdxfor extension'\n }),\n value: TiltAxisValues.RIGHT\n }\n ];\n }\n\n get TILT_MENU_ANY () {\n return [\n ...this.TILT_MENU,\n {\n text: formatMessage({\n id: 'gdxfor.tiltDirectionMenu.any',\n default: 'any',\n description: 'label for any direction element in tilt direction picker for gdxfor extension'\n }),\n value: TiltAxisValues.ANY\n }\n ];\n }\n\n get PUSH_PULL_MENU () {\n return [\n {\n text: formatMessage({\n id: 'gdxfor.pushed',\n default: 'pushed',\n description: 'the force sensor was pushed inward'\n }),\n value: PushPullValues.PUSHED\n },\n {\n text: formatMessage({\n id: 'gdxfor.pulled',\n default: 'pulled',\n description: 'the force sensor was pulled outward'\n }),\n value: PushPullValues.PULLED\n }\n ];\n }\n\n get GESTURE_MENU () {\n return [\n {\n text: formatMessage({\n id: 'gdxfor.shaken',\n default: 'shaken',\n description: 'the sensor was shaken'\n }),\n value: GestureValues.SHAKEN\n },\n {\n text: formatMessage({\n id: 'gdxfor.startedFalling',\n default: 'started falling',\n description: 'the sensor started free falling'\n }),\n value: GestureValues.STARTED_FALLING\n },\n {\n text: formatMessage({\n id: 'gdxfor.turnedFaceUp',\n default: 'turned face up',\n description: 'the sensor was turned to face up'\n }),\n value: GestureValues.TURNED_FACE_UP\n },\n {\n text: formatMessage({\n id: 'gdxfor.turnedFaceDown',\n default: 'turned face down',\n description: 'the sensor was turned to face down'\n }),\n value: GestureValues.TURNED_FACE_DOWN\n }\n ];\n }\n\n /**\n * Construct a set of GDX-FOR blocks.\n * @param {Runtime} runtime - the Scratch 3.0 runtime.\n */\n constructor (runtime) {\n /**\n * The Scratch 3.0 runtime.\n * @type {Runtime}\n */\n this.runtime = runtime;\n\n // Create a new GdxFor peripheral instance\n this._peripheral = new GdxFor(this.runtime, Scratch3GdxForBlocks.EXTENSION_ID);\n }\n\n /**\n * @returns {object} metadata for this extension and its blocks.\n */\n getInfo () {\n return {\n id: Scratch3GdxForBlocks.EXTENSION_ID,\n name: Scratch3GdxForBlocks.EXTENSION_NAME,\n blockIconURI: blockIconURI,\n menuIconURI: menuIconURI,\n showStatusButton: true,\n blocks: [\n {\n opcode: 'whenGesture',\n text: formatMessage({\n id: 'gdxfor.whenGesture',\n default: 'when [GESTURE]',\n description: 'when the sensor detects a gesture'\n }),\n blockType: BlockType.HAT,\n arguments: {\n GESTURE: {\n type: ArgumentType.STRING,\n menu: 'gestureOptions',\n defaultValue: GestureValues.SHAKEN\n }\n }\n },\n {\n opcode: 'whenForcePushedOrPulled',\n text: formatMessage({\n id: 'gdxfor.whenForcePushedOrPulled',\n default: 'when force sensor [PUSH_PULL]',\n description: 'when the force sensor is pushed or pulled'\n }),\n blockType: BlockType.HAT,\n arguments: {\n PUSH_PULL: {\n type: ArgumentType.STRING,\n menu: 'pushPullOptions',\n defaultValue: PushPullValues.PUSHED\n }\n }\n },\n {\n opcode: 'getForce',\n text: formatMessage({\n id: 'gdxfor.getForce',\n default: 'force',\n description: 'gets force'\n }),\n blockType: BlockType.REPORTER\n },\n '---',\n {\n opcode: 'whenTilted',\n text: formatMessage({\n id: 'gdxfor.whenTilted',\n default: 'when tilted [TILT]',\n description: 'when the sensor detects tilt'\n }),\n blockType: BlockType.HAT,\n arguments: {\n TILT: {\n type: ArgumentType.STRING,\n menu: 'tiltAnyOptions',\n defaultValue: TiltAxisValues.ANY\n }\n }\n },\n {\n opcode: 'isTilted',\n text: formatMessage({\n id: 'gdxfor.isTilted',\n default: 'tilted [TILT]?',\n description: 'is the device tilted?'\n }),\n blockType: BlockType.BOOLEAN,\n arguments: {\n TILT: {\n type: ArgumentType.STRING,\n menu: 'tiltAnyOptions',\n defaultValue: TiltAxisValues.ANY\n }\n }\n },\n {\n opcode: 'getTilt',\n text: formatMessage({\n id: 'gdxfor.getTilt',\n default: 'tilt angle [TILT]',\n description: 'gets tilt'\n }),\n blockType: BlockType.REPORTER,\n arguments: {\n TILT: {\n type: ArgumentType.STRING,\n menu: 'tiltOptions',\n defaultValue: TiltAxisValues.FRONT\n }\n }\n },\n '---',\n {\n opcode: 'isFreeFalling',\n text: formatMessage({\n id: 'gdxfor.isFreeFalling',\n default: 'falling?',\n description: 'is the device in free fall?'\n }),\n blockType: BlockType.BOOLEAN\n },\n {\n opcode: 'getSpinSpeed',\n text: formatMessage({\n id: 'gdxfor.getSpin',\n default: 'spin speed [DIRECTION]',\n description: 'gets spin speed'\n }),\n blockType: BlockType.REPORTER,\n arguments: {\n DIRECTION: {\n type: ArgumentType.STRING,\n menu: 'axisOptions',\n defaultValue: AxisValues.Z\n }\n }\n },\n {\n opcode: 'getAcceleration',\n text: formatMessage({\n id: 'gdxfor.getAcceleration',\n default: 'acceleration [DIRECTION]',\n description: 'gets acceleration'\n }),\n blockType: BlockType.REPORTER,\n arguments: {\n DIRECTION: {\n type: ArgumentType.STRING,\n menu: 'axisOptions',\n defaultValue: AxisValues.X\n }\n }\n }\n ],\n menus: {\n pushPullOptions: {\n acceptReporters: true,\n items: this.PUSH_PULL_MENU\n },\n gestureOptions: {\n acceptReporters: true,\n items: this.GESTURE_MENU\n },\n axisOptions: {\n acceptReporters: true,\n items: this.AXIS_MENU\n },\n tiltOptions: {\n acceptReporters: true,\n items: this.TILT_MENU\n },\n tiltAnyOptions: {\n acceptReporters: true,\n items: this.TILT_MENU_ANY\n }\n }\n };\n }\n\n whenForcePushedOrPulled (args) {\n switch (args.PUSH_PULL) {\n case PushPullValues.PUSHED:\n return this._peripheral.getForce() < FORCE_THRESHOLD * -1;\n case PushPullValues.PULLED:\n return this._peripheral.getForce() > FORCE_THRESHOLD;\n default:\n log.warn(`unknown push/pull value in whenForcePushedOrPulled: ${args.PUSH_PULL}`);\n return false;\n }\n }\n\n getForce () {\n return Math.round(this._peripheral.getForce());\n }\n\n whenGesture (args) {\n switch (args.GESTURE) {\n case GestureValues.SHAKEN:\n return this.gestureMagnitude() > SHAKEN_THRESHOLD;\n case GestureValues.STARTED_FALLING:\n return this.isFreeFalling();\n case GestureValues.TURNED_FACE_UP:\n return this._isFacing(GestureValues.TURNED_FACE_UP);\n case GestureValues.TURNED_FACE_DOWN:\n return this._isFacing(GestureValues.TURNED_FACE_DOWN);\n default:\n log.warn(`unknown gesture value in whenGesture: ${args.GESTURE}`);\n return false;\n }\n }\n\n _isFacing (direction) {\n if (typeof this._facingUp === 'undefined') {\n this._facingUp = false;\n }\n if (typeof this._facingDown === 'undefined') {\n this._facingDown = false;\n }\n\n // If the sensor is already facing up or down, reduce the threshold.\n // This prevents small fluctations in acceleration while it is being\n // turned from causing the hat block to trigger multiple times.\n let threshold = FACING_THRESHOLD;\n if (this._facingUp || this._facingDown) {\n threshold -= FACING_THRESHOLD_OFFSET;\n }\n\n this._facingUp = this._peripheral.getAccelerationZ() > threshold;\n this._facingDown = this._peripheral.getAccelerationZ() < threshold * -1;\n\n switch (direction) {\n case GestureValues.TURNED_FACE_UP:\n return this._facingUp;\n case GestureValues.TURNED_FACE_DOWN:\n return this._facingDown;\n default:\n return false;\n }\n }\n\n whenTilted (args) {\n return this._isTilted(args.TILT);\n }\n\n isTilted (args) {\n return this._isTilted(args.TILT);\n }\n\n getTilt (args) {\n return this._getTiltAngle(args.TILT);\n }\n\n _isTilted (direction) {\n switch (direction) {\n case TiltAxisValues.ANY:\n return this._getTiltAngle(TiltAxisValues.FRONT) > TILT_THRESHOLD ||\n this._getTiltAngle(TiltAxisValues.BACK) > TILT_THRESHOLD ||\n this._getTiltAngle(TiltAxisValues.LEFT) > TILT_THRESHOLD ||\n this._getTiltAngle(TiltAxisValues.RIGHT) > TILT_THRESHOLD;\n default:\n return this._getTiltAngle(direction) > TILT_THRESHOLD;\n }\n }\n\n _getTiltAngle (direction) {\n // Tilt values are calculated using acceleration due to gravity,\n // so we need to return 0 when the peripheral is not connected.\n if (!this._peripheral.isConnected()) {\n return 0;\n }\n\n switch (direction) {\n case TiltAxisValues.FRONT:\n return Math.round(this._peripheral.getTiltFrontBack(true));\n case TiltAxisValues.BACK:\n return Math.round(this._peripheral.getTiltFrontBack(false));\n case TiltAxisValues.LEFT:\n return Math.round(this._peripheral.getTiltLeftRight(true));\n case TiltAxisValues.RIGHT:\n return Math.round(this._peripheral.getTiltLeftRight(false));\n default:\n log.warn(`Unknown direction in getTilt: ${direction}`);\n }\n }\n\n getSpinSpeed (args) {\n switch (args.DIRECTION) {\n case AxisValues.X:\n return Math.round(this._peripheral.getSpinSpeedX());\n case AxisValues.Y:\n return Math.round(this._peripheral.getSpinSpeedY());\n case AxisValues.Z:\n return Math.round(this._peripheral.getSpinSpeedZ());\n default:\n log.warn(`Unknown direction in getSpinSpeed: ${args.DIRECTION}`);\n }\n }\n\n getAcceleration (args) {\n switch (args.DIRECTION) {\n case AxisValues.X:\n return Math.round(this._peripheral.getAccelerationX());\n case AxisValues.Y:\n return Math.round(this._peripheral.getAccelerationY());\n case AxisValues.Z:\n return Math.round(this._peripheral.getAccelerationZ());\n default:\n log.warn(`Unknown direction in getAcceleration: ${args.DIRECTION}`);\n }\n }\n\n /**\n * @param {number} x - x axis vector\n * @param {number} y - y axis vector\n * @param {number} z - z axis vector\n * @return {number} - the magnitude of a three dimension vector.\n */\n magnitude (x, y, z) {\n return Math.sqrt((x * x) + (y * y) + (z * z));\n }\n\n accelMagnitude () {\n return this.magnitude(\n this._peripheral.getAccelerationX(),\n this._peripheral.getAccelerationY(),\n this._peripheral.getAccelerationZ()\n );\n }\n\n gestureMagnitude () {\n return this.accelMagnitude() - GRAVITY;\n }\n\n spinMagnitude () {\n return this.magnitude(\n this._peripheral.getSpinSpeedX(),\n this._peripheral.getSpinSpeedY(),\n this._peripheral.getSpinSpeedZ()\n );\n }\n\n isFreeFalling () {\n // When the peripheral is not connected, the acceleration magnitude\n // is 0 instead of ~9.8, which ends up calculating as a positive\n // free fall; so we need to return 'false' here to prevent returning 'true'.\n if (!this._peripheral.isConnected()) {\n return false;\n }\n\n const accelMag = this.accelMagnitude();\n const spinMag = this.spinMagnitude();\n\n // We want to account for rotation during freefall,\n // so we tack on a an estimated \"rotational effect\"\n // The FREEFALL_ROTATION_FACTOR const is used to both scale the\n // gyro measurements and convert them to radians/second.\n // So, we compare our accel magnitude against:\n // FREEFALL_THRESHOLD + (some_scaled_magnitude_of_rotation).\n const ffThresh = FREEFALL_THRESHOLD + (FREEFALL_ROTATION_FACTOR * spinMag);\n\n return accelMag < ffThresh;\n }\n}\n\nmodule.exports = Scratch3GdxForBlocks;\n","const Base64Util = require('../../util/base64-util');\n\n/**\n * Adapter class\n */\nclass ScratchLinkDeviceAdapter {\n constructor (socket, {service, commandChar, responseChar}) {\n this.socket = socket;\n\n this._service = service;\n this._commandChar = commandChar;\n this._responseChar = responseChar;\n this._onResponse = this._onResponse.bind(this);\n this._deviceOnResponse = null;\n }\n\n get godirectAdapter () {\n return true;\n }\n\n writeCommand (commandBuffer) {\n const data = Base64Util.uint8ArrayToBase64(commandBuffer);\n\n return this.socket\n .write(this._service, this._commandChar, data, 'base64');\n }\n\n setup ({onResponse}) {\n this._deviceOnResponse = onResponse;\n return this.socket\n .startNotifications(this._service, this._responseChar, this._onResponse);\n\n // TODO:\n // How do we find out from scratch link if communication closes?\n }\n\n _onResponse (base64) {\n const array = Base64Util.base64ToUint8Array(base64);\n const response = new DataView(array.buffer);\n return this._deviceOnResponse(response);\n }\n}\n\nmodule.exports = ScratchLinkDeviceAdapter;\n","const formatMessage = require('format-message');\nconst ArgumentType = require('../../extension-support/argument-type');\nconst BlockType = require('../../extension-support/block-type');\nconst Cast = require('../../util/cast');\n\n/**\n * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.\n * @type {string}\n */\n// eslint-disable-next-line max-len\nconst blockIconURI = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHN0eWxlPi5zdDJ7ZmlsbDpyZWR9LnN0M3tmaWxsOiNlMGUwZTB9LnN0NHtmaWxsOm5vbmU7c3Ryb2tlOiM2NjY7c3Ryb2tlLXdpZHRoOi41O3N0cm9rZS1taXRlcmxpbWl0OjEwfTwvc3R5bGU+PHBhdGggZD0iTTM1IDI4SDVhMSAxIDAgMCAxLTEtMVYxMmMwLS42LjQtMSAxLTFoMzBjLjUgMCAxIC40IDEgMXYxNWMwIC41LS41IDEtMSAxeiIgZmlsbD0iI2ZmZiIgaWQ9IkxheWVyXzYiLz48ZyBpZD0iTGF5ZXJfNCI+PHBhdGggY2xhc3M9InN0MiIgZD0iTTQgMjVoMzJ2Mi43SDR6TTEzIDI0aC0yLjJhMSAxIDAgMCAxLTEtMXYtOS43YzAtLjYuNC0xIDEtMUgxM2MuNiAwIDEgLjQgMSAxVjIzYzAgLjYtLjUgMS0xIDF6Ii8+PHBhdGggY2xhc3M9InN0MiIgZD0iTTYuMSAxOS4zdi0yLjJjMC0uNS40LTEgMS0xaDkuN2MuNSAwIDEgLjUgMSAxdjIuMmMwIC41LS41IDEtMSAxSDcuMWExIDEgMCAwIDEtMS0xeiIvPjxjaXJjbGUgY2xhc3M9InN0MiIgY3g9IjIyLjgiIGN5PSIxOC4yIiByPSIzLjQiLz48Y2lyY2xlIGNsYXNzPSJzdDIiIGN4PSIzMC42IiBjeT0iMTguMiIgcj0iMy40Ii8+PHBhdGggY2xhc3M9InN0MiIgZD0iTTQuMiAyN2gzMS45di43SDQuMnoiLz48L2c+PGcgaWQ9IkxheWVyXzUiPjxjaXJjbGUgY2xhc3M9InN0MyIgY3g9IjIyLjgiIGN5PSIxOC4yIiByPSIyLjMiLz48Y2lyY2xlIGNsYXNzPSJzdDMiIGN4PSIzMC42IiBjeT0iMTguMiIgcj0iMi4zIi8+PHBhdGggY2xhc3M9InN0MyIgZD0iTTEyLjUgMjIuOWgtMS4yYy0uMyAwLS41LS4yLS41LS41VjE0YzAtLjMuMi0uNS41LS41aDEuMmMuMyAwIC41LjIuNS41djguNGMwIC4zLS4yLjUtLjUuNXoiLz48cGF0aCBjbGFzcz0ic3QzIiBkPSJNNy4yIDE4Ljd2LTEuMmMwLS4zLjItLjUuNS0uNWg4LjRjLjMgMCAuNS4yLjUuNXYxLjJjMCAuMy0uMi41LS41LjVINy43Yy0uMyAwLS41LS4yLS41LS41ek00IDI2aDMydjJINHoiLz48L2c+PGcgaWQ9IkxheWVyXzMiPjxwYXRoIGNsYXNzPSJzdDQiIGQ9Ik0zNS4yIDI3LjlINC44YTEgMSAwIDAgMS0xLTFWMTIuMWMwLS42LjUtMSAxLTFoMzAuNWMuNSAwIDEgLjQgMSAxVjI3YTEgMSAwIDAgMS0xLjEuOXoiLz48cGF0aCBjbGFzcz0ic3Q0IiBkPSJNMzUuMiAyNy45SDQuOGExIDEgMCAwIDEtMS0xVjEyLjFjMC0uNi41LTEgMS0xaDMwLjVjLjUgMCAxIC40IDEgMVYyN2ExIDEgMCAwIDEtMS4xLjl6Ii8+PC9nPjwvc3ZnPg==';\n\n/**\n * Length of the buffer to store key presses for the \"when keys pressed in order\" hat\n * @type {number}\n */\nconst KEY_BUFFER_LENGTH = 100;\n\n/**\n * Timeout in milliseconds to reset the completed flag for a sequence.\n * @type {number}\n */\nconst SEQUENCE_HAT_TIMEOUT = 100;\n\n/**\n * An id for the space key on a keyboard.\n */\nconst KEY_ID_SPACE = 'SPACE';\n\n/**\n * An id for the left arrow key on a keyboard.\n */\nconst KEY_ID_LEFT = 'LEFT';\n\n/**\n * An id for the right arrow key on a keyboard.\n */\nconst KEY_ID_RIGHT = 'RIGHT';\n\n/**\n * An id for the up arrow key on a keyboard.\n */\nconst KEY_ID_UP = 'UP';\n\n/**\n * An id for the down arrow key on a keyboard.\n */\nconst KEY_ID_DOWN = 'DOWN';\n\n/**\n * Names used by keyboard io for keys used in scratch.\n * @enum {string}\n */\nconst SCRATCH_KEY_NAME = {\n [KEY_ID_SPACE]: 'space',\n [KEY_ID_LEFT]: 'left arrow',\n [KEY_ID_UP]: 'up arrow',\n [KEY_ID_RIGHT]: 'right arrow',\n [KEY_ID_DOWN]: 'down arrow'\n};\n\n/**\n * Class for the makey makey blocks in Scratch 3.0\n * @constructor\n */\nclass Scratch3MakeyMakeyBlocks {\n constructor (runtime) {\n /**\n * The runtime instantiating this block package.\n * @type {Runtime}\n */\n this.runtime = runtime;\n\n /**\n * A toggle that alternates true and false each frame, so that an\n * edge-triggered hat can trigger on every other frame.\n * @type {boolean}\n */\n this.frameToggle = false;\n\n // Set an interval that toggles the frameToggle every frame.\n setInterval(() => {\n this.frameToggle = !this.frameToggle;\n }, this.runtime.currentStepTime);\n\n this.keyPressed = this.keyPressed.bind(this);\n this.runtime.on('KEY_PRESSED', this.keyPressed);\n\n this._clearkeyPressBuffer = this._clearkeyPressBuffer.bind(this);\n this.runtime.on('PROJECT_STOP_ALL', this._clearkeyPressBuffer);\n\n /*\n * An object containing a set of sequence objects.\n * These are the key sequences currently being detected by the \"when\n * keys pressed in order\" hat block. Each sequence is keyed by its\n * string representation (the sequence's value in the menu, which is a\n * string of KEY_IDs separated by spaces). Each sequence object\n * has an array property (an array of KEY_IDs) and a boolean\n * completed property that is true when the sequence has just been\n * pressed.\n * @type {object}\n */\n this.sequences = {};\n\n /*\n * An array of the key codes of recently pressed keys.\n * @type {array}\n */\n this.keyPressBuffer = [];\n }\n\n /*\n * Localized short-form names of the space bar and arrow keys, for use in the\n * displayed menu items of the \"when keys pressed in order\" block.\n * @type {object}\n */\n get KEY_TEXT_SHORT () {\n return {\n [KEY_ID_SPACE]: formatMessage({\n id: 'makeymakey.spaceKey',\n default: 'space',\n description: 'The space key on a computer keyboard.'\n }),\n [KEY_ID_LEFT]: formatMessage({\n id: 'makeymakey.leftArrowShort',\n default: 'left',\n description: 'Short name for the left arrow key on a computer keyboard.'\n }),\n [KEY_ID_UP]: formatMessage({\n id: 'makeymakey.upArrowShort',\n default: 'up',\n description: 'Short name for the up arrow key on a computer keyboard.'\n }),\n [KEY_ID_RIGHT]: formatMessage({\n id: 'makeymakey.rightArrowShort',\n default: 'right',\n description: 'Short name for the right arrow key on a computer keyboard.'\n }),\n [KEY_ID_DOWN]: formatMessage({\n id: 'makeymakey.downArrowShort',\n default: 'down',\n description: 'Short name for the down arrow key on a computer keyboard.'\n })\n };\n }\n\n /*\n * An array of strings of KEY_IDs representing the default set of\n * key sequences for use by the \"when keys pressed in order\" block.\n * @type {array}\n */\n get DEFAULT_SEQUENCES () {\n return [\n `${KEY_ID_LEFT} ${KEY_ID_UP} ${KEY_ID_RIGHT}`,\n `${KEY_ID_RIGHT} ${KEY_ID_UP} ${KEY_ID_LEFT}`,\n `${KEY_ID_LEFT} ${KEY_ID_RIGHT}`,\n `${KEY_ID_RIGHT} ${KEY_ID_LEFT}`,\n `${KEY_ID_UP} ${KEY_ID_DOWN}`,\n `${KEY_ID_DOWN} ${KEY_ID_UP}`,\n `${KEY_ID_UP} ${KEY_ID_RIGHT} ${KEY_ID_DOWN} ${KEY_ID_LEFT}`,\n `${KEY_ID_UP} ${KEY_ID_LEFT} ${KEY_ID_DOWN} ${KEY_ID_RIGHT}`,\n `${KEY_ID_UP} ${KEY_ID_UP} ${KEY_ID_DOWN} ${KEY_ID_DOWN} ` +\n `${KEY_ID_LEFT} ${KEY_ID_RIGHT} ${KEY_ID_LEFT} ${KEY_ID_RIGHT}`\n ];\n }\n\n /**\n * @returns {object} metadata for this extension and its blocks.\n */\n getInfo () {\n return {\n id: 'makeymakey',\n name: 'Makey Makey',\n blockIconURI: blockIconURI,\n blocks: [\n {\n opcode: 'whenMakeyKeyPressed',\n text: formatMessage({\n id: 'makeymakey.whenKeyPressed',\n default: 'when [KEY] key pressed',\n description: 'when a keyboard key is pressed'\n }),\n blockType: BlockType.HAT,\n arguments: {\n KEY: {\n type: ArgumentType.STRING,\n menu: 'KEY',\n defaultValue: KEY_ID_SPACE\n }\n }\n },\n {\n opcode: 'whenCodePressed',\n text: formatMessage({\n id: 'makeymakey.whenKeysPressedInOrder',\n default: 'when [SEQUENCE] pressed in order',\n description: 'when a sequence of keyboard keys is pressed in a specific order'\n }),\n blockType: BlockType.HAT,\n arguments: {\n SEQUENCE: {\n type: ArgumentType.STRING,\n menu: 'SEQUENCE',\n defaultValue: this.DEFAULT_SEQUENCES[0]\n }\n }\n }\n ],\n menus: {\n KEY: {\n acceptReporters: true,\n items: [\n {\n text: formatMessage({\n id: 'makeymakey.spaceKey',\n default: 'space',\n description: 'The space key on a computer keyboard.'\n }),\n value: KEY_ID_SPACE\n },\n {\n text: formatMessage({\n id: 'makeymakey.upArrow',\n default: 'up arrow',\n description: 'The up arrow key on a computer keyboard.'\n }),\n value: KEY_ID_UP\n },\n {\n text: formatMessage({\n id: 'makeymakey.downArrow',\n default: 'down arrow',\n description: 'The down arrow key on a computer keyboard.'\n }),\n value: KEY_ID_DOWN\n },\n {\n text: formatMessage({\n id: 'makeymakey.rightArrow',\n default: 'right arrow',\n description: 'The right arrow key on a computer keyboard.'\n }),\n value: KEY_ID_RIGHT\n },\n {\n text: formatMessage({\n id: 'makeymakey.leftArrow',\n default: 'left arrow',\n description: 'The left arrow key on a computer keyboard.'\n }),\n value: KEY_ID_LEFT\n },\n {text: 'w', value: 'w'},\n {text: 'a', value: 'a'},\n {text: 's', value: 's'},\n {text: 'd', value: 'd'},\n {text: 'f', value: 'f'},\n {text: 'g', value: 'g'}\n ]\n },\n SEQUENCE: {\n acceptReporters: true,\n items: this.buildSequenceMenu(this.DEFAULT_SEQUENCES)\n }\n }\n };\n }\n\n /*\n * Build the menu of key sequences.\n * @param {array} sequencesArray an array of strings of KEY_IDs.\n * @returns {array} an array of objects with text and value properties.\n */\n buildSequenceMenu (sequencesArray) {\n return sequencesArray.map(\n str => this.getMenuItemForSequenceString(str)\n );\n }\n\n /*\n * Create a menu item for a sequence string.\n * @param {string} sequenceString a string of KEY_IDs.\n * @return {object} an object with text and value properties.\n */\n getMenuItemForSequenceString (sequenceString) {\n let sequenceArray = sequenceString.split(' ');\n sequenceArray = sequenceArray.map(str => this.KEY_TEXT_SHORT[str]);\n return {\n text: sequenceArray.join(' '),\n value: sequenceString\n };\n }\n\n /*\n * Check whether a keyboard key is currently pressed.\n * Also, toggle the results of the test on alternate frames, so that the\n * hat block fires repeatedly.\n * @param {object} args - the block arguments.\n * @property {number} KEY - a key code.\n * @param {object} util - utility object provided by the runtime.\n */\n whenMakeyKeyPressed (args, util) {\n let key = args.KEY;\n // Convert the key arg, if it is a KEY_ID, to the key name used by\n // the Keyboard io module.\n if (SCRATCH_KEY_NAME[args.KEY]) {\n key = SCRATCH_KEY_NAME[args.KEY];\n }\n const isDown = util.ioQuery('keyboard', 'getKeyIsDown', [key]);\n return (isDown && this.frameToggle);\n }\n\n /*\n * A function called on the KEY_PRESSED event, to update the key press\n * buffer and check if any of the key sequences have been completed.\n * @param {string} key A scratch key name.\n */\n keyPressed (key) {\n // Store only the first word of the Scratch key name, so that e.g. when\n // \"left arrow\" is pressed, we store \"LEFT\", which matches KEY_ID_LEFT\n key = key.split(' ')[0];\n key = key.toUpperCase();\n this.keyPressBuffer.push(key);\n // Keep the buffer under the length limit\n if (this.keyPressBuffer.length > KEY_BUFFER_LENGTH) {\n this.keyPressBuffer.shift();\n }\n // Check the buffer for each sequence in use\n for (const str in this.sequences) {\n const arr = this.sequences[str].array;\n // Bail out if we don't have enough presses for this sequence\n if (this.keyPressBuffer.length < arr.length) {\n continue;\n }\n let missFlag = false;\n // Slice the buffer to the length of the sequence we're checking\n const bufferSegment = this.keyPressBuffer.slice(-1 * arr.length);\n for (let i = 0; i < arr.length; i++) {\n if (arr[i] !== bufferSegment[i]) {\n missFlag = true;\n }\n }\n // If the miss flag is false, the sequence matched the buffer\n if (!missFlag) {\n this.sequences[str].completed = true;\n // Clear the completed flag after a timeout. This is necessary because\n // the hat is edge-triggered (not event triggered). Multiple hats\n // may be checking the same sequence, so this timeout gives them enough\n // time to all trigger before resetting the flag.\n setTimeout(() => {\n this.sequences[str].completed = false;\n }, SEQUENCE_HAT_TIMEOUT);\n }\n }\n }\n\n /**\n * Clear the key press buffer.\n */\n _clearkeyPressBuffer () {\n this.keyPressBuffer = [];\n }\n\n /*\n * Add a key sequence to the set currently being checked on each key press.\n * @param {string} sequenceString a string of space-separated KEY_IDs.\n * @param {array} sequenceArray an array of KEY_IDs.\n */\n addSequence (sequenceString, sequenceArray) {\n // If we already have this sequence string, return.\n if (Object.prototype.hasOwnProperty.call(this.sequences, sequenceString)) {\n return;\n }\n this.sequences[sequenceString] = {\n array: sequenceArray,\n completed: false\n };\n }\n\n /*\n * Check whether a key sequence was recently completed.\n * @param {object} args The block arguments.\n * @property {number} SEQUENCE A string of KEY_IDs.\n */\n whenCodePressed (args) {\n const sequenceString = Cast.toString(args.SEQUENCE).toUpperCase();\n const sequenceArray = sequenceString.split(' ');\n if (sequenceArray.length < 2) {\n return;\n }\n this.addSequence(sequenceString, sequenceArray);\n\n return this.sequences[sequenceString].completed;\n }\n}\nmodule.exports = Scratch3MakeyMakeyBlocks;\n","const ArgumentType = require('../../extension-support/argument-type');\nconst BlockType = require('../../extension-support/block-type');\nconst log = require('../../util/log');\nconst cast = require('../../util/cast');\nconst formatMessage = require('format-message');\nconst BLE = require('../../io/ble');\nconst Base64Util = require('../../util/base64-util');\n\n/**\n * Icon png to be displayed at the left edge of each extension block, encoded as a data URI.\n * @type {string}\n */\n// eslint-disable-next-line max-len\nconst blockIconURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAACXBIWXMAABYlAAAWJQFJUiTwAAAKcElEQVR42u2cfXAU9RnHv7u3L3d7l9yR5PIGXO7MkQKaYiCUWqJhFGvRMk4JZXSc8aXVaSmiYlthVHQEW99FxiIdrVY6teiMdoa+ICqhIqgQAsjwMgYDOQKXl7uY17u9293b3f5x5JKYe8+FJGSfvzbP/n77e/azz+95nt9v90KoqgpN0hdSQ6AB1ABqADWAmmgANYAaQA2gJhpADeBEE2q8GPLaWzu/CslyiY4k9dOn5uijtXGd7+jWkaReVpT3Hrhv6d0awEFC07rgD+ZeYYnXprhwigUAvjj0zbjxQCLebozT7iDzK1ZUWCru2K7L//6MVC8ue45Blz8n6rlQ815QtuohOlXiEdy/AUqPa6y59Mkh6Q1345GNja6m7pHEQKNl3t0704EXat4L6fSOmOeEI1vHKzwAyNJR9MPFpRUPOu0ONm2A0xatWaTLm5WfDrzvAppA8AbiG03fC8CQNkDKZK2YrPAuRrhpifJERsuYywveJc7CqcIDMAyeLm82dEXzw39I/qjXkpr3QuW9lxfAdOABGAKPslWDnbsy7Jl8BxTeM3SqmO0gaA5U6c3jymup0YSn9JyLee67wpTfBQAQjmyF3HFqiJcRtDECjy5dAmbmcgQPvjjxl3Lx4IVjnD/5cE1zkWtyP34VBGcdKLJnLgc9cznk1kMXFdzEn8KJ4KUqqsSHvcxWDf7j1UM8UPr6/YgHhhX8xAaYaXgAIB7fBnbuSrBzV8aNgarEQ/z6/YkLcDTg9V9XlXjQtuqoU1TpcUHlvZDOfDiuyh5qPMCLrJ1bDw3EuUtx81N/BH3pjQBJQ2HMF5V6iKfeRchVm9kkMtrwxmSdobeA9daBde8GwVlBcFYofS1Jw0vaAy9HeJHQwBUPzIBvGxDc92Rmp/BowJs10wkAONfsBs8HAAAltqngOAO8HZ3o6OiMqcvLy4E1Lwc8H8C5ZndMXdLJa/qNacNLCDBw/O8nFUNWxp/64+tWAwBefe1tHKg7CgC4/9d3ori4EHv3HcDrb26PqVt2602ovvaHaGlpw+8ffSamLqXYmya8jG8mpFy6iGLkWLh4HAwG4+r6j4VBfaPpLgU8IMGO9MLqW2pYQ9aQokuR5dgXIwCC1CUcNMj3hpdvLAdSF54EYpCHooRA0Swomo2pC0kCQpIAkqTA6LmYupgxL0X7m78+aG10NXVkpIwxsAwWXncDCESHLkohfPbpbiT6ZFPPZQ9fC0e58Wi6wTDj6UbT/rQAyiERS2pW4Kc3LQDLRO8miCEAKj7d83FcTxyLJJJJ+9MCqKoq9HomMrgkSThxsgEcZ8AMpwMkSYJlKDA0DVUFiHGWRDJp/4jXwqIo4uFHnkZXdw8AYGbZFXhs3WqQJDkhkkim7E8KoMlkxKbnn8DBunrwUli3e8/+yOAA0HjmHDq7upGXm5PUoDUr7hmWRB5Zt3FYwoime+vtd/H6G9uGJIxouniSyP6H7v8FystnY80jGzIA0MihsMAKu20aTp3JzFb6WCWRuDUvHwByw8cOhw2FBVaYjNzIAba1e3Hfb9aiq7MTNStuBwAsvr4KO3d9GnmKztIS5EyxTJiVSDT7p04tipx/9MnnYc7ORlu7NzMxsK3di5AkDHgGw2DTC+uHBeGJshJJZL/fxyMQEDKbRAiCQDAoQhBDYBkKNE2j4uqrhpUBoiSBIMZfEhkN+1NeiWSqEB2rlUg69md0JRIQRHy86z8jXsqNVRLJlP0jqgNJXXgAgjbCcONmCHUvQ+44NWG2s/rtH5Mt/ciToo0wLH4JBGO6LLazRiJk2vBYy4gHHw/bWSN+LZBKEhkMjzn/CaSiKgQOvJDyFB7L7axUJWNJZDA8IhQA1boPin7KZbMSGfUYyFx9b3hXg/cCsoBA2Z0AoYOaxlcC4+mdyCUDKBzanLFBJ3USyaRMuiSSKZmUSSSTMimTCABUlblRU9kAZ0E39p+eii21c+EL0jHbOwu6sfaWgyjND//U4oP6MmzZnfi79XT7mfQSNi7bh0JzOLG19XBY/89r49pYVebGqhuOosDsh1+gsWV3BXYdd2Q+BlaVuXFv9bHgkSbzk+vfcVRyjHhi47J9cftsXLYf7T36Ix8cLHlo6ydlv6qpPI2qssRZcuOy/Wjp4k5s+2zG+offKqtcUt6kJtNv7S0H0RtkvEufXTB/6bML5je2Wy7UVDbEbF9o9mPDsv2oP5v75vbPS26rP5u3fdXiozDppcwDrKlswOlWy9E//DX09Mt/azh8zzNM1RybF86C7pheVGD240CDeX3NWtfml94Rt+0+Mf3Lm8qbEnpfgdmPs+3G9+564vTT//pM/GrHYduWRP0AYOEMN/5S61xT92Vtfd2XtfWb/vu91fHALyxzw9tnkB/cTD5w+2Ou9375HHtfa7exM5mxRpKFaafdQQKgAcDERs98/foLHrXdaXfoABi8vczhWO2/28/TRR5z2h00gKymNl1ton79oigq6bQ7dE67Q+ew9mb1h4FYYwVESgLAXLSRa+3mWpIdK+UYuPiq89f8+XfT/+ftZQ4vLm9ZmUyfdcsv1M2fWfRaUCK8i8vdK1u6ktuAWPWTsztm24o/cnnYHUsrWzd1+fVJ9XtqxbG3XzFdNcPTawjcueibpxK1t+X26f/9R8a953jub4typOvm2b1XnvUmv8JKWMZcaZffX3XDERRP8cGaFRjWxtPLoZvXY4oxgPBNEsgxBhCUKEzL6Ru+JydS8Ak0giKFgESDJFQoKmCgQzAwIfQEWETzmoBIwd2VNaStu8uEHGO4Buz06zHHFv0dRkefAZ1+PQx0KNK2eIoPLCUj2zDc275qzgcBFWv+cf3IyxgTK2KOzQufEM5kfpGF12eGPSf8DXN+No/87HDWiwYYALw+M6ym8AscAxO++X7xCTRM7EDQzht0Da8v/NWo1dQDAxNCocUXs+303IGHdaptOmYXnh/SLlZbV+fwnwJm6UXEm/ojqgM/PFmJQ81OPHfrtqT7bN23BE8seTflYLvz5DwYGQHLKz5Puo/XZ8aLtT+D1dSDuxbsGQIymmz48DbwIguOESJOcce8XaO3oVpZ8k3Em5KVVAAMFnuOB9as1MbimCBunn04vBmR40ls29Wfgxf1KMn1gBdY+MXUCvK4ANvPndpLzrLzALjBN2VPwrDBksgLYkn1jBMp90nVY2++8vAw3RlPeLNYVZSPAEgjKWP6ZCn4lF+gMdnE08spQb73RQB9aXtgo6tJcNodf8rWz3L//Br340UW3sExEkXrFFKSSUVHqkRfkJZ8QSZk5gS6hw9H+GyDQAclSs41BVmSUIn+toAKIUTJskKoQUknCxKlkISKb/sM0NMyyVAhXW+AlYosfgOgQlUJVadTSUWBKoQoudvPioPbenq5oIUTaRUqenhWKi3oyVIUqKpKREoLggDhF6hQb4CV9LRM9rctMPN6glChp2SdTqeSskwoAECSKnG61fzFR/XsGu+FhmONriYl7TImsjoYKJyZSeB8CoBQo6spqU8TCO1fgE7gDVUNoCYaQA2gBlADqAHURAOoAdQAagA10QCOgfwfNp/hXbfBMCAAAAAASUVORK5CYII=';\n\n/**\n * Enum for micro:bit BLE command protocol.\n * https://github.com/scratchfoundation/scratch-microbit-firmware/blob/master/protocol.md\n * @readonly\n * @enum {number}\n */\nconst BLECommand = {\n CMD_PIN_CONFIG: 0x80,\n CMD_DISPLAY_TEXT: 0x81,\n CMD_DISPLAY_LED: 0x82\n};\n\n\n/**\n * A time interval to wait (in milliseconds) before reporting to the BLE socket\n * that data has stopped coming from the peripheral.\n */\nconst BLETimeout = 4500;\n\n/**\n * A time interval to wait (in milliseconds) while a block that sends a BLE message is running.\n * @type {number}\n */\nconst BLESendInterval = 100;\n\n/**\n * A string to report to the BLE socket when the micro:bit has stopped receiving data.\n * @type {string}\n */\nconst BLEDataStoppedError = 'micro:bit extension stopped receiving data';\n\n/**\n * Enum for micro:bit protocol.\n * https://github.com/scratchfoundation/scratch-microbit-firmware/blob/master/protocol.md\n * @readonly\n * @enum {string}\n */\nconst BLEUUID = {\n service: 0xf005,\n rxChar: '5261da01-fa7e-42ab-850b-7c80220097cc',\n txChar: '5261da02-fa7e-42ab-850b-7c80220097cc'\n};\n\n/**\n * Manage communication with a MicroBit peripheral over a Scrath Link client socket.\n */\nclass MicroBit {\n\n /**\n * Construct a MicroBit communication object.\n * @param {Runtime} runtime - the Scratch 3.0 runtime\n * @param {string} extensionId - the id of the extension\n */\n constructor (runtime, extensionId) {\n\n /**\n * The Scratch 3.0 runtime used to trigger the green flag button.\n * @type {Runtime}\n * @private\n */\n this._runtime = runtime;\n\n /**\n * The BluetoothLowEnergy connection socket for reading/writing peripheral data.\n * @type {BLE}\n * @private\n */\n this._ble = null;\n this._runtime.registerPeripheralExtension(extensionId, this);\n\n /**\n * The id of the extension this peripheral belongs to.\n */\n this._extensionId = extensionId;\n\n /**\n * The most recently received value for each sensor.\n * @type {Object.}\n * @private\n */\n this._sensors = {\n tiltX: 0,\n tiltY: 0,\n buttonA: 0,\n buttonB: 0,\n touchPins: [0, 0, 0],\n gestureState: 0,\n ledMatrixState: new Uint8Array(5)\n };\n\n /**\n * The most recently received value for each gesture.\n * @type {Object.}\n * @private\n */\n this._gestures = {\n moving: false,\n move: {\n active: false,\n timeout: false\n },\n shake: {\n active: false,\n timeout: false\n },\n jump: {\n active: false,\n timeout: false\n }\n };\n\n /**\n * Interval ID for data reading timeout.\n * @type {number}\n * @private\n */\n this._timeoutID = null;\n\n /**\n * A flag that is true while we are busy sending data to the BLE socket.\n * @type {boolean}\n * @private\n */\n this._busy = false;\n\n /**\n * ID for a timeout which is used to clear the busy flag if it has been\n * true for a long time.\n */\n this._busyTimeoutID = null;\n\n this.reset = this.reset.bind(this);\n this._onConnect = this._onConnect.bind(this);\n this._onMessage = this._onMessage.bind(this);\n }\n\n /**\n * @param {string} text - the text to display.\n * @return {Promise} - a Promise that resolves when writing to peripheral.\n */\n displayText (text) {\n const output = new Uint8Array(text.length);\n for (let i = 0; i < text.length; i++) {\n output[i] = text.charCodeAt(i);\n }\n return this.send(BLECommand.CMD_DISPLAY_TEXT, output);\n }\n\n /**\n * @param {Uint8Array} matrix - the matrix to display.\n * @return {Promise} - a Promise that resolves when writing to peripheral.\n */\n displayMatrix (matrix) {\n return this.send(BLECommand.CMD_DISPLAY_LED, matrix);\n }\n\n /**\n * @return {number} - the latest value received for the tilt sensor's tilt about the X axis.\n */\n get tiltX () {\n return this._sensors.tiltX;\n }\n\n /**\n * @return {number} - the latest value received for the tilt sensor's tilt about the Y axis.\n */\n get tiltY () {\n return this._sensors.tiltY;\n }\n\n /**\n * @return {boolean} - the latest value received for the A button.\n */\n get buttonA () {\n return this._sensors.buttonA;\n }\n\n /**\n * @return {boolean} - the latest value received for the B button.\n */\n get buttonB () {\n return this._sensors.buttonB;\n }\n\n /**\n * @return {number} - the latest value received for the motion gesture states.\n */\n get gestureState () {\n return this._sensors.gestureState;\n }\n\n /**\n * @return {Uint8Array} - the current state of the 5x5 LED matrix.\n */\n get ledMatrixState () {\n return this._sensors.ledMatrixState;\n }\n\n /**\n * Called by the runtime when user wants to scan for a peripheral.\n */\n scan () {\n if (this._ble) {\n this._ble.disconnect();\n }\n this._ble = new BLE(this._runtime, this._extensionId, {\n filters: [\n {services: [BLEUUID.service]}\n ]\n }, this._onConnect, this.reset);\n }\n\n /**\n * Called by the runtime when user wants to connect to a certain peripheral.\n * @param {number} id - the id of the peripheral to connect to.\n */\n connect (id) {\n if (this._ble) {\n this._ble.connectPeripheral(id);\n }\n }\n\n /**\n * Disconnect from the micro:bit.\n */\n disconnect () {\n if (this._ble) {\n this._ble.disconnect();\n }\n\n this.reset();\n }\n\n /**\n * Reset all the state and timeout/interval ids.\n */\n reset () {\n if (this._timeoutID) {\n window.clearTimeout(this._timeoutID);\n this._timeoutID = null;\n }\n }\n\n /**\n * Return true if connected to the micro:bit.\n * @return {boolean} - whether the micro:bit is connected.\n */\n isConnected () {\n let connected = false;\n if (this._ble) {\n connected = this._ble.isConnected();\n }\n return connected;\n }\n\n /**\n * Send a message to the peripheral BLE socket.\n * @param {number} command - the BLE command hex.\n * @param {Uint8Array} message - the message to write\n */\n send (command, message) {\n if (!this.isConnected()) return;\n if (this._busy) return;\n\n // Set a busy flag so that while we are sending a message and waiting for\n // the response, additional messages are ignored.\n this._busy = true;\n\n // Set a timeout after which to reset the busy flag. This is used in case\n // a BLE message was sent for which we never received a response, because\n // e.g. the peripheral was turned off after the message was sent. We reset\n // the busy flag after a while so that it is possible to try again later.\n this._busyTimeoutID = window.setTimeout(() => {\n this._busy = false;\n }, 5000);\n\n const output = new Uint8Array(message.length + 1);\n output[0] = command; // attach command to beginning of message\n for (let i = 0; i < message.length; i++) {\n output[i + 1] = message[i];\n }\n const data = Base64Util.uint8ArrayToBase64(output);\n\n this._ble.write(BLEUUID.service, BLEUUID.txChar, data, 'base64', true).then(\n () => {\n this._busy = false;\n window.clearTimeout(this._busyTimeoutID);\n }\n );\n }\n\n /**\n * Starts reading data from peripheral after BLE has connected to it.\n * @private\n */\n _onConnect () {\n this._ble.read(BLEUUID.service, BLEUUID.rxChar, true, this._onMessage);\n this._timeoutID = window.setTimeout(\n () => this._ble.handleDisconnectError(BLEDataStoppedError),\n BLETimeout\n );\n }\n\n /**\n * Process the sensor data from the incoming BLE characteristic.\n * @param {object} base64 - the incoming BLE data.\n * @private\n */\n _onMessage (base64) {\n // parse data\n const data = Base64Util.base64ToUint8Array(base64);\n\n this._sensors.tiltX = data[1] | (data[0] << 8);\n if (this._sensors.tiltX > (1 << 15)) this._sensors.tiltX -= (1 << 16);\n this._sensors.tiltY = data[3] | (data[2] << 8);\n if (this._sensors.tiltY > (1 << 15)) this._sensors.tiltY -= (1 << 16);\n\n this._sensors.buttonA = data[4];\n this._sensors.buttonB = data[5];\n\n this._sensors.touchPins[0] = data[6];\n this._sensors.touchPins[1] = data[7];\n this._sensors.touchPins[2] = data[8];\n\n this._sensors.gestureState = data[9];\n\n // cancel disconnect timeout and start a new one\n window.clearTimeout(this._timeoutID);\n this._timeoutID = window.setTimeout(\n () => this._ble.handleDisconnectError(BLEDataStoppedError),\n BLETimeout\n );\n }\n\n /**\n * @param {number} pin - the pin to check touch state.\n * @return {number} - the latest value received for the touch pin states.\n * @private\n */\n _checkPinState (pin) {\n return this._sensors.touchPins[pin];\n }\n}\n\n/**\n * Enum for tilt sensor direction.\n * @readonly\n * @enum {string}\n */\nconst MicroBitTiltDirection = {\n FRONT: 'front',\n BACK: 'back',\n LEFT: 'left',\n RIGHT: 'right',\n ANY: 'any'\n};\n\n/**\n * Enum for micro:bit gestures.\n * @readonly\n * @enum {string}\n */\nconst MicroBitGestures = {\n MOVED: 'moved',\n SHAKEN: 'shaken',\n JUMPED: 'jumped'\n};\n\n/**\n * Enum for micro:bit buttons.\n * @readonly\n * @enum {string}\n */\nconst MicroBitButtons = {\n A: 'A',\n B: 'B',\n ANY: 'any'\n};\n\n/**\n * Enum for micro:bit pin states.\n * @readonly\n * @enum {string}\n */\nconst MicroBitPinState = {\n ON: 'on',\n OFF: 'off'\n};\n\n/**\n * Scratch 3.0 blocks to interact with a MicroBit peripheral.\n */\nclass Scratch3MicroBitBlocks {\n\n /**\n * @return {string} - the name of this extension.\n */\n static get EXTENSION_NAME () {\n return 'micro:bit';\n }\n\n /**\n * @return {string} - the ID of this extension.\n */\n static get EXTENSION_ID () {\n return 'microbit';\n }\n\n /**\n * @return {number} - the tilt sensor counts as \"tilted\" if its tilt angle meets or exceeds this threshold.\n */\n static get TILT_THRESHOLD () {\n return 15;\n }\n\n /**\n * @return {array} - text and values for each buttons menu element\n */\n get BUTTONS_MENU () {\n return [\n {\n text: 'A',\n value: MicroBitButtons.A\n },\n {\n text: 'B',\n value: MicroBitButtons.B\n },\n {\n text: formatMessage({\n id: 'microbit.buttonsMenu.any',\n default: 'any',\n description: 'label for \"any\" element in button picker for micro:bit extension'\n }),\n value: MicroBitButtons.ANY\n }\n ];\n }\n\n /**\n * @return {array} - text and values for each gestures menu element\n */\n get GESTURES_MENU () {\n return [\n {\n text: formatMessage({\n id: 'microbit.gesturesMenu.moved',\n default: 'moved',\n description: 'label for moved gesture in gesture picker for micro:bit extension'\n }),\n value: MicroBitGestures.MOVED\n },\n {\n text: formatMessage({\n id: 'microbit.gesturesMenu.shaken',\n default: 'shaken',\n description: 'label for shaken gesture in gesture picker for micro:bit extension'\n }),\n value: MicroBitGestures.SHAKEN\n },\n {\n text: formatMessage({\n id: 'microbit.gesturesMenu.jumped',\n default: 'jumped',\n description: 'label for jumped gesture in gesture picker for micro:bit extension'\n }),\n value: MicroBitGestures.JUMPED\n }\n ];\n }\n\n /**\n * @return {array} - text and values for each pin state menu element\n */\n get PIN_STATE_MENU () {\n return [\n {\n text: formatMessage({\n id: 'microbit.pinStateMenu.on',\n default: 'on',\n description: 'label for on element in pin state picker for micro:bit extension'\n }),\n value: MicroBitPinState.ON\n },\n {\n text: formatMessage({\n id: 'microbit.pinStateMenu.off',\n default: 'off',\n description: 'label for off element in pin state picker for micro:bit extension'\n }),\n value: MicroBitPinState.OFF\n }\n ];\n }\n\n /**\n * @return {array} - text and values for each tilt direction menu element\n */\n get TILT_DIRECTION_MENU () {\n return [\n {\n text: formatMessage({\n id: 'microbit.tiltDirectionMenu.front',\n default: 'front',\n description: 'label for front element in tilt direction picker for micro:bit extension'\n }),\n value: MicroBitTiltDirection.FRONT\n },\n {\n text: formatMessage({\n id: 'microbit.tiltDirectionMenu.back',\n default: 'back',\n description: 'label for back element in tilt direction picker for micro:bit extension'\n }),\n value: MicroBitTiltDirection.BACK\n },\n {\n text: formatMessage({\n id: 'microbit.tiltDirectionMenu.left',\n default: 'left',\n description: 'label for left element in tilt direction picker for micro:bit extension'\n }),\n value: MicroBitTiltDirection.LEFT\n },\n {\n text: formatMessage({\n id: 'microbit.tiltDirectionMenu.right',\n default: 'right',\n description: 'label for right element in tilt direction picker for micro:bit extension'\n }),\n value: MicroBitTiltDirection.RIGHT\n }\n ];\n }\n\n /**\n * @return {array} - text and values for each tilt direction (plus \"any\") menu element\n */\n get TILT_DIRECTION_ANY_MENU () {\n return [\n ...this.TILT_DIRECTION_MENU,\n {\n text: formatMessage({\n id: 'microbit.tiltDirectionMenu.any',\n default: 'any',\n description: 'label for any direction element in tilt direction picker for micro:bit extension'\n }),\n value: MicroBitTiltDirection.ANY\n }\n ];\n }\n\n /**\n * Construct a set of MicroBit blocks.\n * @param {Runtime} runtime - the Scratch 3.0 runtime.\n */\n constructor (runtime) {\n /**\n * The Scratch 3.0 runtime.\n * @type {Runtime}\n */\n this.runtime = runtime;\n\n // Create a new MicroBit peripheral instance\n this._peripheral = new MicroBit(this.runtime, Scratch3MicroBitBlocks.EXTENSION_ID);\n }\n\n /**\n * @returns {object} metadata for this extension and its blocks.\n */\n getInfo () {\n return {\n id: Scratch3MicroBitBlocks.EXTENSION_ID,\n name: Scratch3MicroBitBlocks.EXTENSION_NAME,\n blockIconURI: blockIconURI,\n showStatusButton: true,\n blocks: [\n {\n opcode: 'whenButtonPressed',\n text: formatMessage({\n id: 'microbit.whenButtonPressed',\n default: 'when [BTN] button pressed',\n description: 'when the selected button on the micro:bit is pressed'\n }),\n blockType: BlockType.HAT,\n arguments: {\n BTN: {\n type: ArgumentType.STRING,\n menu: 'buttons',\n defaultValue: MicroBitButtons.A\n }\n }\n },\n {\n opcode: 'isButtonPressed',\n text: formatMessage({\n id: 'microbit.isButtonPressed',\n default: '[BTN] button pressed?',\n description: 'is the selected button on the micro:bit pressed?'\n }),\n blockType: BlockType.BOOLEAN,\n arguments: {\n BTN: {\n type: ArgumentType.STRING,\n menu: 'buttons',\n defaultValue: MicroBitButtons.A\n }\n }\n },\n '---',\n {\n opcode: 'whenGesture',\n text: formatMessage({\n id: 'microbit.whenGesture',\n default: 'when [GESTURE]',\n description: 'when the selected gesture is detected by the micro:bit'\n }),\n blockType: BlockType.HAT,\n arguments: {\n GESTURE: {\n type: ArgumentType.STRING,\n menu: 'gestures',\n defaultValue: MicroBitGestures.MOVED\n }\n }\n },\n '---',\n {\n opcode: 'displaySymbol',\n text: formatMessage({\n id: 'microbit.displaySymbol',\n default: 'display [MATRIX]',\n description: 'display a pattern on the micro:bit display'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n MATRIX: {\n type: ArgumentType.MATRIX,\n defaultValue: '0101010101100010101000100'\n }\n }\n },\n {\n opcode: 'displayText',\n text: formatMessage({\n id: 'microbit.displayText',\n default: 'display text [TEXT]',\n description: 'display text on the micro:bit display'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n TEXT: {\n type: ArgumentType.STRING,\n defaultValue: formatMessage({\n id: 'microbit.defaultTextToDisplay',\n default: 'Hello!',\n description: `default text to display.\n IMPORTANT - the micro:bit only supports letters a-z, A-Z.\n Please substitute a default word in your language\n that can be written with those characters,\n substitute non-accented characters or leave it as \"Hello!\".\n Check the micro:bit site documentation for details`\n })\n }\n }\n },\n {\n opcode: 'displayClear',\n text: formatMessage({\n id: 'microbit.clearDisplay',\n default: 'clear display',\n description: 'display nothing on the micro:bit display'\n }),\n blockType: BlockType.COMMAND\n },\n '---',\n {\n opcode: 'whenTilted',\n text: formatMessage({\n id: 'microbit.whenTilted',\n default: 'when tilted [DIRECTION]',\n description: 'when the micro:bit is tilted in a direction'\n }),\n blockType: BlockType.HAT,\n arguments: {\n DIRECTION: {\n type: ArgumentType.STRING,\n menu: 'tiltDirectionAny',\n defaultValue: MicroBitTiltDirection.ANY\n }\n }\n },\n {\n opcode: 'isTilted',\n text: formatMessage({\n id: 'microbit.isTilted',\n default: 'tilted [DIRECTION]?',\n description: 'is the micro:bit is tilted in a direction?'\n }),\n blockType: BlockType.BOOLEAN,\n arguments: {\n DIRECTION: {\n type: ArgumentType.STRING,\n menu: 'tiltDirectionAny',\n defaultValue: MicroBitTiltDirection.ANY\n }\n }\n },\n {\n opcode: 'getTiltAngle',\n text: formatMessage({\n id: 'microbit.tiltAngle',\n default: 'tilt angle [DIRECTION]',\n description: 'how much the micro:bit is tilted in a direction'\n }),\n blockType: BlockType.REPORTER,\n arguments: {\n DIRECTION: {\n type: ArgumentType.STRING,\n menu: 'tiltDirection',\n defaultValue: MicroBitTiltDirection.FRONT\n }\n }\n },\n '---',\n {\n opcode: 'whenPinConnected',\n text: formatMessage({\n id: 'microbit.whenPinConnected',\n default: 'when pin [PIN] connected',\n description: 'when the pin detects a connection to Earth/Ground'\n\n }),\n blockType: BlockType.HAT,\n arguments: {\n PIN: {\n type: ArgumentType.STRING,\n menu: 'touchPins',\n defaultValue: '0'\n }\n }\n }\n ],\n menus: {\n buttons: {\n acceptReporters: true,\n items: this.BUTTONS_MENU\n },\n gestures: {\n acceptReporters: true,\n items: this.GESTURES_MENU\n },\n pinState: {\n acceptReporters: true,\n items: this.PIN_STATE_MENU\n },\n tiltDirection: {\n acceptReporters: true,\n items: this.TILT_DIRECTION_MENU\n },\n tiltDirectionAny: {\n acceptReporters: true,\n items: this.TILT_DIRECTION_ANY_MENU\n },\n touchPins: {\n acceptReporters: true,\n items: ['0', '1', '2']\n }\n }\n };\n }\n\n /**\n * Test whether the A or B button is pressed\n * @param {object} args - the block's arguments.\n * @return {boolean} - true if the button is pressed.\n */\n whenButtonPressed (args) {\n if (args.BTN === 'any') {\n return this._peripheral.buttonA | this._peripheral.buttonB;\n } else if (args.BTN === 'A') {\n return this._peripheral.buttonA;\n } else if (args.BTN === 'B') {\n return this._peripheral.buttonB;\n }\n return false;\n }\n\n /**\n * Test whether the A or B button is pressed\n * @param {object} args - the block's arguments.\n * @return {boolean} - true if the button is pressed.\n */\n isButtonPressed (args) {\n if (args.BTN === 'any') {\n return (this._peripheral.buttonA | this._peripheral.buttonB) !== 0;\n } else if (args.BTN === 'A') {\n return this._peripheral.buttonA !== 0;\n } else if (args.BTN === 'B') {\n return this._peripheral.buttonB !== 0;\n }\n return false;\n }\n\n /**\n * Test whether the micro:bit is moving\n * @param {object} args - the block's arguments.\n * @return {boolean} - true if the micro:bit is moving.\n */\n whenGesture (args) {\n const gesture = cast.toString(args.GESTURE);\n if (gesture === 'moved') {\n return (this._peripheral.gestureState >> 2) & 1;\n } else if (gesture === 'shaken') {\n return this._peripheral.gestureState & 1;\n } else if (gesture === 'jumped') {\n return (this._peripheral.gestureState >> 1) & 1;\n }\n return false;\n }\n\n /**\n * Display a predefined symbol on the 5x5 LED matrix.\n * @param {object} args - the block's arguments.\n * @return {Promise} - a Promise that resolves after a tick.\n */\n displaySymbol (args) {\n const symbol = cast.toString(args.MATRIX).replace(/\\s/g, '');\n const reducer = (accumulator, c, index) => {\n const value = (c === '0') ? accumulator : accumulator + Math.pow(2, index);\n return value;\n };\n const hex = symbol.split('').reduce(reducer, 0);\n if (hex !== null) {\n this._peripheral.ledMatrixState[0] = hex & 0x1F;\n this._peripheral.ledMatrixState[1] = (hex >> 5) & 0x1F;\n this._peripheral.ledMatrixState[2] = (hex >> 10) & 0x1F;\n this._peripheral.ledMatrixState[3] = (hex >> 15) & 0x1F;\n this._peripheral.ledMatrixState[4] = (hex >> 20) & 0x1F;\n this._peripheral.displayMatrix(this._peripheral.ledMatrixState);\n }\n\n return new Promise(resolve => {\n setTimeout(() => {\n resolve();\n }, BLESendInterval);\n });\n }\n\n /**\n * Display text on the 5x5 LED matrix.\n * @param {object} args - the block's arguments.\n * @return {Promise} - a Promise that resolves after the text is done printing.\n * Note the limit is 19 characters\n * The print time is calculated by multiplying the number of horizontal pixels\n * by the default scroll delay of 120ms.\n * The number of horizontal pixels = 6px for each character in the string,\n * 1px before the string, and 5px after the string.\n */\n displayText (args) {\n const text = String(args.TEXT).substring(0, 19);\n if (text.length > 0) this._peripheral.displayText(text);\n const yieldDelay = 120 * ((6 * text.length) + 6);\n\n return new Promise(resolve => {\n setTimeout(() => {\n resolve();\n }, yieldDelay);\n });\n }\n\n /**\n * Turn all 5x5 matrix LEDs off.\n * @return {Promise} - a Promise that resolves after a tick.\n */\n displayClear () {\n for (let i = 0; i < 5; i++) {\n this._peripheral.ledMatrixState[i] = 0;\n }\n this._peripheral.displayMatrix(this._peripheral.ledMatrixState);\n\n return new Promise(resolve => {\n setTimeout(() => {\n resolve();\n }, BLESendInterval);\n });\n }\n\n /**\n * Test whether the tilt sensor is currently tilted.\n * @param {object} args - the block's arguments.\n * @property {TiltDirection} DIRECTION - the tilt direction to test (front, back, left, right, or any).\n * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction.\n */\n whenTilted (args) {\n return this._isTilted(args.DIRECTION);\n }\n\n /**\n * Test whether the tilt sensor is currently tilted.\n * @param {object} args - the block's arguments.\n * @property {TiltDirection} DIRECTION - the tilt direction to test (front, back, left, right, or any).\n * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction.\n */\n isTilted (args) {\n return this._isTilted(args.DIRECTION);\n }\n\n /**\n * @param {object} args - the block's arguments.\n * @property {TiltDirection} DIRECTION - the direction (front, back, left, right) to check.\n * @return {number} - the tilt sensor's angle in the specified direction.\n * Note that getTiltAngle(front) = -getTiltAngle(back) and getTiltAngle(left) = -getTiltAngle(right).\n */\n getTiltAngle (args) {\n return this._getTiltAngle(args.DIRECTION);\n }\n\n /**\n * Test whether the tilt sensor is currently tilted.\n * @param {TiltDirection} direction - the tilt direction to test (front, back, left, right, or any).\n * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction.\n * @private\n */\n _isTilted (direction) {\n switch (direction) {\n case MicroBitTiltDirection.ANY:\n return (Math.abs(this._peripheral.tiltX / 10) >= Scratch3MicroBitBlocks.TILT_THRESHOLD) ||\n (Math.abs(this._peripheral.tiltY / 10) >= Scratch3MicroBitBlocks.TILT_THRESHOLD);\n default:\n return this._getTiltAngle(direction) >= Scratch3MicroBitBlocks.TILT_THRESHOLD;\n }\n }\n\n /**\n * @param {TiltDirection} direction - the direction (front, back, left, right) to check.\n * @return {number} - the tilt sensor's angle in the specified direction.\n * Note that getTiltAngle(front) = -getTiltAngle(back) and getTiltAngle(left) = -getTiltAngle(right).\n * @private\n */\n _getTiltAngle (direction) {\n switch (direction) {\n case MicroBitTiltDirection.FRONT:\n return Math.round(this._peripheral.tiltY / -10);\n case MicroBitTiltDirection.BACK:\n return Math.round(this._peripheral.tiltY / 10);\n case MicroBitTiltDirection.LEFT:\n return Math.round(this._peripheral.tiltX / -10);\n case MicroBitTiltDirection.RIGHT:\n return Math.round(this._peripheral.tiltX / 10);\n default:\n log.warn(`Unknown tilt direction in _getTiltAngle: ${direction}`);\n }\n }\n\n /**\n * @param {object} args - the block's arguments.\n * @return {boolean} - the touch pin state.\n * @private\n */\n whenPinConnected (args) {\n const pin = parseInt(args.PIN, 10);\n if (isNaN(pin)) return;\n if (pin < 0 || pin > 2) return false;\n return this._peripheral._checkPinState(pin);\n }\n}\n\nmodule.exports = Scratch3MicroBitBlocks;\n","const ArgumentType = require('../../extension-support/argument-type');\nconst BlockType = require('../../extension-support/block-type');\nconst Clone = require('../../util/clone');\nconst Cast = require('../../util/cast');\nconst formatMessage = require('format-message');\nconst MathUtil = require('../../util/math-util');\nconst Timer = require('../../util/timer');\n\n/**\n * The instrument and drum sounds, loaded as static assets.\n * @type {object}\n */\nlet assetData = {};\ntry {\n assetData = require('./manifest');\n} catch (e) {\n // Non-webpack environment, don't worry about assets.\n}\n\n/**\n * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.\n * @type {string}\n */\n// eslint-disable-next-line max-len\nconst blockIconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PHRpdGxlPm11c2ljLWJsb2NrLWljb248L3RpdGxlPjxkZWZzPjxwYXRoIGQ9Ik0zMi4xOCAyNS44NzRDMzIuNjM2IDI4LjE1NyAzMC41MTIgMzAgMjcuNDMzIDMwYy0zLjA3IDAtNS45MjMtMS44NDMtNi4zNzItNC4xMjYtLjQ1OC0yLjI4NSAxLjY2NS00LjEzNiA0Ljc0My00LjEzNi42NDcgMCAxLjI4My4wODQgMS44OS4yMzQuMzM4LjA4Ni42MzcuMTguOTM4LjMwMi44Ny0uMDItLjEwNC0yLjI5NC0xLjgzNS0xMi4yMy0yLjEzNC0xMi4zMDIgMy4wNi0xLjg3IDguNzY4LTIuNzUyIDUuNzA4LS44ODUuMDc2IDQuODItMy42NSAzLjg0NC0zLjcyNC0uOTg3LTQuNjUtNy4xNTMuMjYzIDE0LjczOHptLTE2Ljk5OCA1Ljk5QzE1LjYzIDM0LjE0OCAxMy41MDcgMzYgMTAuNDQgMzZjLTMuMDcgMC01LjkyMi0xLjg1Mi02LjM4LTQuMTM2LS40NDgtMi4yODQgMS42NzQtNC4xMzUgNC43NS00LjEzNSAxLjAwMyAwIDEuOTc1LjE5NiAyLjg1NS41NDMuODIyLS4wNTUtLjE1LTIuMzc3LTEuODYyLTEyLjIyOC0yLjEzMy0xMi4zMDMgMy4wNi0xLjg3IDguNzY0LTIuNzUzIDUuNzA2LS44OTQuMDc2IDQuODItMy42NDggMy44MzQtMy43MjQtLjk4Ny00LjY1LTcuMTUyLjI2MiAxNC43Mzh6IiBpZD0iYSIvPjwvZGVmcz48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjx1c2UgZmlsbD0iI0ZGRiIgeGxpbms6aHJlZj0iI2EiLz48cGF0aCBzdHJva2Utb3BhY2l0eT0iLjEiIHN0cm9rZT0iIzAwMCIgZD0iTTI4LjQ1NiAyMS42NzVjLS4wMS0uMzEyLS4wODctLjgyNS0uMjU2LTEuNzAyLS4wOTYtLjQ5NS0uNjEyLTMuMDIyLS43NTMtMy43My0uMzk1LTEuOTgtLjc2LTMuOTItMS4xNDItNi4xMTMtLjczMi00LjIyMy0uNjkzLTYuMDUuMzQ0LTYuNTI3LjUtLjIzIDEuMDYtLjA4IDEuODQuMzUuNDE0LjIyNyAyLjE4MiAxLjM2NSAyLjA3IDEuMjk2IDEuOTk0IDEuMjQyIDMuNDY0IDEuNzc0IDQuOTMgMS41NDggMS41MjYtLjIzNyAyLjUwNC0uMDYgMi44NzYuNjE4LjM0OC42MzUuMDE1IDEuNDE2LS43MyAyLjE4LTEuNDcyIDEuNTE2LTMuOTc1IDIuNTE0LTUuODQ4IDIuMDIzLS44MjItLjIyLTEuMjM4LS40NjUtMi4zOC0xLjI2N2wtLjA5NS0uMDY2Yy4wNDcuNTkzLjI2NCAxLjc0LjcxNyAzLjgwMy4yOTQgMS4zMzYgMi4wOCA5LjE4NyAyLjYzNyAxMS42NzRsLjAwMi4wMTJjLjUyOCAyLjYzNy0xLjg3MyA0LjcyNC01LjIzNiA0LjcyNC0zLjI5IDAtNi4zNjMtMS45ODgtNi44NjItNC41MjgtLjUzLTIuNjQgMS44NzMtNC43MzQgNS4yMzMtNC43MzQuNjcyIDAgMS4zNDcuMDg1IDIuMDE0LjI1LjIyNy4wNTcuNDM2LjExOC42MzYuMTg3em0tMTYuOTk2IDUuOTljLS4wMS0uMzE4LS4wOS0uODM4LS4yNjYtMS43MzctLjA5LS40Ni0uNTk1LTIuOTM3LS43NTMtMy43MjctLjM5LTEuOTYtLjc1LTMuODktMS4xMy02LjA3LS43MzItNC4yMjMtLjY5Mi02LjA1LjM0NC02LjUyNi41MDItLjIzIDEuMDYtLjA4MiAxLjg0LjM1LjQxNS4yMjcgMi4xODIgMS4zNjQgMi4wNyAxLjI5NSAxLjk5MyAxLjI0MiAzLjQ2MiAxLjc3NCA0LjkyNiAxLjU0OCAxLjUyNS0uMjQgMi41MDQtLjA2NCAyLjg3Ni42MTQuMzQ4LjYzNS4wMTUgMS40MTUtLjcyOCAyLjE4LTEuNDc0IDEuNTE3LTMuOTc3IDIuNTEzLTUuODQ3IDIuMDE3LS44Mi0uMjItMS4yMzYtLjQ2NC0yLjM3OC0xLjI2N2wtLjA5NS0uMDY1Yy4wNDcuNTkzLjI2NCAxLjc0LjcxNyAzLjgwMi4yOTQgMS4zMzcgMi4wNzggOS4xOSAyLjYzNiAxMS42NzVsLjAwMy4wMTNjLjUxNyAyLjYzOC0xLjg4NCA0LjczMi01LjIzNCA0LjczMi0zLjI4NyAwLTYuMzYtMS45OTMtNi44Ny00LjU0LS41Mi0yLjY0IDEuODg0LTQuNzMgNS4yNC00LjczLjkwNSAwIDEuODAzLjE1IDIuNjUuNDM2eiIvPjwvZz48L3N2Zz4=';\n\n/**\n * Icon svg to be displayed in the category menu, encoded as a data URI.\n * @type {string}\n */\n// eslint-disable-next-line max-len\nconst menuIconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE2LjA5IDEyLjkzN2MuMjI4IDEuMTQxLS44MzMgMi4wNjMtMi4zNzMgMi4wNjMtMS41MzUgMC0yLjk2Mi0uOTIyLTMuMTg2LTIuMDYzLS4yMy0xLjE0Mi44MzMtMi4wNjggMi4zNzItMi4wNjguMzIzIDAgLjY0MS4wNDIuOTQ1LjExN2EzLjUgMy41IDAgMCAxIC40NjguMTUxYy40MzUtLjAxLS4wNTItMS4xNDctLjkxNy02LjExNC0xLjA2Ny02LjE1MiAxLjUzLS45MzUgNC4zODQtMS4zNzcgMi44NTQtLjQ0Mi4wMzggMi40MS0xLjgyNSAxLjkyMi0xLjg2Mi0uNDkzLTIuMzI1LTMuNTc3LjEzMiA3LjM3ek03LjQ2IDguNTYzYy0xLjg2Mi0uNDkzLTIuMzI1LTMuNTc2LjEzIDcuMzdDNy44MTYgMTcuMDczIDYuNzU0IDE4IDUuMjIgMThjLTEuNTM1IDAtMi45NjEtLjkyNi0zLjE5LTIuMDY4LS4yMjQtMS4xNDIuODM3LTIuMDY3IDIuMzc1LTIuMDY3LjUwMSAwIC45ODcuMDk4IDEuNDI3LjI3Mi40MTItLjAyOC0uMDc0LTEuMTg5LS45My02LjExNEMzLjgzNCAxLjg3IDYuNDMgNy4wODcgOS4yODIgNi42NDZjMi44NTQtLjQ0Ny4wMzggMi40MS0xLjgyMyAxLjkxN3oiIGZpbGw9IiM1NzVFNzUiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==';\n\n/**\n * Class for the music-related blocks in Scratch 3.0\n * @param {Runtime} runtime - the runtime instantiating this block package.\n * @constructor\n */\nclass Scratch3MusicBlocks {\n constructor (runtime) {\n /**\n * The runtime instantiating this block package.\n * @type {Runtime}\n */\n this.runtime = runtime;\n\n /**\n * The number of drum and instrument sounds currently being played simultaneously.\n * @type {number}\n * @private\n */\n this._concurrencyCounter = 0;\n\n /**\n * An array of sound players, one for each drum sound.\n * @type {Array}\n * @private\n */\n this._drumPlayers = [];\n\n /**\n * An array of arrays of sound players. Each instrument has one or more audio players.\n * @type {Array[]}\n * @private\n */\n this._instrumentPlayerArrays = [];\n\n /**\n * An array of arrays of sound players. Each instrument mya have an audio player for each playable note.\n * @type {Array[]}\n * @private\n */\n this._instrumentPlayerNoteArrays = [];\n\n /**\n * An array of audio bufferSourceNodes. Each time you play an instrument or drum sound,\n * a bufferSourceNode is created. We keep references to them to make sure their onended\n * events can fire.\n * @type {Array}\n * @private\n */\n this._bufferSources = [];\n\n this._loadAllSounds();\n\n this._onTargetCreated = this._onTargetCreated.bind(this);\n this.runtime.on('targetWasCreated', this._onTargetCreated);\n\n this._playNoteForPicker = this._playNoteForPicker.bind(this);\n this.runtime.on('PLAY_NOTE', this._playNoteForPicker);\n }\n\n /**\n * Decode the full set of drum and instrument sounds, and store the audio buffers in arrays.\n */\n _loadAllSounds () {\n const loadingPromises = [];\n this.DRUM_INFO.forEach((drumInfo, index) => {\n const filePath = `drums/${drumInfo.fileName}`;\n const promise = this._storeSound(filePath, index, this._drumPlayers);\n loadingPromises.push(promise);\n });\n this.INSTRUMENT_INFO.forEach((instrumentInfo, instrumentIndex) => {\n this._instrumentPlayerArrays[instrumentIndex] = [];\n this._instrumentPlayerNoteArrays[instrumentIndex] = [];\n instrumentInfo.samples.forEach((sample, noteIndex) => {\n const filePath = `instruments/${instrumentInfo.dirName}/${sample}`;\n const promise = this._storeSound(filePath, noteIndex, this._instrumentPlayerArrays[instrumentIndex]);\n loadingPromises.push(promise);\n });\n });\n Promise.all(loadingPromises).then(() => {\n // @TODO: Update the extension status indicator.\n });\n }\n\n /**\n * Decode a sound and store the player in an array.\n * @param {string} filePath - the audio file name.\n * @param {number} index - the index at which to store the audio player.\n * @param {array} playerArray - the array of players in which to store it.\n * @return {Promise} - a promise which will resolve once the sound has been stored.\n */\n _storeSound (filePath, index, playerArray) {\n const fullPath = `${filePath}.mp3`;\n\n if (!assetData[fullPath]) return;\n\n // The sound player has already been downloaded via the manifest file required above.\n const soundBuffer = assetData[fullPath];\n\n return this._decodeSound(soundBuffer).then(player => {\n playerArray[index] = player;\n });\n }\n\n /**\n * Decode a sound and return a promise with the audio buffer.\n * @param {ArrayBuffer} soundBuffer - a buffer containing the encoded audio.\n * @return {Promise} - a promise which will resolve once the sound has decoded.\n */\n _decodeSound (soundBuffer) {\n const engine = this.runtime.audioEngine;\n\n if (!engine) {\n return Promise.reject(new Error('No Audio Context Detected'));\n }\n\n // Check for newer promise-based API\n return engine.decodeSoundPlayer({data: {buffer: soundBuffer}});\n }\n\n /**\n * Create data for a menu in scratch-blocks format, consisting of an array of objects with text and\n * value properties. The text is a translated string, and the value is one-indexed.\n * @param {object[]} info - An array of info objects each having a name property.\n * @return {array} - An array of objects with text and value properties.\n * @private\n */\n _buildMenu (info) {\n return info.map((entry, index) => {\n const obj = {};\n obj.text = entry.name;\n obj.value = String(index + 1);\n return obj;\n });\n }\n\n /**\n * An array of info about each drum.\n * @type {object[]}\n * @param {string} name - the translatable name to display in the drums menu.\n * @param {string} fileName - the name of the audio file containing the drum sound.\n */\n get DRUM_INFO () {\n return [\n {\n name: formatMessage({\n id: 'music.drumSnare',\n default: '(1) Snare Drum',\n description: 'Sound of snare drum as used in a standard drum kit'\n }),\n fileName: '1-snare'\n },\n {\n name: formatMessage({\n id: 'music.drumBass',\n default: '(2) Bass Drum',\n description: 'Sound of bass drum as used in a standard drum kit'\n }),\n fileName: '2-bass-drum'\n },\n {\n name: formatMessage({\n id: 'music.drumSideStick',\n default: '(3) Side Stick',\n description: 'Sound of a drum stick hitting the side of a drum (usually the snare)'\n }),\n fileName: '3-side-stick'\n },\n {\n name: formatMessage({\n id: 'music.drumCrashCymbal',\n default: '(4) Crash Cymbal',\n description: 'Sound of a drum stick hitting a crash cymbal'\n }),\n fileName: '4-crash-cymbal'\n },\n {\n name: formatMessage({\n id: 'music.drumOpenHiHat',\n default: '(5) Open Hi-Hat',\n description: 'Sound of a drum stick hitting a hi-hat while open'\n }),\n fileName: '5-open-hi-hat'\n },\n {\n name: formatMessage({\n id: 'music.drumClosedHiHat',\n default: '(6) Closed Hi-Hat',\n description: 'Sound of a drum stick hitting a hi-hat while closed'\n }),\n fileName: '6-closed-hi-hat'\n },\n {\n name: formatMessage({\n id: 'music.drumTambourine',\n default: '(7) Tambourine',\n description: 'Sound of a tambourine being struck'\n }),\n fileName: '7-tambourine'\n },\n {\n name: formatMessage({\n id: 'music.drumHandClap',\n default: '(8) Hand Clap',\n description: 'Sound of two hands clapping together'\n }),\n fileName: '8-hand-clap'\n },\n {\n name: formatMessage({\n id: 'music.drumClaves',\n default: '(9) Claves',\n description: 'Sound of claves being struck together'\n }),\n fileName: '9-claves'\n },\n {\n name: formatMessage({\n id: 'music.drumWoodBlock',\n default: '(10) Wood Block',\n description: 'Sound of a wood block being struck'\n }),\n fileName: '10-wood-block'\n },\n {\n name: formatMessage({\n id: 'music.drumCowbell',\n default: '(11) Cowbell',\n description: 'Sound of a cowbell being struck'\n }),\n fileName: '11-cowbell'\n },\n {\n name: formatMessage({\n id: 'music.drumTriangle',\n default: '(12) Triangle',\n description: 'Sound of a triangle (instrument) being struck'\n }),\n fileName: '12-triangle'\n },\n {\n name: formatMessage({\n id: 'music.drumBongo',\n default: '(13) Bongo',\n description: 'Sound of a bongo being struck'\n }),\n fileName: '13-bongo'\n },\n {\n name: formatMessage({\n id: 'music.drumConga',\n default: '(14) Conga',\n description: 'Sound of a conga being struck'\n }),\n fileName: '14-conga'\n },\n {\n name: formatMessage({\n id: 'music.drumCabasa',\n default: '(15) Cabasa',\n description: 'Sound of a cabasa being shaken'\n }),\n fileName: '15-cabasa'\n },\n {\n name: formatMessage({\n id: 'music.drumGuiro',\n default: '(16) Guiro',\n description: 'Sound of a guiro being played'\n }),\n fileName: '16-guiro'\n },\n {\n name: formatMessage({\n id: 'music.drumVibraslap',\n default: '(17) Vibraslap',\n description: 'Sound of a Vibraslap being played'\n }),\n fileName: '17-vibraslap'\n },\n {\n name: formatMessage({\n id: 'music.drumCuica',\n default: '(18) Cuica',\n description: 'Sound of a cuica being played'\n }),\n fileName: '18-cuica'\n }\n ];\n }\n\n /**\n * An array of info about each instrument.\n * @type {object[]}\n * @param {string} name - the translatable name to display in the instruments menu.\n * @param {string} dirName - the name of the directory containing audio samples for this instrument.\n * @param {number} [releaseTime] - an optional duration for the release portion of each note.\n * @param {number[]} samples - an array of numbers representing the MIDI note number for each\n * sampled sound used to play this instrument.\n */\n get INSTRUMENT_INFO () {\n return [\n {\n name: formatMessage({\n id: 'music.instrumentPiano',\n default: '(1) Piano',\n description: 'Sound of a piano'\n }),\n dirName: '1-piano',\n releaseTime: 0.5,\n samples: [24, 36, 48, 60, 72, 84, 96, 108]\n },\n {\n name: formatMessage({\n id: 'music.instrumentElectricPiano',\n default: '(2) Electric Piano',\n description: 'Sound of an electric piano'\n }),\n dirName: '2-electric-piano',\n releaseTime: 0.5,\n samples: [60]\n },\n {\n name: formatMessage({\n id: 'music.instrumentOrgan',\n default: '(3) Organ',\n description: 'Sound of an organ'\n }),\n dirName: '3-organ',\n releaseTime: 0.5,\n samples: [60]\n },\n {\n name: formatMessage({\n id: 'music.instrumentGuitar',\n default: '(4) Guitar',\n description: 'Sound of an accoustic guitar'\n }),\n dirName: '4-guitar',\n releaseTime: 0.5,\n samples: [60]\n },\n {\n name: formatMessage({\n id: 'music.instrumentElectricGuitar',\n default: '(5) Electric Guitar',\n description: 'Sound of an electric guitar'\n }),\n dirName: '5-electric-guitar',\n releaseTime: 0.5,\n samples: [60]\n },\n {\n name: formatMessage({\n id: 'music.instrumentBass',\n default: '(6) Bass',\n description: 'Sound of an accoustic upright bass'\n }),\n dirName: '6-bass',\n releaseTime: 0.25,\n samples: [36, 48]\n },\n {\n name: formatMessage({\n id: 'music.instrumentPizzicato',\n default: '(7) Pizzicato',\n description: 'Sound of a string instrument (e.g. violin) being plucked'\n }),\n dirName: '7-pizzicato',\n releaseTime: 0.25,\n samples: [60]\n },\n {\n name: formatMessage({\n id: 'music.instrumentCello',\n default: '(8) Cello',\n description: 'Sound of a cello being played with a bow'\n }),\n dirName: '8-cello',\n releaseTime: 0.1,\n samples: [36, 48, 60]\n },\n {\n name: formatMessage({\n id: 'music.instrumentTrombone',\n default: '(9) Trombone',\n description: 'Sound of a trombone being played'\n }),\n dirName: '9-trombone',\n samples: [36, 48, 60]\n },\n {\n name: formatMessage({\n id: 'music.instrumentClarinet',\n default: '(10) Clarinet',\n description: 'Sound of a clarinet being played'\n }),\n dirName: '10-clarinet',\n samples: [48, 60]\n },\n {\n name: formatMessage({\n id: 'music.instrumentSaxophone',\n default: '(11) Saxophone',\n description: 'Sound of a saxophone being played'\n }),\n dirName: '11-saxophone',\n samples: [36, 60, 84]\n },\n {\n name: formatMessage({\n id: 'music.instrumentFlute',\n default: '(12) Flute',\n description: 'Sound of a flute being played'\n }),\n dirName: '12-flute',\n samples: [60, 72]\n },\n {\n name: formatMessage({\n id: 'music.instrumentWoodenFlute',\n default: '(13) Wooden Flute',\n description: 'Sound of a wooden flute being played'\n }),\n dirName: '13-wooden-flute',\n samples: [60, 72]\n },\n {\n name: formatMessage({\n id: 'music.instrumentBassoon',\n default: '(14) Bassoon',\n description: 'Sound of a bassoon being played'\n }),\n dirName: '14-bassoon',\n samples: [36, 48, 60]\n },\n {\n name: formatMessage({\n id: 'music.instrumentChoir',\n default: '(15) Choir',\n description: 'Sound of a choir singing'\n }),\n dirName: '15-choir',\n releaseTime: 0.25,\n samples: [48, 60, 72]\n },\n {\n name: formatMessage({\n id: 'music.instrumentVibraphone',\n default: '(16) Vibraphone',\n description: 'Sound of a vibraphone being struck'\n }),\n dirName: '16-vibraphone',\n releaseTime: 0.5,\n samples: [60, 72]\n },\n {\n name: formatMessage({\n id: 'music.instrumentMusicBox',\n default: '(17) Music Box',\n description: 'Sound of a music box playing'\n }),\n dirName: '17-music-box',\n releaseTime: 0.25,\n samples: [60]\n },\n {\n name: formatMessage({\n id: 'music.instrumentSteelDrum',\n default: '(18) Steel Drum',\n description: 'Sound of a steel drum being struck'\n }),\n dirName: '18-steel-drum',\n releaseTime: 0.5,\n samples: [60]\n },\n {\n name: formatMessage({\n id: 'music.instrumentMarimba',\n default: '(19) Marimba',\n description: 'Sound of a marimba being struck'\n }),\n dirName: '19-marimba',\n samples: [60]\n },\n {\n name: formatMessage({\n id: 'music.instrumentSynthLead',\n default: '(20) Synth Lead',\n description: 'Sound of a \"lead\" synthesizer being played'\n }),\n dirName: '20-synth-lead',\n releaseTime: 0.1,\n samples: [60]\n },\n {\n name: formatMessage({\n id: 'music.instrumentSynthPad',\n default: '(21) Synth Pad',\n description: 'Sound of a \"pad\" synthesizer being played'\n }),\n dirName: '21-synth-pad',\n releaseTime: 0.25,\n samples: [60]\n }\n ];\n }\n\n /**\n * An array that is a mapping from MIDI instrument numbers to Scratch instrument numbers.\n * @type {number[]}\n */\n get MIDI_INSTRUMENTS () {\n return [\n // Acoustic Grand, Bright Acoustic, Electric Grand, Honky-Tonk\n 1, 1, 1, 1,\n // Electric Piano 1, Electric Piano 2, Harpsichord, Clavinet\n 2, 2, 4, 4,\n // Celesta, Glockenspiel, Music Box, Vibraphone\n 17, 17, 17, 16,\n // Marimba, Xylophone, Tubular Bells, Dulcimer\n 19, 16, 17, 17,\n // Drawbar Organ, Percussive Organ, Rock Organ, Church Organ\n 3, 3, 3, 3,\n // Reed Organ, Accordion, Harmonica, Tango Accordion\n 3, 3, 3, 3,\n // Nylon String Guitar, Steel String Guitar, Electric Jazz Guitar, Electric Clean Guitar\n 4, 4, 5, 5,\n // Electric Muted Guitar, Overdriven Guitar,Distortion Guitar, Guitar Harmonics\n 5, 5, 5, 5,\n // Acoustic Bass, Electric Bass (finger), Electric Bass (pick), Fretless Bass\n 6, 6, 6, 6,\n // Slap Bass 1, Slap Bass 2, Synth Bass 1, Synth Bass 2\n 6, 6, 6, 6,\n // Violin, Viola, Cello, Contrabass\n 8, 8, 8, 8,\n // Tremolo Strings, Pizzicato Strings, Orchestral Strings, Timpani\n 8, 7, 8, 19,\n // String Ensemble 1, String Ensemble 2, SynthStrings 1, SynthStrings 2\n 8, 8, 8, 8,\n // Choir Aahs, Voice Oohs, Synth Voice, Orchestra Hit\n 15, 15, 15, 19,\n // Trumpet, Trombone, Tuba, Muted Trumpet\n 9, 9, 9, 9,\n // French Horn, Brass Section, SynthBrass 1, SynthBrass 2\n 9, 9, 9, 9,\n // Soprano Sax, Alto Sax, Tenor Sax, Baritone Sax\n 11, 11, 11, 11,\n // Oboe, English Horn, Bassoon, Clarinet\n 14, 14, 14, 10,\n // Piccolo, Flute, Recorder, Pan Flute\n 12, 12, 13, 13,\n // Blown Bottle, Shakuhachi, Whistle, Ocarina\n 13, 13, 12, 12,\n // Lead 1 (square), Lead 2 (sawtooth), Lead 3 (calliope), Lead 4 (chiff)\n 20, 20, 20, 20,\n // Lead 5 (charang), Lead 6 (voice), Lead 7 (fifths), Lead 8 (bass+lead)\n 20, 20, 20, 20,\n // Pad 1 (new age), Pad 2 (warm), Pad 3 (polysynth), Pad 4 (choir)\n 21, 21, 21, 21,\n // Pad 5 (bowed), Pad 6 (metallic), Pad 7 (halo), Pad 8 (sweep)\n 21, 21, 21, 21,\n // FX 1 (rain), FX 2 (soundtrack), FX 3 (crystal), FX 4 (atmosphere)\n 21, 21, 21, 21,\n // FX 5 (brightness), FX 6 (goblins), FX 7 (echoes), FX 8 (sci-fi)\n 21, 21, 21, 21,\n // Sitar, Banjo, Shamisen, Koto\n 4, 4, 4, 4,\n // Kalimba, Bagpipe, Fiddle, Shanai\n 17, 14, 8, 10,\n // Tinkle Bell, Agogo, Steel Drums, Woodblock\n 17, 17, 18, 19,\n // Taiko Drum, Melodic Tom, Synth Drum, Reverse Cymbal\n 1, 1, 1, 1,\n // Guitar Fret Noise, Breath Noise, Seashore, Bird Tweet\n 21, 21, 21, 21,\n // Telephone Ring, Helicopter, Applause, Gunshot\n 21, 21, 21, 21\n ];\n }\n\n /**\n * An array that is a mapping from MIDI drum numbers in range (35..81) to Scratch drum numbers.\n * It's in the format [drumNum, pitch, decay].\n * The pitch and decay properties are not currently being used.\n * @type {Array[]}\n */\n get MIDI_DRUMS () {\n return [\n [1, -4], // \"BassDrum\" in 2.0, \"Bass Drum\" in 3.0 (which was \"Tom\" in 2.0)\n [1, 0], // Same as just above\n [2, 0],\n [0, 0],\n [7, 0],\n [0, 2],\n [1, -6, 4],\n [5, 0],\n [1, -3, 3.2],\n [5, 0], // \"HiHatPedal\" in 2.0, \"Closed Hi-Hat\" in 3.0\n [1, 0, 3],\n [4, -8],\n [1, 4, 3],\n [1, 7, 2.7],\n [3, -8],\n [1, 10, 2.7],\n [4, -2],\n [3, -11],\n [4, 2],\n [6, 0],\n [3, 0, 3.5],\n [10, 0],\n [3, -8, 3.5],\n [16, -6],\n [4, 2],\n [12, 2],\n [12, 0],\n [13, 0, 0.2],\n [13, 0, 2],\n [13, -5, 2],\n [12, 12],\n [12, 5],\n [10, 19],\n [10, 12],\n [14, 0],\n [14, 0], // \"Maracas\" in 2.0, \"Cabasa\" in 3.0 (TODO: pitch up?)\n [17, 12],\n [17, 5],\n [15, 0], // \"GuiroShort\" in 2.0, \"Guiro\" in 3.0 (which was \"GuiroLong\" in 2.0) (TODO: decay?)\n [15, 0],\n [8, 0],\n [9, 0],\n [9, -4],\n [17, -5],\n [17, 0],\n [11, -6, 1],\n [11, -6, 3]\n ];\n }\n\n /**\n * The key to load & store a target's music-related state.\n * @type {string}\n */\n static get STATE_KEY () {\n return 'Scratch.music';\n }\n\n /**\n * The default music-related state, to be used when a target has no existing music state.\n * @type {MusicState}\n */\n static get DEFAULT_MUSIC_STATE () {\n return {\n currentInstrument: 0\n };\n }\n\n /**\n * The minimum and maximum MIDI note numbers, for clamping the input to play note.\n * @type {{min: number, max: number}}\n */\n static get MIDI_NOTE_RANGE () {\n return {min: 0, max: 130};\n }\n\n /**\n * The minimum and maximum beat values, for clamping the duration of play note, play drum and rest.\n * 100 beats at the default tempo of 60bpm is 100 seconds.\n * @type {{min: number, max: number}}\n */\n static get BEAT_RANGE () {\n return {min: 0, max: 100};\n }\n\n /** The minimum and maximum tempo values, in bpm.\n * @type {{min: number, max: number}}\n */\n static get TEMPO_RANGE () {\n return {min: 20, max: 500};\n }\n\n /**\n * The maximum number of sounds to allow to play simultaneously.\n * @type {number}\n */\n static get CONCURRENCY_LIMIT () {\n return 30;\n }\n\n /**\n * @param {Target} target - collect music state for this target.\n * @returns {MusicState} the mutable music state associated with that target. This will be created if necessary.\n * @private\n */\n _getMusicState (target) {\n let musicState = target.getCustomState(Scratch3MusicBlocks.STATE_KEY);\n if (!musicState) {\n musicState = Clone.simple(Scratch3MusicBlocks.DEFAULT_MUSIC_STATE);\n target.setCustomState(Scratch3MusicBlocks.STATE_KEY, musicState);\n }\n return musicState;\n }\n\n /**\n * When a music-playing Target is cloned, clone the music state.\n * @param {Target} newTarget - the newly created target.\n * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any.\n * @listens Runtime#event:targetWasCreated\n * @private\n */\n _onTargetCreated (newTarget, sourceTarget) {\n if (sourceTarget) {\n const musicState = sourceTarget.getCustomState(Scratch3MusicBlocks.STATE_KEY);\n if (musicState) {\n newTarget.setCustomState(Scratch3MusicBlocks.STATE_KEY, Clone.simple(musicState));\n }\n }\n }\n\n /**\n * @returns {object} metadata for this extension and its blocks.\n */\n getInfo () {\n return {\n id: 'music',\n name: formatMessage({\n id: 'music.categoryName',\n default: 'Music',\n description: 'Label for the Music extension category'\n }),\n menuIconURI: menuIconURI,\n blockIconURI: blockIconURI,\n blocks: [\n {\n opcode: 'playDrumForBeats',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'music.playDrumForBeats',\n default: 'play drum [DRUM] for [BEATS] beats',\n description: 'play drum sample for a number of beats'\n }),\n arguments: {\n DRUM: {\n type: ArgumentType.NUMBER,\n menu: 'DRUM',\n defaultValue: 1\n },\n BEATS: {\n type: ArgumentType.NUMBER,\n defaultValue: 0.25\n }\n }\n },\n {\n opcode: 'midiPlayDrumForBeats',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'music.midiPlayDrumForBeats',\n default: 'play drum [DRUM] for [BEATS] beats',\n description: 'play drum sample for a number of beats according to a mapping of MIDI codes'\n }),\n arguments: {\n DRUM: {\n type: ArgumentType.NUMBER,\n menu: 'DRUM',\n defaultValue: 1\n },\n BEATS: {\n type: ArgumentType.NUMBER,\n defaultValue: 0.25\n }\n },\n hideFromPalette: true\n },\n {\n opcode: 'restForBeats',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'music.restForBeats',\n default: 'rest for [BEATS] beats',\n description: 'rest (play no sound) for a number of beats'\n }),\n arguments: {\n BEATS: {\n type: ArgumentType.NUMBER,\n defaultValue: 0.25\n }\n }\n },\n {\n opcode: 'playNoteForBeats',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'music.playNoteForBeats',\n default: 'play note [NOTE] for [BEATS] beats',\n description: 'play a note for a number of beats'\n }),\n arguments: {\n NOTE: {\n type: ArgumentType.NOTE,\n defaultValue: 60\n },\n BEATS: {\n type: ArgumentType.NUMBER,\n defaultValue: 0.25\n }\n }\n },\n {\n opcode: 'setInstrument',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'music.setInstrument',\n default: 'set instrument to [INSTRUMENT]',\n description: 'set the instrument (e.g. piano, guitar, trombone) for notes played'\n }),\n arguments: {\n INSTRUMENT: {\n type: ArgumentType.NUMBER,\n menu: 'INSTRUMENT',\n defaultValue: 1\n }\n }\n },\n {\n opcode: 'midiSetInstrument',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'music.midiSetInstrument',\n default: 'set instrument to [INSTRUMENT]',\n description: 'set the instrument for notes played according to a mapping of MIDI codes'\n }),\n arguments: {\n INSTRUMENT: {\n type: ArgumentType.NUMBER,\n defaultValue: 1\n }\n },\n hideFromPalette: true\n },\n {\n opcode: 'setTempo',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'music.setTempo',\n default: 'set tempo to [TEMPO]',\n description: 'set tempo (speed) for notes, drums, and rests played'\n }),\n arguments: {\n TEMPO: {\n type: ArgumentType.NUMBER,\n defaultValue: 60\n }\n }\n },\n {\n opcode: 'changeTempo',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'music.changeTempo',\n default: 'change tempo by [TEMPO]',\n description: 'change tempo (speed) for notes, drums, and rests played'\n }),\n arguments: {\n TEMPO: {\n type: ArgumentType.NUMBER,\n defaultValue: 20\n }\n }\n },\n {\n opcode: 'getTempo',\n text: formatMessage({\n id: 'music.getTempo',\n default: 'tempo',\n description: 'get the current tempo (speed) for notes, drums, and rests played'\n }),\n blockType: BlockType.REPORTER\n }\n ],\n menus: {\n DRUM: {\n acceptReporters: true,\n items: this._buildMenu(this.DRUM_INFO)\n },\n INSTRUMENT: {\n acceptReporters: true,\n items: this._buildMenu(this.INSTRUMENT_INFO)\n }\n }\n };\n }\n\n /**\n * Play a drum sound for some number of beats.\n * @param {object} args - the block arguments.\n * @param {object} util - utility object provided by the runtime.\n * @property {int} DRUM - the number of the drum to play.\n * @property {number} BEATS - the duration in beats of the drum sound.\n */\n playDrumForBeats (args, util) {\n this._playDrumForBeats(args.DRUM, args.BEATS, util);\n }\n\n /**\n * Play a drum sound for some number of beats according to the range of \"MIDI\" drum codes supported.\n * This block is implemented for compatibility with old Scratch projects that use the\n * 'drum:duration:elapsed:from:' block.\n * @param {object} args - the block arguments.\n * @param {object} util - utility object provided by the runtime.\n */\n midiPlayDrumForBeats (args, util) {\n let drumNum = Cast.toNumber(args.DRUM);\n drumNum = Math.round(drumNum);\n const midiDescription = this.MIDI_DRUMS[drumNum - 35];\n if (midiDescription) {\n drumNum = midiDescription[0];\n } else {\n drumNum = 2; // Default instrument used in Scratch 2.0\n }\n drumNum += 1; // drumNum input to _playDrumForBeats is one-indexed\n this._playDrumForBeats(drumNum, args.BEATS, util);\n }\n\n /**\n * Internal code to play a drum sound for some number of beats.\n * @param {number} drumNum - the drum number.\n * @param {beats} beats - the duration in beats to pause after playing the sound.\n * @param {object} util - utility object provided by the runtime.\n */\n _playDrumForBeats (drumNum, beats, util) {\n if (this._stackTimerNeedsInit(util)) {\n drumNum = Cast.toNumber(drumNum);\n drumNum = Math.round(drumNum);\n drumNum -= 1; // drums are one-indexed\n drumNum = MathUtil.wrapClamp(drumNum, 0, this.DRUM_INFO.length - 1);\n beats = Cast.toNumber(beats);\n beats = this._clampBeats(beats);\n this._playDrumNum(util, drumNum);\n this._startStackTimer(util, this._beatsToSec(beats));\n } else {\n this._checkStackTimer(util);\n }\n }\n\n /**\n * Play a drum sound using its 0-indexed number.\n * @param {object} util - utility object provided by the runtime.\n * @param {number} drumNum - the number of the drum to play.\n * @private\n */\n _playDrumNum (util, drumNum) {\n if (util.runtime.audioEngine === null) return;\n if (util.target.sprite.soundBank === null) return;\n // If we're playing too many sounds, do not play the drum sound.\n if (this._concurrencyCounter > Scratch3MusicBlocks.CONCURRENCY_LIMIT) {\n return;\n }\n\n const player = this._drumPlayers[drumNum];\n\n if (typeof player === 'undefined') return;\n\n if (player.isPlaying && !player.isStarting) {\n // Take the internal player state and create a new player with it.\n // `.play` does this internally but then instructs the sound to\n // stop.\n player.take();\n }\n\n const engine = util.runtime.audioEngine;\n const context = engine.audioContext;\n const volumeGain = context.createGain();\n volumeGain.gain.setValueAtTime(util.target.volume / 100, engine.currentTime);\n volumeGain.connect(engine.getInputNode());\n\n this._concurrencyCounter++;\n player.once('stop', () => {\n this._concurrencyCounter--;\n });\n\n player.play();\n // Connect the player to the gain node.\n player.connect({getInputNode () {\n return volumeGain;\n }});\n }\n\n /**\n * Rest for some number of beats.\n * @param {object} args - the block arguments.\n * @param {object} util - utility object provided by the runtime.\n * @property {number} BEATS - the duration in beats of the rest.\n */\n restForBeats (args, util) {\n if (this._stackTimerNeedsInit(util)) {\n let beats = Cast.toNumber(args.BEATS);\n beats = this._clampBeats(beats);\n this._startStackTimer(util, this._beatsToSec(beats));\n } else {\n this._checkStackTimer(util);\n }\n }\n\n /**\n * Play a note using the current musical instrument for some number of beats.\n * This function processes the arguments, and handles the timing of the block's execution.\n * @param {object} args - the block arguments.\n * @param {object} util - utility object provided by the runtime.\n * @property {number} NOTE - the pitch of the note to play, interpreted as a MIDI note number.\n * @property {number} BEATS - the duration in beats of the note.\n */\n playNoteForBeats (args, util) {\n if (this._stackTimerNeedsInit(util)) {\n let note = Cast.toNumber(args.NOTE);\n note = MathUtil.clamp(note,\n Scratch3MusicBlocks.MIDI_NOTE_RANGE.min, Scratch3MusicBlocks.MIDI_NOTE_RANGE.max);\n let beats = Cast.toNumber(args.BEATS);\n beats = this._clampBeats(beats);\n // If the duration is 0, do not play the note. In Scratch 2.0, \"play drum for 0 beats\" plays the drum,\n // but \"play note for 0 beats\" is silent.\n if (beats === 0) return;\n\n const durationSec = this._beatsToSec(beats);\n\n this._playNote(util, note, durationSec);\n\n this._startStackTimer(util, durationSec);\n } else {\n this._checkStackTimer(util);\n }\n }\n\n _playNoteForPicker (noteNum, category) {\n if (category !== this.getInfo().name) return;\n const util = {\n runtime: this.runtime,\n target: this.runtime.getEditingTarget()\n };\n this._playNote(util, noteNum, 0.25);\n }\n\n /**\n * Play a note using the current instrument for a duration in seconds.\n * This function actually plays the sound, and handles the timing of the sound, including the\n * \"release\" portion of the sound, which continues briefly after the block execution has finished.\n * @param {object} util - utility object provided by the runtime.\n * @param {number} note - the pitch of the note to play, interpreted as a MIDI note number.\n * @param {number} durationSec - the duration in seconds to play the note.\n * @private\n */\n _playNote (util, note, durationSec) {\n if (util.runtime.audioEngine === null) return;\n if (util.target.sprite.soundBank === null) return;\n\n // If we're playing too many sounds, do not play the note.\n if (this._concurrencyCounter > Scratch3MusicBlocks.CONCURRENCY_LIMIT) {\n return;\n }\n\n // Determine which of the audio samples for this instrument to play\n const musicState = this._getMusicState(util.target);\n const inst = musicState.currentInstrument;\n const instrumentInfo = this.INSTRUMENT_INFO[inst];\n const sampleArray = instrumentInfo.samples;\n const sampleIndex = this._selectSampleIndexForNote(note, sampleArray);\n\n // If the audio sample has not loaded yet, bail out\n if (typeof this._instrumentPlayerArrays[inst] === 'undefined') return;\n if (typeof this._instrumentPlayerArrays[inst][sampleIndex] === 'undefined') return;\n\n // Fetch the sound player to play the note.\n const engine = util.runtime.audioEngine;\n\n if (!this._instrumentPlayerNoteArrays[inst][note]) {\n this._instrumentPlayerNoteArrays[inst][note] = this._instrumentPlayerArrays[inst][sampleIndex].take();\n }\n\n const player = this._instrumentPlayerNoteArrays[inst][note];\n\n if (player.isPlaying && !player.isStarting) {\n // Take the internal player state and create a new player with it.\n // `.play` does this internally but then instructs the sound to\n // stop.\n player.take();\n }\n\n // Set its pitch.\n const sampleNote = sampleArray[sampleIndex];\n const notePitchInterval = this._ratioForPitchInterval(note - sampleNote);\n\n // Create gain nodes for this note's volume and release, and chain them\n // to the output.\n const context = engine.audioContext;\n const volumeGain = context.createGain();\n volumeGain.gain.setValueAtTime(util.target.volume / 100, engine.currentTime);\n const releaseGain = context.createGain();\n volumeGain.connect(releaseGain);\n releaseGain.connect(engine.getInputNode());\n\n // Schedule the release of the note, ramping its gain down to zero,\n // and then stopping the sound.\n let releaseDuration = this.INSTRUMENT_INFO[inst].releaseTime;\n if (typeof releaseDuration === 'undefined') {\n releaseDuration = 0.01;\n }\n const releaseStart = context.currentTime + durationSec;\n const releaseEnd = releaseStart + releaseDuration;\n releaseGain.gain.setValueAtTime(1, releaseStart);\n releaseGain.gain.linearRampToValueAtTime(0.0001, releaseEnd);\n\n this._concurrencyCounter++;\n player.once('stop', () => {\n this._concurrencyCounter--;\n });\n\n // Start playing the note\n player.play();\n // Connect the player to the gain node.\n player.connect({getInputNode () {\n return volumeGain;\n }});\n // Set playback now after play creates the outputNode.\n player.outputNode.playbackRate.value = notePitchInterval;\n // Schedule playback to stop.\n player.outputNode.stop(releaseEnd);\n }\n\n /**\n * The samples array for each instrument is the set of pitches of the available audio samples.\n * This function selects the best one to use to play a given input note, and returns its index\n * in the samples array.\n * @param {number} note - the input note to select a sample for.\n * @param {number[]} samples - an array of the pitches of the available samples.\n * @return {index} the index of the selected sample in the samples array.\n * @private\n */\n _selectSampleIndexForNote (note, samples) {\n // Step backwards through the array of samples, i.e. in descending pitch, in order to find\n // the sample that is the closest one below (or matching) the pitch of the input note.\n for (let i = samples.length - 1; i >= 0; i--) {\n if (note >= samples[i]) {\n return i;\n }\n }\n return 0;\n }\n\n /**\n * Calcuate the frequency ratio for a given musical interval.\n * @param {number} interval - the pitch interval to convert.\n * @return {number} a ratio corresponding to the input interval.\n * @private\n */\n _ratioForPitchInterval (interval) {\n return Math.pow(2, (interval / 12));\n }\n\n /**\n * Clamp a duration in beats to the allowed min and max duration.\n * @param {number} beats - a duration in beats.\n * @return {number} - the clamped duration.\n * @private\n */\n _clampBeats (beats) {\n return MathUtil.clamp(beats, Scratch3MusicBlocks.BEAT_RANGE.min, Scratch3MusicBlocks.BEAT_RANGE.max);\n }\n\n /**\n * Convert a number of beats to a number of seconds, using the current tempo.\n * @param {number} beats - number of beats to convert to secs.\n * @return {number} seconds - number of seconds `beats` will last.\n * @private\n */\n _beatsToSec (beats) {\n return (60 / this.getTempo()) * beats;\n }\n\n /**\n * Check if the stack timer needs initialization.\n * @param {object} util - utility object provided by the runtime.\n * @return {boolean} - true if the stack timer needs to be initialized.\n * @private\n */\n _stackTimerNeedsInit (util) {\n return !util.stackFrame.timer;\n }\n\n /**\n * Start the stack timer and the yield the thread if necessary.\n * @param {object} util - utility object provided by the runtime.\n * @param {number} duration - a duration in seconds to set the timer for.\n * @private\n */\n _startStackTimer (util, duration) {\n util.stackFrame.timer = new Timer();\n util.stackFrame.timer.start();\n util.stackFrame.duration = duration;\n util.yield();\n }\n\n /**\n * Check the stack timer, and if its time is not up yet, yield the thread.\n * @param {object} util - utility object provided by the runtime.\n * @private\n */\n _checkStackTimer (util) {\n const timeElapsed = util.stackFrame.timer.timeElapsed();\n if (timeElapsed < util.stackFrame.duration * 1000) {\n util.yield();\n }\n }\n\n /**\n * Select an instrument for playing notes.\n * @param {object} args - the block arguments.\n * @param {object} util - utility object provided by the runtime.\n * @property {int} INSTRUMENT - the number of the instrument to select.\n */\n setInstrument (args, util) {\n this._setInstrument(args.INSTRUMENT, util, false);\n }\n\n /**\n * Select an instrument for playing notes according to a mapping of MIDI codes to Scratch instrument numbers.\n * This block is implemented for compatibility with old Scratch projects that use the 'midiInstrument:' block.\n * @param {object} args - the block arguments.\n * @param {object} util - utility object provided by the runtime.\n * @property {int} INSTRUMENT - the MIDI number of the instrument to select.\n */\n midiSetInstrument (args, util) {\n this._setInstrument(args.INSTRUMENT, util, true);\n }\n\n /**\n * Internal code to select an instrument for playing notes. If mapMidi is true, set the instrument according to\n * the MIDI to Scratch instrument mapping.\n * @param {number} instNum - the instrument number.\n * @param {object} util - utility object provided by the runtime.\n * @param {boolean} mapMidi - whether or not instNum is a MIDI instrument number.\n */\n _setInstrument (instNum, util, mapMidi) {\n const musicState = this._getMusicState(util.target);\n instNum = Cast.toNumber(instNum);\n instNum = Math.round(instNum);\n instNum -= 1; // instruments are one-indexed\n if (mapMidi) {\n instNum = (this.MIDI_INSTRUMENTS[instNum] || 0) - 1;\n }\n instNum = MathUtil.wrapClamp(instNum, 0, this.INSTRUMENT_INFO.length - 1);\n musicState.currentInstrument = instNum;\n }\n\n /**\n * Set the current tempo to a new value.\n * @param {object} args - the block arguments.\n * @property {number} TEMPO - the tempo, in beats per minute.\n */\n setTempo (args) {\n const tempo = Cast.toNumber(args.TEMPO);\n this._updateTempo(tempo);\n }\n\n /**\n * Change the current tempo by some amount.\n * @param {object} args - the block arguments.\n * @property {number} TEMPO - the amount to change the tempo, in beats per minute.\n */\n changeTempo (args) {\n const change = Cast.toNumber(args.TEMPO);\n const tempo = change + this.getTempo();\n this._updateTempo(tempo);\n }\n\n /**\n * Update the current tempo, clamping it to the min and max allowable range.\n * @param {number} tempo - the tempo to set, in beats per minute.\n * @private\n */\n _updateTempo (tempo) {\n tempo = MathUtil.clamp(tempo, Scratch3MusicBlocks.TEMPO_RANGE.min, Scratch3MusicBlocks.TEMPO_RANGE.max);\n const stage = this.runtime.getTargetForStage();\n if (stage) {\n stage.tempo = tempo;\n }\n }\n\n /**\n * Get the current tempo.\n * @return {number} - the current tempo, in beats per minute.\n */\n getTempo () {\n const stage = this.runtime.getTargetForStage();\n if (stage) {\n return stage.tempo;\n }\n return 60;\n }\n}\n\nmodule.exports = Scratch3MusicBlocks;\n","module.exports = {\n 'drums/1-snare.mp3': require('!arraybuffer-loader!./assets/drums/1-snare.mp3'),\n 'drums/2-bass-drum.mp3': require('!arraybuffer-loader!./assets/drums/2-bass-drum.mp3'),\n 'drums/3-side-stick.mp3': require('!arraybuffer-loader!./assets/drums/3-side-stick.mp3'),\n 'drums/4-crash-cymbal.mp3': require('!arraybuffer-loader!./assets/drums/4-crash-cymbal.mp3'),\n 'drums/5-open-hi-hat.mp3': require('!arraybuffer-loader!./assets/drums/5-open-hi-hat.mp3'),\n 'drums/6-closed-hi-hat.mp3': require('!arraybuffer-loader!./assets/drums/6-closed-hi-hat.mp3'),\n 'drums/7-tambourine.mp3': require('!arraybuffer-loader!./assets/drums/7-tambourine.mp3'),\n 'drums/8-hand-clap.mp3': require('!arraybuffer-loader!./assets/drums/8-hand-clap.mp3'),\n 'drums/9-claves.mp3': require('!arraybuffer-loader!./assets/drums/9-claves.mp3'),\n 'drums/10-wood-block.mp3': require('!arraybuffer-loader!./assets/drums/10-wood-block.mp3'),\n 'drums/11-cowbell.mp3': require('!arraybuffer-loader!./assets/drums/11-cowbell.mp3'),\n 'drums/12-triangle.mp3': require('!arraybuffer-loader!./assets/drums/12-triangle.mp3'),\n 'drums/13-bongo.mp3': require('!arraybuffer-loader!./assets/drums/13-bongo.mp3'),\n 'drums/14-conga.mp3': require('!arraybuffer-loader!./assets/drums/14-conga.mp3'),\n 'drums/15-cabasa.mp3': require('!arraybuffer-loader!./assets/drums/15-cabasa.mp3'),\n 'drums/16-guiro.mp3': require('!arraybuffer-loader!./assets/drums/16-guiro.mp3'),\n 'drums/17-vibraslap.mp3': require('!arraybuffer-loader!./assets/drums/17-vibraslap.mp3'),\n 'drums/18-cuica.mp3': require('!arraybuffer-loader!./assets/drums/18-cuica.mp3'),\n 'instruments/1-piano/24.mp3': require('!arraybuffer-loader!./assets/instruments/1-piano/24.mp3'),\n 'instruments/1-piano/36.mp3': require('!arraybuffer-loader!./assets/instruments/1-piano/36.mp3'),\n 'instruments/1-piano/48.mp3': require('!arraybuffer-loader!./assets/instruments/1-piano/48.mp3'),\n 'instruments/1-piano/60.mp3': require('!arraybuffer-loader!./assets/instruments/1-piano/60.mp3'),\n 'instruments/1-piano/72.mp3': require('!arraybuffer-loader!./assets/instruments/1-piano/72.mp3'),\n 'instruments/1-piano/84.mp3': require('!arraybuffer-loader!./assets/instruments/1-piano/84.mp3'),\n 'instruments/1-piano/96.mp3': require('!arraybuffer-loader!./assets/instruments/1-piano/96.mp3'),\n 'instruments/1-piano/108.mp3': require('!arraybuffer-loader!./assets/instruments/1-piano/108.mp3'),\n 'instruments/2-electric-piano/60.mp3': require('!arraybuffer-loader!./assets/instruments/2-electric-piano/60.mp3'),\n 'instruments/3-organ/60.mp3': require('!arraybuffer-loader!./assets/instruments/3-organ/60.mp3'),\n 'instruments/4-guitar/60.mp3': require('!arraybuffer-loader!./assets/instruments/4-guitar/60.mp3'),\n 'instruments/5-electric-guitar/60.mp3': require(\n '!arraybuffer-loader!./assets/instruments/5-electric-guitar/60.mp3'\n ),\n 'instruments/6-bass/36.mp3': require('!arraybuffer-loader!./assets/instruments/6-bass/36.mp3'),\n 'instruments/6-bass/48.mp3': require('!arraybuffer-loader!./assets/instruments/6-bass/48.mp3'),\n 'instruments/7-pizzicato/60.mp3': require('!arraybuffer-loader!./assets/instruments/7-pizzicato/60.mp3'),\n 'instruments/8-cello/36.mp3': require('!arraybuffer-loader!./assets/instruments/8-cello/36.mp3'),\n 'instruments/8-cello/48.mp3': require('!arraybuffer-loader!./assets/instruments/8-cello/48.mp3'),\n 'instruments/8-cello/60.mp3': require('!arraybuffer-loader!./assets/instruments/8-cello/60.mp3'),\n 'instruments/9-trombone/36.mp3': require('!arraybuffer-loader!./assets/instruments/9-trombone/36.mp3'),\n 'instruments/9-trombone/48.mp3': require('!arraybuffer-loader!./assets/instruments/9-trombone/48.mp3'),\n 'instruments/9-trombone/60.mp3': require('!arraybuffer-loader!./assets/instruments/9-trombone/60.mp3'),\n 'instruments/10-clarinet/48.mp3': require('!arraybuffer-loader!./assets/instruments/10-clarinet/48.mp3'),\n 'instruments/10-clarinet/60.mp3': require('!arraybuffer-loader!./assets/instruments/10-clarinet/60.mp3'),\n 'instruments/11-saxophone/36.mp3': require('!arraybuffer-loader!./assets/instruments/11-saxophone/36.mp3'),\n 'instruments/11-saxophone/60.mp3': require('!arraybuffer-loader!./assets/instruments/11-saxophone/60.mp3'),\n 'instruments/11-saxophone/84.mp3': require('!arraybuffer-loader!./assets/instruments/11-saxophone/84.mp3'),\n 'instruments/12-flute/60.mp3': require('!arraybuffer-loader!./assets/instruments/12-flute/60.mp3'),\n 'instruments/12-flute/72.mp3': require('!arraybuffer-loader!./assets/instruments/12-flute/72.mp3'),\n 'instruments/13-wooden-flute/60.mp3': require('!arraybuffer-loader!./assets/instruments/13-wooden-flute/60.mp3'),\n 'instruments/13-wooden-flute/72.mp3': require('!arraybuffer-loader!./assets/instruments/13-wooden-flute/72.mp3'),\n 'instruments/14-bassoon/36.mp3': require('!arraybuffer-loader!./assets/instruments/14-bassoon/36.mp3'),\n 'instruments/14-bassoon/48.mp3': require('!arraybuffer-loader!./assets/instruments/14-bassoon/48.mp3'),\n 'instruments/14-bassoon/60.mp3': require('!arraybuffer-loader!./assets/instruments/14-bassoon/60.mp3'),\n 'instruments/15-choir/48.mp3': require('!arraybuffer-loader!./assets/instruments/15-choir/48.mp3'),\n 'instruments/15-choir/60.mp3': require('!arraybuffer-loader!./assets/instruments/15-choir/60.mp3'),\n 'instruments/15-choir/72.mp3': require('!arraybuffer-loader!./assets/instruments/15-choir/72.mp3'),\n 'instruments/16-vibraphone/60.mp3': require('!arraybuffer-loader!./assets/instruments/16-vibraphone/60.mp3'),\n 'instruments/16-vibraphone/72.mp3': require('!arraybuffer-loader!./assets/instruments/16-vibraphone/72.mp3'),\n 'instruments/17-music-box/60.mp3': require('!arraybuffer-loader!./assets/instruments/17-music-box/60.mp3'),\n 'instruments/18-steel-drum/60.mp3': require('!arraybuffer-loader!./assets/instruments/18-steel-drum/60.mp3'),\n 'instruments/19-marimba/60.mp3': require('!arraybuffer-loader!./assets/instruments/19-marimba/60.mp3'),\n 'instruments/20-synth-lead/60.mp3': require('!arraybuffer-loader!./assets/instruments/20-synth-lead/60.mp3'),\n 'instruments/21-synth-pad/60.mp3': require('!arraybuffer-loader!./assets/instruments/21-synth-pad/60.mp3')\n};\n","const ArgumentType = require('../../extension-support/argument-type');\nconst BlockType = require('../../extension-support/block-type');\nconst TargetType = require('../../extension-support/target-type');\nconst Cast = require('../../util/cast');\nconst Clone = require('../../util/clone');\nconst Color = require('../../util/color');\nconst formatMessage = require('format-message');\nconst MathUtil = require('../../util/math-util');\nconst RenderedTarget = require('../../sprites/rendered-target');\nconst log = require('../../util/log');\nconst StageLayering = require('../../engine/stage-layering');\n\n/**\n * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.\n * @type {string}\n */\n// eslint-disable-next-line max-len\nconst blockIconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+cGVuLWljb248L3RpdGxlPjxnIHN0cm9rZT0iIzU3NUU3NSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik04Ljc1MyAzNC42MDJsLTQuMjUgMS43OCAxLjc4My00LjIzN2MxLjIxOC0yLjg5MiAyLjkwNy01LjQyMyA1LjAzLTcuNTM4TDMxLjA2NiA0LjkzYy44NDYtLjg0MiAyLjY1LS40MSA0LjAzMi45NjcgMS4zOCAxLjM3NSAxLjgxNiAzLjE3My45NyA0LjAxNUwxNi4zMTggMjkuNTljLTIuMTIzIDIuMTE2LTQuNjY0IDMuOC03LjU2NSA1LjAxMiIgZmlsbD0iI0ZGRiIvPjxwYXRoIGQ9Ik0yOS40MSA2LjExcy00LjQ1LTIuMzc4LTguMjAyIDUuNzcyYy0xLjczNCAzLjc2Ni00LjM1IDEuNTQ2LTQuMzUgMS41NDYiLz48cGF0aCBkPSJNMzYuNDIgOC44MjVjMCAuNDYzLS4xNC44NzMtLjQzMiAxLjE2NGwtOS4zMzUgOS4zYy4yODItLjI5LjQxLS42NjguNDEtMS4xMiAwLS44NzQtLjUwNy0xLjk2My0xLjQwNi0yLjg2OC0xLjM2Mi0xLjM1OC0zLjE0Ny0xLjgtNC4wMDItLjk5TDMwLjk5IDUuMDFjLjg0NC0uODQgMi42NS0uNDEgNC4wMzUuOTYuODk4LjkwNCAxLjM5NiAxLjk4MiAxLjM5NiAyLjg1NU0xMC41MTUgMzMuNzc0Yy0uNTczLjMwMi0xLjE1Ny41Ny0xLjc2NC44M0w0LjUgMzYuMzgybDEuNzg2LTQuMjM1Yy4yNTgtLjYwNC41My0xLjE4Ni44MzMtMS43NTcuNjkuMTgzIDEuNDQ4LjYyNSAyLjEwOCAxLjI4Mi42Ni42NTggMS4xMDIgMS40MTIgMS4yODcgMi4xMDIiIGZpbGw9IiM0Qzk3RkYiLz48cGF0aCBkPSJNMzYuNDk4IDguNzQ4YzAgLjQ2NC0uMTQuODc0LS40MzMgMS4xNjVsLTE5Ljc0MiAxOS42OGMtMi4xMyAyLjExLTQuNjczIDMuNzkzLTcuNTcyIDUuMDFMNC41IDM2LjM4bC45NzQtMi4zMTYgMS45MjUtLjgwOGMyLjg5OC0xLjIxOCA1LjQ0LTIuOSA3LjU3LTUuMDFsMTkuNzQzLTE5LjY4Yy4yOTItLjI5Mi40MzItLjcwMi40MzItMS4xNjUgMC0uNjQ2LS4yNy0xLjQtLjc4LTIuMTIyLjI1LjE3Mi41LjM3Ny43MzcuNjE0Ljg5OC45MDUgMS4zOTYgMS45ODMgMS4zOTYgMi44NTYiIGZpbGw9IiM1NzVFNzUiIG9wYWNpdHk9Ii4xNSIvPjxwYXRoIGQ9Ik0xOC40NSAxMi44M2MwIC41LS40MDQuOTA1LS45MDQuOTA1cy0uOTA1LS40MDUtLjkwNS0uOTA0YzAtLjUuNDA3LS45MDMuOTA2LS45MDMuNSAwIC45MDQuNDA0LjkwNC45MDR6IiBmaWxsPSIjNTc1RTc1Ii8+PC9nPjwvc3ZnPg==';\n\n/**\n * Enum for pen color parameter values.\n * @readonly\n * @enum {string}\n */\nconst ColorParam = {\n COLOR: 'color',\n SATURATION: 'saturation',\n BRIGHTNESS: 'brightness',\n TRANSPARENCY: 'transparency'\n};\n\n/**\n * @typedef {object} PenState - the pen state associated with a particular target.\n * @property {Boolean} penDown - tracks whether the pen should draw for this target.\n * @property {number} color - the current color (hue) of the pen.\n * @property {PenAttributes} penAttributes - cached pen attributes for the renderer. This is the authoritative value for\n * diameter but not for pen color.\n */\n\n/**\n * Host for the Pen-related blocks in Scratch 3.0\n * @param {Runtime} runtime - the runtime instantiating this block package.\n * @constructor\n */\nclass Scratch3PenBlocks {\n constructor (runtime) {\n /**\n * The runtime instantiating this block package.\n * @type {Runtime}\n */\n this.runtime = runtime;\n\n /**\n * The ID of the renderer Drawable corresponding to the pen layer.\n * @type {int}\n * @private\n */\n this._penDrawableId = -1;\n\n /**\n * The ID of the renderer Skin corresponding to the pen layer.\n * @type {int}\n * @private\n */\n this._penSkinId = -1;\n\n this._onTargetCreated = this._onTargetCreated.bind(this);\n this._onTargetMoved = this._onTargetMoved.bind(this);\n\n runtime.on('targetWasCreated', this._onTargetCreated);\n runtime.on('RUNTIME_DISPOSED', this.clear.bind(this));\n }\n\n /**\n * The default pen state, to be used when a target has no existing pen state.\n * @type {PenState}\n */\n static get DEFAULT_PEN_STATE () {\n return {\n penDown: false,\n color: 66.66,\n saturation: 100,\n brightness: 100,\n transparency: 0,\n _shade: 50, // Used only for legacy `change shade by` blocks\n penAttributes: {\n color4f: [0, 0, 1, 1],\n diameter: 1\n }\n };\n }\n\n\n /**\n * The minimum and maximum allowed pen size.\n * The maximum is twice the diagonal of the stage, so that even an\n * off-stage sprite can fill it.\n * @type {{min: number, max: number}}\n */\n static get PEN_SIZE_RANGE () {\n return {min: 1, max: 1200};\n }\n\n /**\n * The key to load & store a target's pen-related state.\n * @type {string}\n */\n static get STATE_KEY () {\n return 'Scratch.pen';\n }\n\n /**\n * Clamp a pen size value to the range allowed by the pen.\n * @param {number} requestedSize - the requested pen size.\n * @returns {number} the clamped size.\n * @private\n */\n _clampPenSize (requestedSize) {\n return MathUtil.clamp(\n requestedSize,\n Scratch3PenBlocks.PEN_SIZE_RANGE.min,\n Scratch3PenBlocks.PEN_SIZE_RANGE.max\n );\n }\n\n /**\n * Retrieve the ID of the renderer \"Skin\" corresponding to the pen layer. If\n * the pen Skin doesn't yet exist, create it.\n * @returns {int} the Skin ID of the pen layer, or -1 on failure.\n * @private\n */\n _getPenLayerID () {\n if (this._penSkinId < 0 && this.runtime.renderer) {\n this._penSkinId = this.runtime.renderer.createPenSkin();\n this._penDrawableId = this.runtime.renderer.createDrawable(StageLayering.PEN_LAYER);\n this.runtime.renderer.updateDrawableSkinId(this._penDrawableId, this._penSkinId);\n }\n return this._penSkinId;\n }\n\n /**\n * @param {Target} target - collect pen state for this target. Probably, but not necessarily, a RenderedTarget.\n * @returns {PenState} the mutable pen state associated with that target. This will be created if necessary.\n * @private\n */\n _getPenState (target) {\n let penState = target.getCustomState(Scratch3PenBlocks.STATE_KEY);\n if (!penState) {\n penState = Clone.simple(Scratch3PenBlocks.DEFAULT_PEN_STATE);\n target.setCustomState(Scratch3PenBlocks.STATE_KEY, penState);\n }\n return penState;\n }\n\n /**\n * When a pen-using Target is cloned, clone the pen state.\n * @param {Target} newTarget - the newly created target.\n * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any.\n * @listens Runtime#event:targetWasCreated\n * @private\n */\n _onTargetCreated (newTarget, sourceTarget) {\n if (sourceTarget) {\n const penState = sourceTarget.getCustomState(Scratch3PenBlocks.STATE_KEY);\n if (penState) {\n newTarget.setCustomState(Scratch3PenBlocks.STATE_KEY, Clone.simple(penState));\n if (penState.penDown) {\n newTarget.addListener(RenderedTarget.EVENT_TARGET_MOVED, this._onTargetMoved);\n }\n }\n }\n }\n\n /**\n * Handle a target which has moved. This only fires when the pen is down.\n * @param {RenderedTarget} target - the target which has moved.\n * @param {number} oldX - the previous X position.\n * @param {number} oldY - the previous Y position.\n * @param {boolean} isForce - whether the movement was forced.\n * @private\n */\n _onTargetMoved (target, oldX, oldY, isForce) {\n // Only move the pen if the movement isn't forced (ie. dragged).\n if (!isForce) {\n const penSkinId = this._getPenLayerID();\n if (penSkinId >= 0) {\n const penState = this._getPenState(target);\n this.runtime.renderer.penLine(penSkinId, penState.penAttributes, oldX, oldY, target.x, target.y);\n this.runtime.requestRedraw();\n }\n }\n }\n\n /**\n * Wrap a color input into the range (0,100).\n * @param {number} value - the value to be wrapped.\n * @returns {number} the wrapped value.\n * @private\n */\n _wrapColor (value) {\n return MathUtil.wrapClamp(value, 0, 100);\n }\n\n /**\n * Initialize color parameters menu with localized strings\n * @returns {array} of the localized text and values for each menu element\n * @private\n */\n _initColorParam () {\n return [\n {\n text: formatMessage({\n id: 'pen.colorMenu.color',\n default: 'color',\n description: 'label for color element in color picker for pen extension'\n }),\n value: ColorParam.COLOR\n },\n {\n text: formatMessage({\n id: 'pen.colorMenu.saturation',\n default: 'saturation',\n description: 'label for saturation element in color picker for pen extension'\n }),\n value: ColorParam.SATURATION\n },\n {\n text: formatMessage({\n id: 'pen.colorMenu.brightness',\n default: 'brightness',\n description: 'label for brightness element in color picker for pen extension'\n }),\n value: ColorParam.BRIGHTNESS\n },\n {\n text: formatMessage({\n id: 'pen.colorMenu.transparency',\n default: 'transparency',\n description: 'label for transparency element in color picker for pen extension'\n }),\n value: ColorParam.TRANSPARENCY\n\n }\n ];\n }\n\n /**\n * Clamp a pen color parameter to the range (0,100).\n * @param {number} value - the value to be clamped.\n * @returns {number} the clamped value.\n * @private\n */\n _clampColorParam (value) {\n return MathUtil.clamp(value, 0, 100);\n }\n\n /**\n * Convert an alpha value to a pen transparency value.\n * Alpha ranges from 0 to 1, where 0 is transparent and 1 is opaque.\n * Transparency ranges from 0 to 100, where 0 is opaque and 100 is transparent.\n * @param {number} alpha - the input alpha value.\n * @returns {number} the transparency value.\n * @private\n */\n _alphaToTransparency (alpha) {\n return (1.0 - alpha) * 100.0;\n }\n\n /**\n * Convert a pen transparency value to an alpha value.\n * Alpha ranges from 0 to 1, where 0 is transparent and 1 is opaque.\n * Transparency ranges from 0 to 100, where 0 is opaque and 100 is transparent.\n * @param {number} transparency - the input transparency value.\n * @returns {number} the alpha value.\n * @private\n */\n _transparencyToAlpha (transparency) {\n return 1.0 - (transparency / 100.0);\n }\n\n /**\n * @returns {object} metadata for this extension and its blocks.\n */\n getInfo () {\n return {\n id: 'pen',\n name: formatMessage({\n id: 'pen.categoryName',\n default: 'Pen',\n description: 'Label for the pen extension category'\n }),\n blockIconURI: blockIconURI,\n blocks: [\n {\n opcode: 'clear',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'pen.clear',\n default: 'erase all',\n description: 'erase all pen trails and stamps'\n })\n },\n {\n opcode: 'stamp',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'pen.stamp',\n default: 'stamp',\n description: 'render current costume on the background'\n }),\n filter: [TargetType.SPRITE]\n },\n {\n opcode: 'penDown',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'pen.penDown',\n default: 'pen down',\n description: 'start leaving a trail when the sprite moves'\n }),\n filter: [TargetType.SPRITE]\n },\n {\n opcode: 'penUp',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'pen.penUp',\n default: 'pen up',\n description: 'stop leaving a trail behind the sprite'\n }),\n filter: [TargetType.SPRITE]\n },\n {\n opcode: 'setPenColorToColor',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'pen.setColor',\n default: 'set pen color to [COLOR]',\n description: 'set the pen color to a particular (RGB) value'\n }),\n arguments: {\n COLOR: {\n type: ArgumentType.COLOR\n }\n },\n filter: [TargetType.SPRITE]\n },\n {\n opcode: 'changePenColorParamBy',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'pen.changeColorParam',\n default: 'change pen [COLOR_PARAM] by [VALUE]',\n description: 'change the state of a pen color parameter'\n }),\n arguments: {\n COLOR_PARAM: {\n type: ArgumentType.STRING,\n menu: 'colorParam',\n defaultValue: ColorParam.COLOR\n },\n VALUE: {\n type: ArgumentType.NUMBER,\n defaultValue: 10\n }\n },\n filter: [TargetType.SPRITE]\n },\n {\n opcode: 'setPenColorParamTo',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'pen.setColorParam',\n default: 'set pen [COLOR_PARAM] to [VALUE]',\n description: 'set the state for a pen color parameter e.g. saturation'\n }),\n arguments: {\n COLOR_PARAM: {\n type: ArgumentType.STRING,\n menu: 'colorParam',\n defaultValue: ColorParam.COLOR\n },\n VALUE: {\n type: ArgumentType.NUMBER,\n defaultValue: 50\n }\n },\n filter: [TargetType.SPRITE]\n },\n {\n opcode: 'changePenSizeBy',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'pen.changeSize',\n default: 'change pen size by [SIZE]',\n description: 'change the diameter of the trail left by a sprite'\n }),\n arguments: {\n SIZE: {\n type: ArgumentType.NUMBER,\n defaultValue: 1\n }\n },\n filter: [TargetType.SPRITE]\n },\n {\n opcode: 'setPenSizeTo',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'pen.setSize',\n default: 'set pen size to [SIZE]',\n description: 'set the diameter of a trail left by a sprite'\n }),\n arguments: {\n SIZE: {\n type: ArgumentType.NUMBER,\n defaultValue: 1\n }\n },\n filter: [TargetType.SPRITE]\n },\n /* Legacy blocks, should not be shown in flyout */\n {\n opcode: 'setPenShadeToNumber',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'pen.setShade',\n default: 'set pen shade to [SHADE]',\n description: 'legacy pen blocks - set pen shade'\n }),\n arguments: {\n SHADE: {\n type: ArgumentType.NUMBER,\n defaultValue: 1\n }\n },\n hideFromPalette: true\n },\n {\n opcode: 'changePenShadeBy',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'pen.changeShade',\n default: 'change pen shade by [SHADE]',\n description: 'legacy pen blocks - change pen shade'\n }),\n arguments: {\n SHADE: {\n type: ArgumentType.NUMBER,\n defaultValue: 1\n }\n },\n hideFromPalette: true\n },\n {\n opcode: 'setPenHueToNumber',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'pen.setHue',\n default: 'set pen color to [HUE]',\n description: 'legacy pen blocks - set pen color to number'\n }),\n arguments: {\n HUE: {\n type: ArgumentType.NUMBER,\n defaultValue: 1\n }\n },\n hideFromPalette: true\n },\n {\n opcode: 'changePenHueBy',\n blockType: BlockType.COMMAND,\n text: formatMessage({\n id: 'pen.changeHue',\n default: 'change pen color by [HUE]',\n description: 'legacy pen blocks - change pen color'\n }),\n arguments: {\n HUE: {\n type: ArgumentType.NUMBER,\n defaultValue: 1\n }\n },\n hideFromPalette: true\n }\n ],\n menus: {\n colorParam: {\n acceptReporters: true,\n items: this._initColorParam()\n }\n }\n };\n }\n\n /**\n * The pen \"clear\" block clears the pen layer's contents.\n */\n clear () {\n const penSkinId = this._getPenLayerID();\n if (penSkinId >= 0) {\n this.runtime.renderer.penClear(penSkinId);\n this.runtime.requestRedraw();\n }\n }\n\n /**\n * The pen \"stamp\" block stamps the current drawable's image onto the pen layer.\n * @param {object} args - the block arguments.\n * @param {object} util - utility object provided by the runtime.\n */\n stamp (args, util) {\n const penSkinId = this._getPenLayerID();\n if (penSkinId >= 0) {\n const target = util.target;\n this.runtime.renderer.penStamp(penSkinId, target.drawableID);\n this.runtime.requestRedraw();\n }\n }\n\n /**\n * The pen \"pen down\" block causes the target to leave pen trails on future motion.\n * @param {object} args - the block arguments.\n * @param {object} util - utility object provided by the runtime.\n */\n penDown (args, util) {\n const target = util.target;\n const penState = this._getPenState(target);\n\n if (!penState.penDown) {\n penState.penDown = true;\n target.addListener(RenderedTarget.EVENT_TARGET_MOVED, this._onTargetMoved);\n }\n\n const penSkinId = this._getPenLayerID();\n if (penSkinId >= 0) {\n this.runtime.renderer.penPoint(penSkinId, penState.penAttributes, target.x, target.y);\n this.runtime.requestRedraw();\n }\n }\n\n /**\n * The pen \"pen up\" block stops the target from leaving pen trails.\n * @param {object} args - the block arguments.\n * @param {object} util - utility object provided by the runtime.\n */\n penUp (args, util) {\n const target = util.target;\n const penState = this._getPenState(target);\n\n if (penState.penDown) {\n penState.penDown = false;\n target.removeListener(RenderedTarget.EVENT_TARGET_MOVED, this._onTargetMoved);\n }\n }\n\n /**\n * The pen \"set pen color to {color}\" block sets the pen to a particular RGB color.\n * The transparency is reset to 0.\n * @param {object} args - the block arguments.\n * @property {int} COLOR - the color to set, expressed as a 24-bit RGB value (0xRRGGBB).\n * @param {object} util - utility object provided by the runtime.\n */\n setPenColorToColor (args, util) {\n const penState = this._getPenState(util.target);\n const rgb = Cast.toRgbColorObject(args.COLOR);\n const hsv = Color.rgbToHsv(rgb);\n penState.color = (hsv.h / 360) * 100;\n penState.saturation = hsv.s * 100;\n penState.brightness = hsv.v * 100;\n if (Object.prototype.hasOwnProperty.call(rgb, 'a')) {\n penState.transparency = 100 * (1 - (rgb.a / 255.0));\n } else {\n penState.transparency = 0;\n }\n\n // Set the legacy \"shade\" value the same way scratch 2 did.\n penState._shade = penState.brightness / 2;\n\n this._updatePenColor(penState);\n }\n\n /**\n * Update the cached color from the color, saturation, brightness and transparency values\n * in the provided PenState object.\n * @param {PenState} penState - the pen state to update.\n * @private\n */\n _updatePenColor (penState) {\n const rgb = Color.hsvToRgb({\n h: penState.color * 360 / 100,\n s: penState.saturation / 100,\n v: penState.brightness / 100\n });\n penState.penAttributes.color4f[0] = rgb.r / 255.0;\n penState.penAttributes.color4f[1] = rgb.g / 255.0;\n penState.penAttributes.color4f[2] = rgb.b / 255.0;\n penState.penAttributes.color4f[3] = this._transparencyToAlpha(penState.transparency);\n }\n\n /**\n * Set or change a single color parameter on the pen state, and update the pen color.\n * @param {ColorParam} param - the name of the color parameter to set or change.\n * @param {number} value - the value to set or change the param by.\n * @param {PenState} penState - the pen state to update.\n * @param {boolean} change - if true change param by value, if false set param to value.\n * @private\n */\n _setOrChangeColorParam (param, value, penState, change) {\n switch (param) {\n case ColorParam.COLOR:\n penState.color = this._wrapColor(value + (change ? penState.color : 0));\n break;\n case ColorParam.SATURATION:\n penState.saturation = this._clampColorParam(value + (change ? penState.saturation : 0));\n break;\n case ColorParam.BRIGHTNESS:\n penState.brightness = this._clampColorParam(value + (change ? penState.brightness : 0));\n break;\n case ColorParam.TRANSPARENCY:\n penState.transparency = this._clampColorParam(value + (change ? penState.transparency : 0));\n break;\n default:\n log.warn(`Tried to set or change unknown color parameter: ${param}`);\n }\n this._updatePenColor(penState);\n }\n\n /**\n * The \"change pen {ColorParam} by {number}\" block changes one of the pen's color parameters\n * by a given amound.\n * @param {object} args - the block arguments.\n * @property {ColorParam} COLOR_PARAM - the name of the selected color parameter.\n * @property {number} VALUE - the amount to change the selected parameter by.\n * @param {object} util - utility object provided by the runtime.\n */\n changePenColorParamBy (args, util) {\n const penState = this._getPenState(util.target);\n this._setOrChangeColorParam(args.COLOR_PARAM, Cast.toNumber(args.VALUE), penState, true);\n }\n\n /**\n * The \"set pen {ColorParam} to {number}\" block sets one of the pen's color parameters\n * to a given amound.\n * @param {object} args - the block arguments.\n * @property {ColorParam} COLOR_PARAM - the name of the selected color parameter.\n * @property {number} VALUE - the amount to set the selected parameter to.\n * @param {object} util - utility object provided by the runtime.\n */\n setPenColorParamTo (args, util) {\n const penState = this._getPenState(util.target);\n this._setOrChangeColorParam(args.COLOR_PARAM, Cast.toNumber(args.VALUE), penState, false);\n }\n\n /**\n * The pen \"change pen size by {number}\" block changes the pen size by the given amount.\n * @param {object} args - the block arguments.\n * @property {number} SIZE - the amount of desired size change.\n * @param {object} util - utility object provided by the runtime.\n */\n changePenSizeBy (args, util) {\n const penAttributes = this._getPenState(util.target).penAttributes;\n penAttributes.diameter = this._clampPenSize(penAttributes.diameter + Cast.toNumber(args.SIZE));\n }\n\n /**\n * The pen \"set pen size to {number}\" block sets the pen size to the given amount.\n * @param {object} args - the block arguments.\n * @property {number} SIZE - the amount of desired size change.\n * @param {object} util - utility object provided by the runtime.\n */\n setPenSizeTo (args, util) {\n const penAttributes = this._getPenState(util.target).penAttributes;\n penAttributes.diameter = this._clampPenSize(Cast.toNumber(args.SIZE));\n }\n\n /* LEGACY OPCODES */\n /**\n * Scratch 2 \"hue\" param is equivelant to twice the new \"color\" param.\n * @param {object} args - the block arguments.\n * @property {number} HUE - the amount to set the hue to.\n * @param {object} util - utility object provided by the runtime.\n */\n setPenHueToNumber (args, util) {\n const penState = this._getPenState(util.target);\n const hueValue = Cast.toNumber(args.HUE);\n const colorValue = hueValue / 2;\n this._setOrChangeColorParam(ColorParam.COLOR, colorValue, penState, false);\n this._setOrChangeColorParam(ColorParam.TRANSPARENCY, 0, penState, false);\n this._legacyUpdatePenColor(penState);\n }\n\n /**\n * Scratch 2 \"hue\" param is equivelant to twice the new \"color\" param.\n * @param {object} args - the block arguments.\n * @property {number} HUE - the amount of desired hue change.\n * @param {object} util - utility object provided by the runtime.\n */\n changePenHueBy (args, util) {\n const penState = this._getPenState(util.target);\n const hueChange = Cast.toNumber(args.HUE);\n const colorChange = hueChange / 2;\n this._setOrChangeColorParam(ColorParam.COLOR, colorChange, penState, true);\n\n this._legacyUpdatePenColor(penState);\n }\n\n /**\n * Use legacy \"set shade\" code to calculate RGB value for shade,\n * then convert back to HSV and store those components.\n * It is important to also track the given shade in penState._shade\n * because it cannot be accurately backed out of the new HSV later.\n * @param {object} args - the block arguments.\n * @property {number} SHADE - the amount to set the shade to.\n * @param {object} util - utility object provided by the runtime.\n */\n setPenShadeToNumber (args, util) {\n const penState = this._getPenState(util.target);\n let newShade = Cast.toNumber(args.SHADE);\n\n // Wrap clamp the new shade value the way scratch 2 did.\n newShade = newShade % 200;\n if (newShade < 0) newShade += 200;\n\n // And store the shade that was used to compute this new color for later use.\n penState._shade = newShade;\n\n this._legacyUpdatePenColor(penState);\n }\n\n /**\n * Because \"shade\" cannot be backed out of hsv consistently, use the previously\n * stored penState._shade to make the shade change.\n * @param {object} args - the block arguments.\n * @property {number} SHADE - the amount of desired shade change.\n * @param {object} util - utility object provided by the runtime.\n */\n changePenShadeBy (args, util) {\n const penState = this._getPenState(util.target);\n const shadeChange = Cast.toNumber(args.SHADE);\n this.setPenShadeToNumber({SHADE: penState._shade + shadeChange}, util);\n }\n\n /**\n * Update the pen state's color from its hue & shade values, Scratch 2.0 style.\n * @param {object} penState - update the HSV & RGB values in this pen state from its hue & shade values.\n * @private\n */\n _legacyUpdatePenColor (penState) {\n // Create the new color in RGB using the scratch 2 \"shade\" model\n let rgb = Color.hsvToRgb({h: penState.color * 360 / 100, s: 1, v: 1});\n const shade = (penState._shade > 100) ? 200 - penState._shade : penState._shade;\n if (shade < 50) {\n rgb = Color.mixRgb(Color.RGB_BLACK, rgb, (10 + shade) / 60);\n } else {\n rgb = Color.mixRgb(rgb, Color.RGB_WHITE, (shade - 50) / 60);\n }\n\n // Update the pen state according to new color\n const hsv = Color.rgbToHsv(rgb);\n penState.color = 100 * hsv.h / 360;\n penState.saturation = 100 * hsv.s;\n penState.brightness = 100 * hsv.v;\n\n this._updatePenColor(penState);\n }\n}\n\nmodule.exports = Scratch3PenBlocks;\n","const formatMessage = require('format-message');\nconst languageNames = require('scratch-translate-extension-languages');\n\nconst ArgumentType = require('../../extension-support/argument-type');\nconst BlockType = require('../../extension-support/block-type');\nconst Cast = require('../../util/cast');\nconst MathUtil = require('../../util/math-util');\nconst Clone = require('../../util/clone');\nconst log = require('../../util/log');\nconst {fetchWithTimeout} = require('../../util/fetch-with-timeout');\n\n/**\n * Icon svg to be displayed in the blocks category menu, encoded as a data URI.\n * @type {string}\n */\n// eslint-disable-next-line max-len\nconst menuIconURI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjBweCIgaGVpZ2h0PSIyMHB4IiB2aWV3Qm94PSIwIDAgMjAgMjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjIgKDY3MTQ1KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5FeHRlbnNpb25zL1NvZnR3YXJlL1RleHQtdG8tU3BlZWNoLU1lbnU8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZyBpZD0iRXh0ZW5zaW9ucy9Tb2Z0d2FyZS9UZXh0LXRvLVNwZWVjaC1NZW51IiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8ZyBpZD0idGV4dDJzcGVlY2giIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIuMDAwMDAwLCAyLjAwMDAwMCkiIGZpbGwtcnVsZT0ibm9uemVybyI+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik01Ljc1LDguODM0NjcxNzMgQzUuNzUsOC4zMjY5NjM0NCA1LjAwMzAwNzI3LDguMDQyMjEzNzEgNC41NTYyODAxMiw4LjQ0NDE0OTk5IEwzLjIwNjI4MDEyLDkuNTI1MzU3MDIgQzIuNjk2NzMzNzgsOS45MzM0NDk2OCAyLjAzNzQ4Njc1LDEwLjE2NTg3ODggMS4zNSwxMC4xNjU4Nzg4IEwxLjE1LDEwLjE2NTg3ODggQzAuNjMyNTk2MTY1LDEwLjE2NTg3ODggMC4yNSwxMC41MTA2MDAyIDAuMjUsMTAuOTUyMDM1NSBMMC4yNSwxMy4wNjkzOTkzIEMwLjI1LDEzLjUxMDgzNDYgMC42MzI1OTYxNjUsMTMuODU1NTU2IDEuMTUsMTMuODU1NTU2IEwxLjM1LDEzLjg1NTU1NiBDMi4wNzg3Nzg0MSwxMy44NTU1NTYgMi43MjY4NjE2MSwxNC4wNjY3NjM2IDMuMjU5ODYwNDksMTQuNDk5IEw0LjU1OTIwMTQ3LDE1LjU3OTY2MDggQzUuMDEzMDkyNzYsMTUuOTU0NTM5NiA1Ljc1LDE1LjY3MzYzNDQgNS43NSwxNS4xNDE3MTI4IEw1Ljc1LDguODM0NjcxNzMgWiIgaWQ9InNwZWFrZXIiIHN0cm9rZS1vcGFjaXR5PSIwLjE1IiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMC41IiBmaWxsPSIjNEQ0RDREIj48L3BhdGg+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMC43MDQ4MzEzLDggQzkuNzkwNjc0NjgsOS4xMzExNDg0NyA4LjMwNjYxODQsOS43MTQyODU3MSA3LjgzMzMzMzMzLDkuNzE0Mjg1NzEgQzcuODMzMzMzMzMsOS43MTQyODU3MSA3LjUsOS43MTQyODU3MSA3LjUsOS4zODA5NTIzOCBDNy41LDkuMDg1MjI2ODQgOC4wNjIyMDE2OCw4LjkwMTk0MTY0IDguMTg5MDYwNjcsNy41Njc1NDA1OCBDNi44ODk5Njk5MSw2LjkwNjc5MDA1IDYsNS41NTczMjY4MyA2LDQgQzYsMS43OTA4NjEgNy43OTA4NjEsNC4wNTgxMjI1MWUtMTYgMTAsMCBMMTIsMCBDMTQuMjA5MTM5LC00LjA1ODEyMjUxZS0xNiAxNiwxLjc5MDg2MSAxNiw0IEMxNiw2LjIwOTEzOSAxNC4yMDkxMzksOCAxMiw4IEwxMC43MDQ4MzEzLDggWiIgaWQ9InNwZWVjaCIgZmlsbD0iIzBFQkQ4QyI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+';\n\n/**\n * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.\n * @type {string}\n */\n// eslint-disable-next-line max-len\nconst blockIconURI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSIwIDAgNDAgNDAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjIgKDY3MTQ1KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5FeHRlbnNpb25zL1NvZnR3YXJlL1RleHQtdG8tU3BlZWNoLUJsb2NrPC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGcgaWQ9IkV4dGVuc2lvbnMvU29mdHdhcmUvVGV4dC10by1TcGVlY2gtQmxvY2siIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZS1vcGFjaXR5PSIwLjE1Ij4KICAgICAgICA8ZyBpZD0idGV4dDJzcGVlY2giIHRyYW5zZm9ybT0idHJhbnNsYXRlKDQuMDAwMDAwLCA0LjAwMDAwMCkiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSIjMDAwMDAwIj4KICAgICAgICAgICAgPHBhdGggZD0iTTExLjUsMTcuNjY5MzQzNSBDMTEuNSwxNi42NTM5MjY5IDEwLjAwNjAxNDUsMTYuMDg0NDI3NCA5LjExMjU2MDI0LDE2Ljg4ODMgTDYuNDEyNTYwMjQsMTkuMDUwNzE0IEM1LjM5MzQ2NzU1LDE5Ljg2Njg5OTQgNC4wNzQ5NzM1MSwyMC4zMzE3NTc1IDIuNywyMC4zMzE3NTc1IEwyLjMsMjAuMzMxNzU3NSBDMS4yNjUxOTIzMywyMC4zMzE3NTc1IDAuNSwyMS4wMjEyMDAzIDAuNSwyMS45MDQwNzEgTDAuNSwyNi4xMzg3OTg2IEMwLjUsMjcuMDIxNjY5MyAxLjI2NTE5MjMzLDI3LjcxMTExMiAyLjMsMjcuNzExMTEyIEwyLjcsMjcuNzExMTEyIEM0LjE1NzU1NjgyLDI3LjcxMTExMiA1LjQ1MzcyMzIyLDI4LjEzMzUyNzEgNi41MTk3MjA5OCwyOC45OTggTDkuMTE4NDAyOTMsMzEuMTU5MzIxNiBDMTAuMDI2MTg1NSwzMS45MDkwNzkzIDExLjUsMzEuMzQ3MjY4OSAxMS41LDMwLjI4MzQyNTUgTDExLjUsMTcuNjY5MzQzNSBaIiBpZD0ic3BlYWtlciIgZmlsbD0iIzRENEQ0RCI+PC9wYXRoPgogICAgICAgICAgICA8cGF0aCBkPSJNMjEuNjQzNjA2NiwxNi41IEMxOS45NzcwMDk5LDE4LjQzNzAyMzQgMTcuMTA1MDI3NSwxOS45Mjg1NzE0IDE1LjY2NjY2NjcsMTkuOTI4NTcxNCBDMTUuNTEyNjM5NywxOS45Mjg1NzE0IDE1LjMxNjYyOTIsMTkuODk1OTAzIDE1LjEwOTcyNjUsMTkuNzkyNDUxNyBDMTQuNzM3NjAzOSwxOS42MDYzOTA0IDE0LjUsMTkuMjQ5OTg0NiAxNC41LDE4Ljc2MTkwNDggQzE0LjUsMTguNjU2ODA0MSAxNC41MTcwNTU1LDE4LjU1NDUwNzYgMTQuNTQ5NDQ2NywxOC40NTQwODQ0IEMxNC42MjU3NTQ1LDE4LjIxNzUwNjMgMTUuMTczNTcyMSwxNy40Njc1MzEgMTUuMjc3MjA3MSwxNy4yODA5ODgxIEMxNS41NDYzNTI2LDE2Ljc5NjUyNjEgMTUuNzM5MDI1LDE2LjIwNjM1NjEgMTUuODQzMjg5MSwxNS40MTYwMDM0IEMxMy4xODk3MDA1LDEzLjkyNjgzNjkgMTEuNSwxMS4xMTM5NjY4IDExLjUsOCBDMTEuNSwzLjMwNTU3OTYzIDE1LjMwNTU3OTYsLTAuNSAyMCwtMC41IEwyNCwtMC41IEMyOC42OTQ0MjA0LC0wLjUgMzIuNSwzLjMwNTU3OTYzIDMyLjUsOCBDMzIuNSwxMi42OTQ0MjA0IDI4LjY5NDQyMDQsMTYuNSAyNCwxNi41IEwyMS42NDM2MDY2LDE2LjUgWiIgaWQ9InNwZWVjaCIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+';\n\n/**\n * The url of the synthesis server.\n * @type {string}\n */\nconst SERVER_HOST = 'https://synthesis-service.scratch.mit.edu';\n\n/**\n * How long to wait in ms before timing out requests to synthesis server.\n * @type {int}\n */\nconst SERVER_TIMEOUT = 10000; // 10 seconds\n\n/**\n * Volume for playback of speech sounds, as a percentage.\n * @type {number}\n */\nconst SPEECH_VOLUME = 250;\n\n/**\n * An id for one of the voices.\n */\nconst ALTO_ID = 'ALTO';\n\n/**\n * An id for one of the voices.\n */\nconst TENOR_ID = 'TENOR';\n\n/**\n * An id for one of the voices.\n */\nconst SQUEAK_ID = 'SQUEAK';\n\n/**\n * An id for one of the voices.\n */\nconst GIANT_ID = 'GIANT';\n\n/**\n * An id for one of the voices.\n */\nconst KITTEN_ID = 'KITTEN';\n\n/**\n * Playback rate for the tenor voice, for cases where we have only a female gender voice.\n */\nconst FEMALE_TENOR_RATE = 0.89; // -2 semitones\n\n/**\n * Playback rate for the giant voice, for cases where we have only a female gender voice.\n */\nconst FEMALE_GIANT_RATE = 0.79; // -4 semitones\n\n/**\n * Language ids. The value for each language id is a valid Scratch locale.\n */\nconst ARABIC_ID = 'ar';\nconst CHINESE_ID = 'zh-cn';\nconst DANISH_ID = 'da';\nconst DUTCH_ID = 'nl';\nconst ENGLISH_ID = 'en';\nconst FRENCH_ID = 'fr';\nconst GERMAN_ID = 'de';\nconst HINDI_ID = 'hi';\nconst ICELANDIC_ID = 'is';\nconst ITALIAN_ID = 'it';\nconst JAPANESE_ID = 'ja';\nconst KOREAN_ID = 'ko';\nconst NORWEGIAN_ID = 'nb';\nconst POLISH_ID = 'pl';\nconst PORTUGUESE_BR_ID = 'pt-br';\nconst PORTUGUESE_ID = 'pt';\nconst ROMANIAN_ID = 'ro';\nconst RUSSIAN_ID = 'ru';\nconst SPANISH_ID = 'es';\nconst SPANISH_419_ID = 'es-419';\nconst SWEDISH_ID = 'sv';\nconst TURKISH_ID = 'tr';\nconst WELSH_ID = 'cy';\n\n/**\n * Class for the text2speech blocks.\n * @constructor\n */\nclass Scratch3Text2SpeechBlocks {\n constructor (runtime) {\n /**\n * The runtime instantiating this block package.\n * @type {Runtime}\n */\n this.runtime = runtime;\n\n /**\n * Map of soundPlayers by sound id.\n * @type {Map}\n */\n this._soundPlayers = new Map();\n\n this._stopAllSpeech = this._stopAllSpeech.bind(this);\n if (this.runtime) {\n this.runtime.on('PROJECT_STOP_ALL', this._stopAllSpeech);\n }\n\n this._onTargetCreated = this._onTargetCreated.bind(this);\n if (this.runtime) {\n runtime.on('targetWasCreated', this._onTargetCreated);\n }\n\n /**\n * A list of all Scratch locales that are supported by the extension.\n * @type {Array}\n */\n this._supportedLocales = this._getSupportedLocales();\n }\n\n /**\n * An object with info for each voice.\n */\n get VOICE_INFO () {\n return {\n [ALTO_ID]: {\n name: formatMessage({\n id: 'text2speech.alto',\n default: 'alto',\n description: 'Name for a voice with ambiguous gender.'\n }),\n gender: 'female',\n playbackRate: 1\n },\n [TENOR_ID]: {\n name: formatMessage({\n id: 'text2speech.tenor',\n default: 'tenor',\n description: 'Name for a voice with ambiguous gender.'\n }),\n gender: 'male',\n playbackRate: 1\n },\n [SQUEAK_ID]: {\n name: formatMessage({\n id: 'text2speech.squeak',\n default: 'squeak',\n description: 'Name for a funny voice with a high pitch.'\n }),\n gender: 'female',\n playbackRate: 1.19 // +3 semitones\n },\n [GIANT_ID]: {\n name: formatMessage({\n id: 'text2speech.giant',\n default: 'giant',\n description: 'Name for a funny voice with a low pitch.'\n }),\n gender: 'male',\n playbackRate: 0.84 // -3 semitones\n },\n [KITTEN_ID]: {\n name: formatMessage({\n id: 'text2speech.kitten',\n default: 'kitten',\n description: 'A baby cat.'\n }),\n gender: 'female',\n playbackRate: 1.41 // +6 semitones\n }\n };\n }\n\n /**\n * An object with information for each language.\n *\n * A note on the different sets of locales referred to in this extension:\n *\n * SCRATCH LOCALE\n * Set by the editor, and used to store the language state in the project.\n * Listed in l10n: https://github.com/scratchfoundation/scratch-l10n/blob/master/src/supported-locales.js\n * SUPPORTED LOCALE\n * A Scratch locale that has a corresponding extension locale.\n * EXTENSION LOCALE\n * A locale corresponding to one of the available spoken languages\n * in the extension. There can be multiple supported locales for a single\n * extension locale. For example, for both written versions of chinese,\n * zh-cn and zh-tw, we use a single spoken language (Mandarin). So there\n * are two supported locales, with a single extension locale.\n * SPEECH SYNTH LOCALE\n * A different locale code system, used by our speech synthesis service.\n * Each extension locale has a speech synth locale.\n */\n get LANGUAGE_INFO () {\n return {\n [ARABIC_ID]: {\n name: 'Arabic',\n locales: ['ar'],\n speechSynthLocale: 'arb',\n singleGender: true\n },\n [CHINESE_ID]: {\n name: 'Chinese (Mandarin)',\n locales: ['zh-cn', 'zh-tw'],\n speechSynthLocale: 'cmn-CN',\n singleGender: true\n },\n [DANISH_ID]: {\n name: 'Danish',\n locales: ['da'],\n speechSynthLocale: 'da-DK'\n },\n [DUTCH_ID]: {\n name: 'Dutch',\n locales: ['nl'],\n speechSynthLocale: 'nl-NL'\n },\n [ENGLISH_ID]: {\n name: 'English',\n locales: ['en'],\n speechSynthLocale: 'en-US'\n },\n [FRENCH_ID]: {\n name: 'French',\n locales: ['fr'],\n speechSynthLocale: 'fr-FR'\n },\n [GERMAN_ID]: {\n name: 'German',\n locales: ['de'],\n speechSynthLocale: 'de-DE'\n },\n [HINDI_ID]: {\n name: 'Hindi',\n locales: ['hi'],\n speechSynthLocale: 'hi-IN',\n singleGender: true\n },\n [ICELANDIC_ID]: {\n name: 'Icelandic',\n locales: ['is'],\n speechSynthLocale: 'is-IS'\n },\n [ITALIAN_ID]: {\n name: 'Italian',\n locales: ['it'],\n speechSynthLocale: 'it-IT'\n },\n [JAPANESE_ID]: {\n name: 'Japanese',\n locales: ['ja', 'ja-hira'],\n speechSynthLocale: 'ja-JP'\n },\n [KOREAN_ID]: {\n name: 'Korean',\n locales: ['ko'],\n speechSynthLocale: 'ko-KR',\n singleGender: true\n },\n [NORWEGIAN_ID]: {\n name: 'Norwegian',\n locales: ['nb', 'nn'],\n speechSynthLocale: 'nb-NO',\n singleGender: true\n },\n [POLISH_ID]: {\n name: 'Polish',\n locales: ['pl'],\n speechSynthLocale: 'pl-PL'\n },\n [PORTUGUESE_BR_ID]: {\n name: 'Portuguese (Brazilian)',\n locales: ['pt-br'],\n speechSynthLocale: 'pt-BR'\n },\n [PORTUGUESE_ID]: {\n name: 'Portuguese (European)',\n locales: ['pt'],\n speechSynthLocale: 'pt-PT'\n },\n [ROMANIAN_ID]: {\n name: 'Romanian',\n locales: ['ro'],\n speechSynthLocale: 'ro-RO',\n singleGender: true\n },\n [RUSSIAN_ID]: {\n name: 'Russian',\n locales: ['ru'],\n speechSynthLocale: 'ru-RU'\n },\n [SPANISH_ID]: {\n name: 'Spanish (European)',\n locales: ['es'],\n speechSynthLocale: 'es-ES'\n },\n [SPANISH_419_ID]: {\n name: 'Spanish (Latin American)',\n locales: ['es-419'],\n speechSynthLocale: 'es-US'\n },\n [SWEDISH_ID]: {\n name: 'Swedish',\n locales: ['sv'],\n speechSynthLocale: 'sv-SE',\n singleGender: true\n },\n [TURKISH_ID]: {\n name: 'Turkish',\n locales: ['tr'],\n speechSynthLocale: 'tr-TR',\n singleGender: true\n },\n [WELSH_ID]: {\n name: 'Welsh',\n locales: ['cy'],\n speechSynthLocale: 'cy-GB',\n singleGender: true\n }\n };\n }\n\n /**\n * The key to load & store a target's text2speech state.\n * @return {string} The key.\n */\n static get STATE_KEY () {\n return 'Scratch.text2speech';\n }\n\n /**\n * The default state, to be used when a target has no existing state.\n * @type {Text2SpeechState}\n */\n static get DEFAULT_TEXT2SPEECH_STATE () {\n return {\n voiceId: ALTO_ID\n };\n }\n\n /**\n * A default language to use for speech synthesis.\n * @type {string}\n */\n get DEFAULT_LANGUAGE () {\n return ENGLISH_ID;\n }\n\n /**\n * @param {Target} target - collect state for this target.\n * @returns {Text2SpeechState} the mutable state associated with that target. This will be created if necessary.\n * @private\n */\n _getState (target) {\n let state = target.getCustomState(Scratch3Text2SpeechBlocks.STATE_KEY);\n if (!state) {\n state = Clone.simple(Scratch3Text2SpeechBlocks.DEFAULT_TEXT2SPEECH_STATE);\n target.setCustomState(Scratch3Text2SpeechBlocks.STATE_KEY, state);\n }\n return state;\n }\n\n /**\n * When a Target is cloned, clone the state.\n * @param {Target} newTarget - the newly created target.\n * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any.\n * @listens Runtime#event:targetWasCreated\n * @private\n */\n _onTargetCreated (newTarget, sourceTarget) {\n if (sourceTarget) {\n const state = sourceTarget.getCustomState(Scratch3Text2SpeechBlocks.STATE_KEY);\n if (state) {\n newTarget.setCustomState(Scratch3Text2SpeechBlocks.STATE_KEY, Clone.simple(state));\n }\n }\n }\n\n /**\n * @returns {object} metadata for this extension and its blocks.\n */\n getInfo () {\n // Only localize the default input to the \"speak\" block if we are in a\n // supported language.\n let defaultTextToSpeak = 'hello';\n if (this.isSupportedLanguage(this.getEditorLanguage())) {\n defaultTextToSpeak = formatMessage({\n id: 'text2speech.defaultTextToSpeak',\n default: 'hello',\n description: 'hello: the default text to speak'\n });\n }\n\n return {\n id: 'text2speech',\n name: formatMessage({\n id: 'text2speech.categoryName',\n default: 'Text to Speech',\n description: 'Name of the Text to Speech extension.'\n }),\n blockIconURI: blockIconURI,\n menuIconURI: menuIconURI,\n blocks: [\n {\n opcode: 'speakAndWait',\n text: formatMessage({\n id: 'text2speech.speakAndWaitBlock',\n default: 'speak [WORDS]',\n description: 'Speak some words.'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n WORDS: {\n type: ArgumentType.STRING,\n defaultValue: defaultTextToSpeak\n }\n }\n },\n {\n opcode: 'setVoice',\n text: formatMessage({\n id: 'text2speech.setVoiceBlock',\n default: 'set voice to [VOICE]',\n description: 'Set the voice for speech synthesis.'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n VOICE: {\n type: ArgumentType.STRING,\n menu: 'voices',\n defaultValue: ALTO_ID\n }\n }\n },\n {\n opcode: 'setLanguage',\n text: formatMessage({\n id: 'text2speech.setLanguageBlock',\n default: 'set language to [LANGUAGE]',\n description: 'Set the language for speech synthesis.'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n LANGUAGE: {\n type: ArgumentType.STRING,\n menu: 'languages',\n defaultValue: this.getCurrentLanguage()\n }\n }\n }\n ],\n menus: {\n voices: {\n acceptReporters: true,\n items: this.getVoiceMenu()\n },\n languages: {\n acceptReporters: true,\n items: this.getLanguageMenu()\n }\n }\n };\n }\n\n /**\n * Get the language code currently set in the editor, or fall back to the\n * browser locale.\n * @return {string} a Scratch locale code.\n */\n getEditorLanguage () {\n const locale = formatMessage.setup().locale ||\n navigator.language || navigator.userLanguage || this.DEFAULT_LANGUAGE;\n return locale.toLowerCase();\n }\n\n /**\n * Get the language code currently set for the extension.\n * @returns {string} a Scratch locale code.\n */\n getCurrentLanguage () {\n const stage = this.runtime.getTargetForStage();\n if (!stage) return this.DEFAULT_LANGUAGE;\n // If no language has been set, set it to the editor locale (or default).\n if (!stage.textToSpeechLanguage) {\n this.setCurrentLanguage(this.getEditorLanguage());\n }\n return stage.textToSpeechLanguage;\n }\n\n /**\n * Set the language code for the extension.\n * It is stored in the stage so it can be saved and loaded with the project.\n * @param {string} locale a locale code.\n */\n setCurrentLanguage (locale) {\n const stage = this.runtime.getTargetForStage();\n if (!stage) return;\n\n if (this.isSupportedLanguage(locale)) {\n stage.textToSpeechLanguage = this._getExtensionLocaleForSupportedLocale(locale);\n }\n\n // Support language names dropped onto the menu via reporter block\n // such as a variable containing a language name (in any language),\n // or the translate extension's language reporter.\n const localeForDroppedName = languageNames.nameMap[locale.toLowerCase()];\n if (localeForDroppedName && this.isSupportedLanguage(localeForDroppedName)) {\n stage.textToSpeechLanguage =\n this._getExtensionLocaleForSupportedLocale(localeForDroppedName);\n }\n\n // If the language is null, set it to the default language.\n // This can occur e.g. if the extension was loaded with the editor\n // set to a language that is not in the list.\n if (!stage.textToSpeechLanguage) {\n stage.textToSpeechLanguage = this.DEFAULT_LANGUAGE;\n }\n }\n\n /**\n * Get the extension locale for a supported locale, or null.\n * @param {string} locale a locale code.\n * @returns {?string} a locale supported by the extension.\n */\n _getExtensionLocaleForSupportedLocale (locale) {\n for (const lang in this.LANGUAGE_INFO) {\n if (this.LANGUAGE_INFO[lang].locales.includes(locale)) {\n return lang;\n }\n }\n log.error(`cannot find extension locale for locale ${locale}`);\n }\n\n /**\n * Get the locale code used by the speech synthesis server corresponding to\n * the current language code set for the extension.\n * @returns {string} a speech synthesis locale.\n */\n _getSpeechSynthLocale () {\n let speechSynthLocale = this.LANGUAGE_INFO[this.DEFAULT_LANGUAGE].speechSynthLocale;\n if (this.LANGUAGE_INFO[this.getCurrentLanguage()]) {\n speechSynthLocale = this.LANGUAGE_INFO[this.getCurrentLanguage()].speechSynthLocale;\n }\n return speechSynthLocale;\n }\n\n /**\n * Get an array of the locales supported by this extension.\n * @returns {Array} An array of locale strings.\n */\n _getSupportedLocales () {\n return Object.keys(this.LANGUAGE_INFO).reduce((acc, lang) =>\n acc.concat(this.LANGUAGE_INFO[lang].locales), []);\n }\n\n /**\n * Check if a Scratch language code is in the list of supported languages for the\n * speech synthesis service.\n * @param {string} languageCode the language code to check.\n * @returns {boolean} true if the language code is supported.\n */\n isSupportedLanguage (languageCode) {\n return this._supportedLocales.includes(languageCode);\n }\n\n /**\n * Get the menu of voices for the \"set voice\" block.\n * @return {array} the text and value for each menu item.\n */\n getVoiceMenu () {\n return Object.keys(this.VOICE_INFO).map(voiceId => ({\n text: this.VOICE_INFO[voiceId].name,\n value: voiceId\n }));\n }\n\n /**\n * Get the localized menu of languages for the \"set language\" block.\n * For each language:\n * if there is a custom translated spoken language name, use that;\n * otherwise use the translation in the languageNames menuMap;\n * otherwise fall back to the untranslated name in LANGUAGE_INFO.\n * @return {array} the text and value for each menu item.\n */\n getLanguageMenu () {\n const editorLanguage = this.getEditorLanguage();\n // Get the array of localized language names\n const localizedNameMap = {};\n let nameArray = languageNames.menuMap[editorLanguage];\n if (nameArray) {\n // Also get any localized names of spoken languages\n let spokenNameArray = [];\n if (languageNames.spokenLanguages) {\n spokenNameArray = languageNames.spokenLanguages[editorLanguage];\n nameArray = nameArray.concat(spokenNameArray);\n }\n // Create a map of language code to localized name\n // The localized spoken language names have been concatenated onto\n // the end of the name array, so the result of the forEach below is\n // when there is both a written language name (e.g. 'Chinese\n // (simplified)') and a spoken language name (e.g. 'Chinese\n // (Mandarin)', we always use the spoken version.\n nameArray.forEach(lang => {\n localizedNameMap[lang.code] = lang.name;\n });\n }\n\n return Object.keys(this.LANGUAGE_INFO).map(key => {\n let name = this.LANGUAGE_INFO[key].name;\n const localizedName = localizedNameMap[key];\n if (localizedName) {\n name = localizedName;\n }\n // Uppercase the first character of the name\n name = name.charAt(0).toUpperCase() + name.slice(1);\n return {\n text: name,\n value: key\n };\n });\n }\n\n /**\n * Set the voice for speech synthesis for this sprite.\n * @param {object} args Block arguments\n * @param {object} util Utility object provided by the runtime.\n */\n setVoice (args, util) {\n const state = this._getState(util.target);\n\n let voice = args.VOICE;\n\n // If the arg is a dropped number, treat it as a voice index\n let voiceNum = parseInt(voice, 10);\n if (!isNaN(voiceNum)) {\n voiceNum -= 1; // Treat dropped args as one-indexed\n voiceNum = MathUtil.wrapClamp(voiceNum, 0, Object.keys(this.VOICE_INFO).length - 1);\n voice = Object.keys(this.VOICE_INFO)[voiceNum];\n }\n\n // Only set the voice if the arg is a valid voice id.\n if (Object.keys(this.VOICE_INFO).includes(voice)) {\n state.voiceId = voice;\n }\n }\n\n /**\n * Set the language for speech synthesis.\n * @param {object} args Block arguments\n */\n setLanguage (args) {\n this.setCurrentLanguage(args.LANGUAGE);\n }\n\n /**\n * Stop all currently playing speech sounds.\n */\n _stopAllSpeech () {\n this._soundPlayers.forEach(player => {\n player.stop();\n });\n }\n\n /**\n * Convert the provided text into a sound file and then play the file.\n * @param {object} args Block arguments\n * @param {object} util Utility object provided by the runtime.\n * @return {Promise} A promise that resolves after playing the sound\n */\n speakAndWait (args, util) {\n // Cast input to string\n let words = Cast.toString(args.WORDS);\n let locale = this._getSpeechSynthLocale();\n\n const state = this._getState(util.target);\n\n let gender = this.VOICE_INFO[state.voiceId].gender;\n let playbackRate = this.VOICE_INFO[state.voiceId].playbackRate;\n\n // Special case for voices where the synthesis service only provides a\n // single gender voice. In that case, always request the female voice,\n // and set special playback rates for the tenor and giant voices.\n if (this.LANGUAGE_INFO[this.getCurrentLanguage()].singleGender) {\n gender = 'female';\n if (state.voiceId === TENOR_ID) {\n playbackRate = FEMALE_TENOR_RATE;\n }\n if (state.voiceId === GIANT_ID) {\n playbackRate = FEMALE_GIANT_RATE;\n }\n }\n\n if (state.voiceId === KITTEN_ID) {\n words = words.replace(/\\S+/g, 'meow');\n locale = this.LANGUAGE_INFO[this.DEFAULT_LANGUAGE].speechSynthLocale;\n }\n\n // Build up URL\n let path = `${SERVER_HOST}/synth`;\n path += `?locale=${locale}`;\n path += `&gender=${gender}`;\n path += `&text=${encodeURIComponent(words.substring(0, 128))}`;\n\n // Perform HTTP request to get audio file\n return fetchWithTimeout(path, {}, SERVER_TIMEOUT)\n .then(res => {\n if (res.status !== 200) {\n throw new Error(`HTTP ${res.status} error reaching translation service`);\n }\n\n return res.arrayBuffer();\n })\n .then(buffer => {\n // Play the sound\n const sound = {\n data: {\n buffer\n }\n };\n return this.runtime.audioEngine.decodeSoundPlayer(sound);\n })\n .then(soundPlayer => {\n this._soundPlayers.set(soundPlayer.id, soundPlayer);\n\n soundPlayer.setPlaybackRate(playbackRate);\n\n // Increase the volume\n const engine = this.runtime.audioEngine;\n const chain = engine.createEffectChain();\n chain.set('volume', SPEECH_VOLUME);\n soundPlayer.connect(chain);\n\n soundPlayer.play();\n return new Promise(resolve => {\n soundPlayer.on('stop', () => {\n this._soundPlayers.delete(soundPlayer.id);\n resolve();\n });\n });\n })\n .catch(err => {\n log.warn(err);\n });\n }\n}\nmodule.exports = Scratch3Text2SpeechBlocks;\n","const ArgumentType = require('../../extension-support/argument-type');\nconst BlockType = require('../../extension-support/block-type');\nconst Cast = require('../../util/cast');\nconst log = require('../../util/log');\nconst {fetchWithTimeout} = require('../../util/fetch-with-timeout');\nconst languageNames = require('scratch-translate-extension-languages');\nconst formatMessage = require('format-message');\n\n/**\n * Icon svg to be displayed in the blocks category menu, encoded as a data URI.\n * @type {string}\n */\n// eslint-disable-next-line max-len\nconst menuIconURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAACXBIWXMAABYlAAAWJQFJUiTwAAAGAklEQVRYhe1YbUxTVxh+rh02o0KtkOEgKA4U4yeRWCdgxDoxCnH6h22iqSz76aasZlnijzkTBlvS4TJ/LGaJsmiyESe4hAVJvMJGxwQhLKECcRWkpWNZERs6Ctb2Lm97C/fe3n6Jyfzhk5y09z3nPPe57znnPe85DMdxeJ6x6LlW90LgM8BLchR1dXUZeXl5b3Ect+ppXsEwzHBfX98PVVVVY0GbmjW2AdgpaFYP4JxTZ+iLyCVdJFeuXNmdn59fn56enrFkyRIsWhSfk30+H1wuF+x2+1hPT4++oqLiJi/wEoA8AJslXSqdOsOlmARWV1dnlpeXd2ZnZ2fEK0xOqMViGWtoaNh++vRpa9CuZo1ZAJokQlc5dYYROR6RCq1WW56WlhZV3H0H8O9sZIHEQVzEKbTzQooBPBCYz4TlET4oFIosGtZoOHUN+Ph61GYgLuIU2tSscSmAYwAeCcx6NWs8o2aNxVKOkEUi9R55qv428Ng7b3viA/6eAs7dmrctVgD6bYBKGZ6LB4mrk7F/whcmokApfh8BWu6G2mc8ADsktuWmAbtzozGiLUJdu9QQVSC98JUkYNgBfPsboH4Z+GhPoK62FZiaAU7sCrTZmB5VHM3BPjVrrARwUVL1B4CD0vYxLVV68YFNQIICcLrn7SROtTjwEbGIE4iksFIpEVfs1BkeSdvGFUsObAz8Gm8CNTcC/49q42EIEbkLwKfhxCGWIRZC/zrQ/ifgcAWMK5YB+zc8nUBeZFuUORmfQIp/PsHGM/04YMta5oPT6cTs7Cw8Ho+oj9vtzmloaCgPZQtApVI96ejo6K2trR3lOM4nrRftJCzLfq3T6Y7LCfvuNtDL7wepfKgkTz6ZdeHdzePYlq30xz2lUintHhH0UbQ12my2+oKCguMcx7mE7aOHmWHgsxvzzzQP3ysMxMfzt2bxKmNHyZblSE5OjktYEImJidBoNFCr1frOzs5khmHe4Thubp8SCVQoFBwNUUJCwpyNwsfyZGBDOvB2fuCZQAH56KYJKJUpTy1OCOJYvXr1ocbGxjIAPwarRKvYZrNdn5iYEHV8LRW4cBj4oHheXBDT09PPRFwQxKXRaIQpmVjgkSNHfrFardcmJydjIqSMRehtOfzjmMTZmm/8hf5HAnF5vV7RVicSyHGcR6vVHh4YGPjKYrFMkTelq5JAH0B1MzMzUT+iu6cfdwfv+wv9jxchgZomaEFBwcmcnJxVY2NjXQqFQlQ/Pj6O/v7+s2az+U2Hw9Ec7X3tHXfm/v/c2hG3wLCruLm5+VBGRoY2mJGQJ0nc4ODgqZKSkjqKWSzL7olEPjJqx4PRv5CaqvE/OxyTflvWitj3xbBbnUql2kRxjYTRcA4MDHR1d3frguJiIW//NeC9/SVF2LplvcgWK8J6sKWl5UuVSrXO4/HYHj58+FNZWVkLx3HT8Rz0u3vN/t8Ho3aRaH3FgYULrKmpodT8jeBzvDcQ3T1m/5D6RXX0zNmn3TP+uq356xcmkE/NTwLoc+oMTXGpA3CnN7Bi99Hw5s8PL4mlulgFys5BXlwbn4I3qlnjsXgFBr22f+8OrFub7S/79u4Q1cWCEA8KxAmPhRfVrBFy51cK1nJnj+/rvwix0eqVswu5pJDzoPTMKhSZJzQolUoLZSLPCsRFnEI6OYE7I7xPdGYoKiq6YLVaByllWiiIg7iIM5rAYBouBB2yq5w6g+iATWGnqampZGhoqItiJSUP4YrcR9CQUh31JQ7iIk5hm7AXmPxdip5/dNIUCnduYBgm8fLly9tzc3NLwzlSqVTuW7NmzVphQkubwL179+xdXV3HKisrTVJxiJKwnuGHVM2XNjVrPCh3h8IT3+SLLKqrq+tKS0uvrly5UksJKvjsJSkpKd3r9TrkxCGSBxHwIoWXc7zAIOiIOOLUGULOsNHAMIzSZDJ9npmZeSIlJcWfTdPQm0ym8zqd7n257hGPnXxYyePv8py8mVb40ji1+UGZUmFh4Yetra1bzGbzteHh4SlKQNxu961wff7XS3Sau/w0c4VLQF7c8i8IAP4DcHKth/4Ur7MAAAAASUVORK5CYII=';\n\n/**\n * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.\n * @type {string}\n */\n// eslint-disable-next-line max-len\nconst blockIconURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAACXBIWXMAABYlAAAWJQFJUiTwAAAN+UlEQVR4Ae1ce2xT1xn/Tkhq4hqHJKRLDAlQGI+GUfFc14HaLmxuGd0ab93GgK6Vmm01y9BUsaU0RfyRFTakaRHq3So6jVapWEUxa9dRuU8x6IAGCoO6wa1KXiSQOE9jkjivO/2u7yWOuff6XvvekFb5SUdx7ON7v/vz9zrnO+cwnudpAokjZYK75DBBYJKYIDBJTBCYJCYITBJfOgIZYzbGWA5jLJ8xNm/z5s334a/4P1omYyzNsPt9WdIYxhiUId/j8azPz89fY7VaF6ampjqi+4TD4Qvd3d0f7t+/fx/HcTVE1M7z/EBS99VDoCikTWzWMdTgYSLqIaIQGs/zwzKyOaqrq1+aOnVqUUZGBqWnp5PFYqG0tDQaHh6mvr4+GhwcpFAoJLSenp4P9+7dW8Zx3Fme5zsTFSxVa0eovtPpnL1ly5YfzJgxw2WxWBYketNEAO25dOmSp7Ky8iBjrFbmoW12u70oOzubMjMzR32QkpJCVqtVeG2322lgYIACgcDK0tLS1+6+++4yxpiH5/krCQkGDYzXiCi3qqrKXVNTE7x48SLf0dHB9/X18WMF3Av3xL0hA2SBTNFyE9E8v9/PX758WbNUuCa+4/F4ymKvp7XF1UDGWK7H43m0sLBwZ05Ozg2/7lgApoiGe3d2dk5ZsWLFcx6Px84Y2xelOcHW1taXiOiRcDgsaJ2gljab0GDKscD1Jk2ahHd3ejwePOtenufb9TySKoEw26qqKhfIy83NFdT/ZiP6oauqqoKMsf2iObeuXr26vKKiwpOVlTXFbrfb8/LyFubk5KyBu1H68aVnGh4e3uZ2u08yxo7pCixKqgrX4XQ6l8JkoOrjDZAJskFGyBolt0UMcGhgJ19yP2rm3dDQwJ85c+akXlNWI9B++PDh38PvjFdANsgIWVUfksheUVHxfZDY3d0t+zT9/f2CP3S73feJWYZFC4FqJmxDtJ06daphRnuiluhrDqJbLcZcD7JBRiJ6Dj5Qrg9jDHfLhlm3tbUdslgsj8i5IvhI+MpNmzaVLlq0yCN+9wMiCvA8H1KSQY1AK3yHFP6ThfcTouePEaVNItrzI6LbpiR/TcgmplOyQmLE4XQ6C3fv3v0y+iEQIbggjZELKiB2+vTpxXl5ecUIRD6fr/nQoUNuxtjbPM/3yN1DLREWPsNNjUBNC9HgMFEKI2q5asglo2VTeo7sioqKP6anpwtBZObMmZSfny9LHokRG5+jn8PhoFtuucVRXFzMQdmVZPiyTyakIblGBqE3/QKZIBLDQafTmavUT/NIRA3XwkSlB4jaFD3FCPqHiJ5+Xb3PNBvRnoeN85UYyiUCmDHg9XoVn8wQDTxRR9RxjQjxLl4DgfH64Fq4pgEY6OjoONTe3q6bRPTH9zBmFsfgsjBEA4vmE/kuE30eiPzfN0B0WYyJGelEWQpx6FJXhNC0FKIcG9Fk0TUVZEWuaQBad+zYUbF79+6FjY2NC+DfpBGKGkBeY2MjBYPBC2VlZU9g1sZUAoFf3zvyGia9/u8RbRrmiSofvrG/1IeEaEn0/E+NkmQEmLVhjPmIaAMisRYSo8nbunXrBq/X6+N5PqzU35QgAt8177bI655+ovPNN/Z50xeJyIyIlswwQ4oI8PAgAWSAFJCjZM56ySMzo/DP7iJKTSEaGibi/jP6M2jfwbNEQzzRpBSin68yS4oItJCYCHlkJoEYcWSKvu9KkOjwxyOf7T9F1DsQ0b6CTGOS6niQIxEJNSVBHhnpA+Xwm28RPfOvSAL9t+NEy2cShcJEr5+P+EcQ+PT9ZkowGiBF8olIsMPhcBFmrnt7e4Voi4Chhzwym0BoYWFexAcODBGVvUbU3RshbxIjWrdobLQvGhKJXq+3xO12z1q3bt2aN9544x2O45A4Neshj8wmENjmJHr8ZaJQ/0iizcRk+fFvmn13eYgk1TLGWjiOwxQWKY1148H0oRwiMohiUYky8Oz3zL5zfIA0qSV6DdM18NWPiF4+FXnNogh84h9EW9cQ3TV7pC+ceVTVTNPooa5OGLLMYkgmE0fcqp8STCPwYhvRn94jauqKBBHJbOED4Q8xAtn1FtH8rxA9WUSUNthJiQy5Zs2aRX6/35usvBqqfrJQrAujmu/3+/3z5s3TJQgCxsEzRP9riuSAUsBYXhCJysCWVyP+EHkgiA0Hr9D9Xw3S/QuJJlvShIlSca4vASr0A5MG0Piuri68vnr69OmyjRs3aip1GqaByPNePRuZCMDwTSIuPY1oy32jTfWFDUQvniB67RzRta4rtDwvSN8tJBrnVT9ZGEIgpupf+C/RgGh90CpMEKwtJFq/XH5aCiOVb0zvpH+fipD3Baj6yUKNQIEOqLcWU4IPx9ANGue6k+iBQvX5PPi6lHD7dc0bD+RJgCxDQ0O0bNmyXU6n8wRjrFsxsKhUshznzp2r0VrSbAny/OcB7eU7VMdQBfuiV/3U8sAQohIcqxZgRHH7NO2/MlIVEitr4xVRVT+bkoiqBCKkIyp1dia8eEkRiHokVtbGK+JV/VQJhM17vd5ahPRAIICZCkMfU8r3xipVSQQaqn7qURjRB0u/ENIRleBYb0aaEQ+Btk4qfXLnqF7bn/oF3bFgjun3jjsWRh7kcrn2VVdXb25pablaW1uLXOl6xWo84LD36A1SHDl6akwk05QHgkTkQwjpsQsstSa/V65cGeUGMIbFMMwIyJFVfdpHj2zopVut6YbcQwmaZ2Ngzl6v9+zatWv/sHjx4u9UVlYKAzMUoOMB5HV1dV09cODAQ/NFOJ1OpxEPAKJ6evtueB/vnTrtM+IWqtA1nYXAwvM81GgAi3CUFi5KQKBobm4WyDt48OCm8vLy93me/xQNSmjEAxw5NqJ91vTJQpNQ/dE4I5AikwxWjuPWZGVlFWM9shLgI+vr6zHDcoHjuAdF8gwN5dd6eulUFEkrlhUKTQI+Q4AxE4lMqOasWrXqafg9uRREquiDvKamppe2bdv2kz179pwwmjyS8X3Lly4SWjSqT38s/2WDkMhkggUBJHbsCuIQndH6+/ubjxw58ju32/0eXKCeCUo9iDbfadMyr2sfzFjyi4ffOkZrnatNoi8xAoX1JjabrRhDnehZZBDn9/v3uFyuV+ItTEwWdQ3NVN9w+fpVViwdMV0QeeTYaeF1W1un0HdWgcNoEQQkYsJd27dvfxa+7dKlS9TQ0NBcV1d36OTJk5sLCwtXuVwujuf5WjPJIxnzvWf18uuvY834TZk80Sjo1kBxdPLp4sWLizBnKr4NewnqLQkmg1gCy575s+LVkOo8UWKOHAlV5cSAgGmadnG/WWCsyZPL/ZSAvmaNTHRroLho2xFdlGaMXRSDxZiQGJvf3bHgdtl+n1y4OOo70WZuFHQRCPKwaHvXrl1/sVqtK7EsYs6cOdsee+yxd8vLy3+Lir/ZJCKvi879Zhbk0fanfinb91dP7hSCCEXlhDnTjJ0M0WzCEnlYZ5eZmbly7ty5woLs2bNnYzxchPfxuaihpiE2r7tnlbJWRUdmMikn1ERgNHl2u33UIkUM5fA/3h8LEpHXRWPFskWKfWNNNva7RkDLZkNF8iTgf7yPFaDoh9VPZpnzvVEaZ7VOVjVJ5H4/fOjbRoswGnG2SIG8pSgu1dXV8UNDQ6pFGHyOfugv7mFT3C5FRHNRVBrLbbN6AdkgI2RNZK+cLvIkyJCYonB9XVW/mwHIBhkhayJVuWxEWyWzVYJkzjabbcGOHTvKUbBT6Kqr6nczANkgo9o2BzVWbEhVMGWllbzrF01JIXwPU16IMwrdTK36JQuxbHEVMiZEoNPpFKaaE62axSPd7KpfMoAskAmyQUa12STFKOz1egODg4PN9fX1Dqxb0TJ1LwG/HuYEg8Hgu5i9Ueo3Hqt+kB3k+Xy+p8QVWqrmoZbGtGOrJ3YrNjc3O6StonhAOTKx4h21D0xvYTYa6+0wOlHb5UMjBat9WMiDtShdXV1TvkjL21TPjcEpQJiB5jhOWM28ZMkSV0FBQTG2gsoBJU/MQp8/f/5tt9v9gZ5F20Yeq4LJXlhNPEjaRjELLEWz1eaYNR57AlWwYTs88iJsj5cDFgxhWz2218fbhq90ToN4zgF+obk4ykRvk7b2azn+JGabf754b9m0S3ceqPCAuTiYAQc0KAGCR53tIgklHQKh6RyCZFr0+QhNTU1x81f0OX78uMe0c2Ni0I7jknDiTzAYxNEiN3SA6VgsFmGV57lz50oDgcA7fr9f2ErQ0dFxlTH2ERE1mVUnwVwlY+z9lpaWB0tKSv4aDocXwOUo+VOkW6FQqJjjOI94gpG+FfsJmFk2TvqB6iudgCGZBzJ5aCsaRif4ztGjR19M9NfWKadl3bp1d+J+uG9bW5uiNra2tkojjpm675OgcLkSiXqGYjBvcWw5z2wC+RGf6uA4bqPP52v67LPPZImMGvPqliuhNdKxqUdvb+8UrJFRW6VAN2Epm+gmmhlj/ySiDzwez4/nz59f2tnZ6ZCOhEJqhvQFlUa1nFUJCS8ylxYcud3umpKSkl2hUGilJFRqaipNnjz5+hEjyLOwoa+7u1tKrk2t2MnIKmygYYzhBI5XqqqqHpgzZ84au93+dRwqgRQGlUaxzqMLSR/AKJ4Gme12uxeuX7/+0YyMjJWxORxGND09PTWNjY3vuFyu/UTUaFYQ0SizRcwOpIU0Q2JVUf8Pa6C/AZGZYuqSL+VkYh6H/3OQS46F7xvLNnGSeZKYOMU3SUwQmCQmCEwSEwQmiQkCkwER/R+aET3lwEIlXgAAAABJRU5ErkJggg==';\n\n/**\n * The url of the translate server.\n * @type {string}\n */\nconst serverURL = 'https://translate-service.scratch.mit.edu/';\n\n/**\n * How long to wait in ms before timing out requests to translate server.\n * @type {int}\n */\nconst serverTimeoutMs = 10000; // 10 seconds (chosen arbitrarily).\n\n/**\n * Class for the translate block in Scratch 3.0.\n * @constructor\n */\nclass Scratch3TranslateBlocks {\n constructor () {\n /**\n * Language code of the viewer, based on their locale.\n * @type {string}\n * @private\n */\n this._viewerLanguageCode = this.getViewerLanguageCode();\n\n /**\n * List of supported language name and language code pairs, for use in the block menu.\n * Filled in by getInfo so it is updated when the interface language changes.\n * @type {Array.>}\n * @private\n */\n this._supportedLanguages = [];\n\n /**\n * A randomly selected language code, for use as the default value in the language menu.\n * Properly filled in getInfo so it is updated when the interface languages changes.\n * @type {string}\n * @private\n */\n this._randomLanguageCode = 'en';\n\n\n /**\n * The result from the most recent translation.\n * @type {string}\n * @private\n */\n this._translateResult = '';\n\n /**\n * The language of the text most recently translated.\n * @type {string}\n * @private\n */\n this._lastLangTranslated = '';\n\n /**\n * The text most recently translated.\n * @type {string}\n * @private\n */\n this._lastTextTranslated = '';\n }\n\n /**\n * The key to load & store a target's translate state.\n * @return {string} The key.\n */\n static get STATE_KEY () {\n return 'Scratch.translate';\n }\n\n /**\n * @returns {object} metadata for this extension and its blocks.\n */\n getInfo () {\n this._supportedLanguages = this._getSupportedLanguages(this.getViewerLanguageCode());\n this._randomLanguageCode = this._supportedLanguages[\n Math.floor(Math.random() * this._supportedLanguages.length)].value;\n\n return {\n id: 'translate',\n name: formatMessage({\n id: 'translate.categoryName',\n default: 'Translate',\n description: 'Name of extension that adds translate blocks'\n }),\n blockIconURI: blockIconURI,\n menuIconURI: menuIconURI,\n blocks: [\n {\n opcode: 'getTranslate',\n text: formatMessage({\n id: 'translate.translateBlock',\n default: 'translate [WORDS] to [LANGUAGE]',\n description: 'translate some text to a different language'\n }),\n blockType: BlockType.REPORTER,\n arguments: {\n WORDS: {\n type: ArgumentType.STRING,\n defaultValue: formatMessage({\n id: 'translate.defaultTextToTranslate',\n default: 'hello',\n description: 'hello: the default text to translate'\n })\n },\n LANGUAGE: {\n type: ArgumentType.STRING,\n menu: 'languages',\n defaultValue: this._randomLanguageCode\n }\n }\n },\n {\n opcode: 'getViewerLanguage',\n text: formatMessage({\n id: 'translate.viewerLanguage',\n default: 'language',\n description: 'the languge of the project viewer'\n }),\n blockType: BlockType.REPORTER,\n arguments: {}\n }\n ],\n menus: {\n languages: {\n acceptReporters: true,\n items: this._supportedLanguages\n }\n }\n };\n }\n\n /**\n * Computes a list of language code and name pairs for the given language.\n * @param {string} code The language code to get the list of language pairs\n * @return {Array.>} An array of languge name and\n * language code pairs.\n * @private\n */\n _getSupportedLanguages (code) {\n return languageNames.menuMap[code].map(entry => {\n const obj = {text: entry.name, value: entry.code};\n return obj;\n });\n }\n /**\n * Get the human readable language value for the reporter block.\n * @return {string} the language name of the project viewer.\n */\n getViewerLanguage () {\n this._viewerLanguageCode = this.getViewerLanguageCode();\n const names = languageNames.menuMap[this._viewerLanguageCode];\n let langNameObj = names.find(obj => obj.code === this._viewerLanguageCode);\n\n // If we don't have a name entry yet, try looking it up via the Google langauge\n // code instead of Scratch's (e.g. for es-419 we look up es to get espanol)\n if (!langNameObj && languageNames.scratchToGoogleMap[this._viewerLanguageCode]) {\n const lookupCode = languageNames.scratchToGoogleMap[this._viewerLanguageCode];\n langNameObj = names.find(obj => obj.code === lookupCode);\n }\n\n let langName = this._viewerLanguageCode;\n if (langNameObj) {\n langName = langNameObj.name;\n }\n return langName;\n }\n\n /**\n * Get the viewer's language code.\n * @return {string} the language code.\n */\n getViewerLanguageCode () {\n const locale = formatMessage.setup().locale;\n const viewerLanguages = [locale].concat(navigator.languages);\n const languageKeys = Object.keys(languageNames.menuMap);\n // Return the first entry in viewerLanguages that matches\n // one of the available language keys.\n const languageCode = viewerLanguages.reduce((acc, lang) => {\n if (acc) {\n return acc;\n }\n if (languageKeys.indexOf(lang.toLowerCase()) > -1) {\n return lang;\n }\n return acc;\n }, '') || 'en';\n\n return languageCode.toLowerCase();\n }\n\n /**\n * Get a language code from a block argument. The arg can be a language code\n * or a language name, written in any language.\n * @param {object} arg A block argument.\n * @return {string} A language code.\n */\n getLanguageCodeFromArg (arg) {\n const languageArg = Cast.toString(arg).toLowerCase();\n // Check if the arg matches a language code in the menu.\n if (Object.prototype.hasOwnProperty.call(languageNames.menuMap, languageArg)) {\n return languageArg;\n }\n // Check for a dropped-in language name, and convert to a language code.\n if (Object.prototype.hasOwnProperty.call(languageNames.nameMap, languageArg)) {\n return languageNames.nameMap[languageArg];\n }\n\n // There are some languages we launched in the language menu that Scratch did not\n // end up launching in. In order to keep projects that may have had that menu item\n // working, check for those language codes and let them through.\n // Examples: 'ab', 'hi'.\n if (languageNames.previouslySupported.indexOf(languageArg) !== -1) {\n return languageArg;\n }\n // Default to English.\n return 'en';\n }\n\n /**\n * Translates the text in the translate block to the language specified in the menu.\n * @param {object} args - the block arguments.\n * @return {Promise} - a promise that resolves after the response from the translate server.\n */\n getTranslate (args) {\n // If the text contains only digits 0-9 and nothing else, return it without\n // making a request.\n if (/^\\d+$/.test(args.WORDS)) return Promise.resolve(args.WORDS);\n\n // Don't remake the request if we already have the value.\n if (this._lastTextTranslated === args.WORDS &&\n this._lastLangTranslated === args.LANGUAGE) {\n return this._translateResult;\n }\n\n const lang = this.getLanguageCodeFromArg(args.LANGUAGE);\n\n let urlBase = `${serverURL}translate?language=`;\n urlBase += lang;\n urlBase += '&text=';\n urlBase += encodeURIComponent(args.WORDS);\n\n const tempThis = this;\n const translatePromise = fetchWithTimeout(urlBase, {}, serverTimeoutMs)\n .then(response => response.text())\n .then(responseText => {\n const translated = JSON.parse(responseText).result;\n tempThis._translateResult = translated;\n // Cache what we just translated so we don't keep making the\n // same call over and over.\n tempThis._lastTextTranslated = args.WORDS;\n tempThis._lastLangTranslated = args.LANGUAGE;\n return translated;\n })\n .catch(err => {\n log.warn(`error fetching translate result! ${err}`);\n return '';\n });\n return translatePromise;\n }\n}\nmodule.exports = Scratch3TranslateBlocks;\n","const Runtime = require('../../engine/runtime');\n\nconst ArgumentType = require('../../extension-support/argument-type');\nconst BlockType = require('../../extension-support/block-type');\nconst Clone = require('../../util/clone');\nconst Cast = require('../../util/cast');\nconst formatMessage = require('format-message');\nconst Video = require('../../io/video');\n\nconst VideoMotion = require('./library');\n\n/**\n * Icon svg to be displayed in the blocks category menu, encoded as a data URI.\n * @type {string}\n */\n// eslint-disable-next-line max-len\nconst menuIconURI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjBweCIgaGVpZ2h0PSIyMHB4IiB2aWV3Qm94PSIwIDAgMjAgMjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjIgKDY3MTQ1KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5FeHRlbnNpb25zL1NvZnR3YXJlL1ZpZGVvLVNlbnNpbmctTWVudTwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxnIGlkPSJFeHRlbnNpb25zL1NvZnR3YXJlL1ZpZGVvLVNlbnNpbmctTWVudSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9InZpZGVvLW1vdGlvbiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDUuMDAwMDAwKSIgZmlsbC1ydWxlPSJub256ZXJvIj4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjMEVCRDhDIiBvcGFjaXR5PSIwLjI1IiBjeD0iMTYiIGN5PSI4IiByPSIyIj48L2NpcmNsZT4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjMEVCRDhDIiBvcGFjaXR5PSIwLjUiIGN4PSIxNiIgY3k9IjYiIHI9IjIiPjwvY2lyY2xlPgogICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsLUNvcHkiIGZpbGw9IiMwRUJEOEMiIG9wYWNpdHk9IjAuNzUiIGN4PSIxNiIgY3k9IjQiIHI9IjIiPjwvY2lyY2xlPgogICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsIiBmaWxsPSIjMEVCRDhDIiBjeD0iMTYiIGN5PSIyIiByPSIyIj48L2NpcmNsZT4KICAgICAgICAgICAgPHBhdGggZD0iTTExLjMzNTk3MzksMi4yMDk3ODgyNSBMOC4yNSw0LjIwOTk1NjQ5IEw4LjI1LDMuMDUgQzguMjUsMi4wNDQ4ODIyNyA3LjQ2ODU5MDMxLDEuMjUgNi41LDEuMjUgTDIuMDUsMS4yNSBDMS4wMzgwNzExOSwxLjI1IDAuMjUsMi4wMzgwNzExOSAwLjI1LDMuMDUgTDAuMjUsNyBDMC4yNSw3Ljk2MzY5OTM3IDEuMDQyMjQ5MTksOC43NTU5NDg1NiAyLjA1LDguOCBMNi41LDguOCBDNy40NTA4MzAwOSw4LjggOC4yNSw3Ljk3MzI3MjUgOC4yNSw3IEw4LjI1LDUuODU4NDUyNDEgTDguNjI4NjIzOTQsNi4wODU2MjY3NyBMMTEuNDI2Nzc2Nyw3Ljc3MzIyMzMgQzExLjQzNjg5NDMsNy43ODMzNDA5MSAxMS40NzU3NjU1LDcuOCAxMS41LDcuOCBDMTEuNjMzNDkzMiw3LjggMTEuNzUsNy42OTEyNjAzNCAxMS43NSw3LjU1IEwxMS43NSwyLjQgQzExLjc1LDIuNDE4MzgyNjkgMTEuNzIxOTAyOSwyLjM1MjgyMjgyIDExLjY4NTYyNjgsMi4yNzg2MjM5NCBDMTEuNjEyOTUyOCwyLjE1NzUwMDY5IDExLjQ3MDc5NjgsMi4xMjkwNjk1IDExLjMzNTk3MzksMi4yMDk3ODgyNSBaIiBpZD0idmlkZW9fMzdfIiBzdHJva2Utb3BhY2l0eT0iMC4xNSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjAuNSIgZmlsbD0iIzRENEQ0RCI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+';\n\n/**\n * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.\n * @type {string}\n */\n// eslint-disable-next-line max-len\nconst blockIconURI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSIwIDAgNDAgNDAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjIgKDY3MTQ1KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5FeHRlbnNpb25zL1NvZnR3YXJlL1ZpZGVvLVNlbnNpbmctQmxvY2s8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZyBpZD0iRXh0ZW5zaW9ucy9Tb2Z0d2FyZS9WaWRlby1TZW5zaW5nLUJsb2NrIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2Utb3BhY2l0eT0iMC4xNSI+CiAgICAgICAgPGcgaWQ9InZpZGVvLW1vdGlvbiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDEwLjAwMDAwMCkiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSIjMDAwMDAwIj4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjRkZGRkZGIiBvcGFjaXR5PSIwLjI1IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGN4PSIzMiIgY3k9IjE2IiByPSI0LjUiPjwvY2lyY2xlPgogICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsLUNvcHkiIGZpbGw9IiNGRkZGRkYiIG9wYWNpdHk9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjeD0iMzIiIGN5PSIxMiIgcj0iNC41Ij48L2NpcmNsZT4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjRkZGRkZGIiBvcGFjaXR5PSIwLjc1IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGN4PSIzMiIgY3k9IjgiIHI9IjQuNSI+PC9jaXJjbGU+CiAgICAgICAgICAgIDxjaXJjbGUgaWQ9Ik92YWwiIGZpbGw9IiNGRkZGRkYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgY3g9IjMyIiBjeT0iNCIgcj0iNC41Ij48L2NpcmNsZT4KICAgICAgICAgICAgPHBhdGggZD0iTTIyLjY3MTk0NzcsNC40MTk1NzY0OSBMMTYuNSw4LjQxOTkxMjk4IEwxNi41LDYuMSBDMTYuNSw0LjA4OTc2NDU0IDE0LjkzNzE4MDYsMi41IDEzLDIuNSBMNC4xLDIuNSBDMi4wNzYxNDIzNywyLjUgMC41LDQuMDc2MTQyMzcgMC41LDYuMSBMMC41LDE0IEMwLjUsMTUuOTI3Mzk4NyAyLjA4NDQ5ODM5LDE3LjUxMTg5NzEgNC4xLDE3LjYgTDEzLDE3LjYgQzE0LjkwMTY2MDIsMTcuNiAxNi41LDE1Ljk0NjU0NSAxNi41LDE0IEwxNi41LDExLjcxNjkwNDggTDIyLjc1NzI0NzksMTUuNDcxMjUzNSBMMjIuODUzNTUzNCwxNS41NDY0NDY2IEMyMi44NzM3ODg2LDE1LjU2NjY4MTggMjIuOTUxNTMxLDE1LjYgMjMsMTUuNiBDMjMuMjY2OTg2NSwxNS42IDIzLjUsMTUuMzgyNTIwNyAyMy41LDE1LjEgTDIzLjUsNC44IEMyMy41LDQuODM2NzY1MzggMjMuNDQzODA1OCw0LjcwNTY0NTYzIDIzLjM3MTI1MzUsNC41NTcyNDc4OCBDMjMuMjI1OTA1Niw0LjMxNTAwMTM5IDIyLjk0MTU5MzcsNC4yNTgxMzg5OSAyMi42NzE5NDc3LDQuNDE5NTc2NDkgWiIgaWQ9InZpZGVvXzM3XyIgZmlsbD0iIzRENEQ0RCI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+';\n\n/**\n * Sensor attribute video sensor block should report.\n * @readonly\n * @enum {string}\n */\nconst SensingAttribute = {\n /** The amount of motion. */\n MOTION: 'motion',\n\n /** The direction of the motion. */\n DIRECTION: 'direction'\n};\n\n/**\n * Subject video sensor block should report for.\n * @readonly\n * @enum {string}\n */\nconst SensingSubject = {\n /** The sensor traits of the whole stage. */\n STAGE: 'Stage',\n\n /** The senosr traits of the area overlapped by this sprite. */\n SPRITE: 'this sprite'\n};\n\n/**\n * States the video sensing activity can be set to.\n * @readonly\n * @enum {string}\n */\nconst VideoState = {\n /** Video turned off. */\n OFF: 'off',\n\n /** Video turned on with default y axis mirroring. */\n ON: 'on',\n\n /** Video turned on without default y axis mirroring. */\n ON_FLIPPED: 'on-flipped'\n};\n\n/**\n * Class for the motion-related blocks in Scratch 3.0\n * @param {Runtime} runtime - the runtime instantiating this block package.\n * @constructor\n */\nclass Scratch3VideoSensingBlocks {\n constructor (runtime) {\n /**\n * The runtime instantiating this block package.\n * @type {Runtime}\n */\n this.runtime = runtime;\n\n /**\n * The motion detection algoritm used to power the motion amount and\n * direction values.\n * @type {VideoMotion}\n */\n this.detect = new VideoMotion();\n\n /**\n * The last millisecond epoch timestamp that the video stream was\n * analyzed.\n * @type {number}\n */\n this._lastUpdate = null;\n\n /**\n * A flag to determine if this extension has been installed in a project.\n * It is set to false the first time getInfo is run.\n * @type {boolean}\n */\n this.firstInstall = true;\n\n if (this.runtime.ioDevices) {\n // Configure the video device with values from globally stored locations.\n this.runtime.on(Runtime.PROJECT_LOADED, this.updateVideoDisplay.bind(this));\n\n // Clear target motion state values when the project starts.\n this.runtime.on(Runtime.PROJECT_RUN_START, this.reset.bind(this));\n\n // Kick off looping the analysis logic.\n this._loop();\n }\n }\n\n /**\n * After analyzing a frame the amount of milliseconds until another frame\n * is analyzed.\n * @type {number}\n */\n static get INTERVAL () {\n return 33;\n }\n\n /**\n * Dimensions the video stream is analyzed at after its rendered to the\n * sample canvas.\n * @type {Array.}\n */\n static get DIMENSIONS () {\n return [480, 360];\n }\n\n /**\n * The key to load & store a target's motion-related state.\n * @type {string}\n */\n static get STATE_KEY () {\n return 'Scratch.videoSensing';\n }\n\n /**\n * The default motion-related state, to be used when a target has no existing motion state.\n * @type {MotionState}\n */\n static get DEFAULT_MOTION_STATE () {\n return {\n motionFrameNumber: 0,\n motionAmount: 0,\n motionDirection: 0\n };\n }\n\n /**\n * The transparency setting of the video preview stored in a value\n * accessible by any object connected to the virtual machine.\n * @type {number}\n */\n get globalVideoTransparency () {\n const stage = this.runtime.getTargetForStage();\n if (stage) {\n return stage.videoTransparency;\n }\n return 50;\n }\n\n set globalVideoTransparency (transparency) {\n const stage = this.runtime.getTargetForStage();\n if (stage) {\n stage.videoTransparency = transparency;\n }\n }\n\n /**\n * The video state of the video preview stored in a value accessible by any\n * object connected to the virtual machine.\n * @type {number}\n */\n get globalVideoState () {\n const stage = this.runtime.getTargetForStage();\n if (stage) {\n return stage.videoState;\n }\n // Though the default value for the stage is normally 'on', we need to default\n // to 'off' here to prevent the video device from briefly activating\n // while waiting for stage targets to be installed that say it should be off\n return VideoState.OFF;\n }\n\n set globalVideoState (state) {\n const stage = this.runtime.getTargetForStage();\n if (stage) {\n stage.videoState = state;\n }\n }\n\n /**\n * Get the latest values for video transparency and state,\n * and set the video device to use them.\n */\n updateVideoDisplay () {\n this.setVideoTransparency({\n TRANSPARENCY: this.globalVideoTransparency\n });\n this.videoToggle({\n VIDEO_STATE: this.globalVideoState\n });\n }\n\n /**\n * Reset the extension's data motion detection data. This will clear out\n * for example old frames, so the first analyzed frame will not be compared\n * against a frame from before reset was called.\n */\n reset () {\n this.detect.reset();\n\n const targets = this.runtime.targets;\n for (let i = 0; i < targets.length; i++) {\n const state = targets[i].getCustomState(Scratch3VideoSensingBlocks.STATE_KEY);\n if (state) {\n state.motionAmount = 0;\n state.motionDirection = 0;\n }\n }\n }\n\n /**\n * Occasionally step a loop to sample the video, stamp it to the preview\n * skin, and add a TypedArray copy of the canvas's pixel data.\n * @private\n */\n _loop () {\n const loopTime = Math.max(this.runtime.currentStepTime, Scratch3VideoSensingBlocks.INTERVAL);\n this._loopInterval = setTimeout(this._loop.bind(this), loopTime);\n\n // Add frame to detector\n const time = Date.now();\n if (this._lastUpdate === null) {\n this._lastUpdate = time;\n }\n const offset = time - this._lastUpdate;\n if (offset > Scratch3VideoSensingBlocks.INTERVAL) {\n const frame = this.runtime.ioDevices.video.getFrame({\n format: Video.FORMAT_IMAGE_DATA,\n dimensions: Scratch3VideoSensingBlocks.DIMENSIONS\n });\n if (frame) {\n this._lastUpdate = time;\n this.detect.addFrame(frame.data);\n }\n }\n }\n\n /**\n * Stop the video sampling loop. Only used for testing.\n */\n _stopLoop () {\n clearTimeout(this._loopInterval);\n }\n\n /**\n * Create data for a menu in scratch-blocks format, consisting of an array\n * of objects with text and value properties. The text is a translated\n * string, and the value is one-indexed.\n * @param {object[]} info - An array of info objects each having a name\n * property.\n * @return {array} - An array of objects with text and value properties.\n * @private\n */\n _buildMenu (info) {\n return info.map((entry, index) => {\n const obj = {};\n obj.text = entry.name;\n obj.value = entry.value || String(index + 1);\n return obj;\n });\n }\n\n /**\n * @param {Target} target - collect motion state for this target.\n * @returns {MotionState} the mutable motion state associated with that\n * target. This will be created if necessary.\n * @private\n */\n _getMotionState (target) {\n let motionState = target.getCustomState(Scratch3VideoSensingBlocks.STATE_KEY);\n if (!motionState) {\n motionState = Clone.simple(Scratch3VideoSensingBlocks.DEFAULT_MOTION_STATE);\n target.setCustomState(Scratch3VideoSensingBlocks.STATE_KEY, motionState);\n }\n return motionState;\n }\n\n static get SensingAttribute () {\n return SensingAttribute;\n }\n\n /**\n * An array of choices of whether a reporter should return the frame's\n * motion amount or direction.\n * @type {object[]}\n * @param {string} name - the translatable name to display in sensor\n * attribute menu\n * @param {string} value - the serializable value of the attribute\n */\n get ATTRIBUTE_INFO () {\n return [\n {\n name: formatMessage({\n id: 'videoSensing.motion',\n default: 'motion',\n description: 'Attribute for the \"video [ATTRIBUTE] on [SUBJECT]\" block'\n }),\n value: SensingAttribute.MOTION\n },\n {\n name: formatMessage({\n id: 'videoSensing.direction',\n default: 'direction',\n description: 'Attribute for the \"video [ATTRIBUTE] on [SUBJECT]\" block'\n }),\n value: SensingAttribute.DIRECTION\n }\n ];\n }\n\n static get SensingSubject () {\n return SensingSubject;\n }\n\n /**\n * An array of info about the subject choices.\n * @type {object[]}\n * @param {string} name - the translatable name to display in the subject menu\n * @param {string} value - the serializable value of the subject\n */\n get SUBJECT_INFO () {\n return [\n {\n name: formatMessage({\n id: 'videoSensing.sprite',\n default: 'sprite',\n description: 'Subject for the \"video [ATTRIBUTE] on [SUBJECT]\" block'\n }),\n value: SensingSubject.SPRITE\n },\n {\n name: formatMessage({\n id: 'videoSensing.stage',\n default: 'stage',\n description: 'Subject for the \"video [ATTRIBUTE] on [SUBJECT]\" block'\n }),\n value: SensingSubject.STAGE\n }\n ];\n }\n\n /**\n * States the video sensing activity can be set to.\n * @readonly\n * @enum {string}\n */\n static get VideoState () {\n return VideoState;\n }\n\n /**\n * An array of info on video state options for the \"turn video [STATE]\" block.\n * @type {object[]}\n * @param {string} name - the translatable name to display in the video state menu\n * @param {string} value - the serializable value stored in the block\n */\n get VIDEO_STATE_INFO () {\n return [\n {\n name: formatMessage({\n id: 'videoSensing.off',\n default: 'off',\n description: 'Option for the \"turn video [STATE]\" block'\n }),\n value: VideoState.OFF\n },\n {\n name: formatMessage({\n id: 'videoSensing.on',\n default: 'on',\n description: 'Option for the \"turn video [STATE]\" block'\n }),\n value: VideoState.ON\n },\n {\n name: formatMessage({\n id: 'videoSensing.onFlipped',\n default: 'on flipped',\n description: 'Option for the \"turn video [STATE]\" block that causes the video to be flipped' +\n ' horizontally (reversed as in a mirror)'\n }),\n value: VideoState.ON_FLIPPED\n }\n ];\n }\n\n /**\n * @returns {object} metadata for this extension and its blocks.\n */\n getInfo () {\n // Set the video display properties to defaults the first time\n // getInfo is run. This turns on the video device when it is\n // first added to a project, and is overwritten by a PROJECT_LOADED\n // event listener that later calls updateVideoDisplay\n if (this.firstInstall) {\n this.globalVideoState = VideoState.ON;\n this.globalVideoTransparency = 50;\n this.updateVideoDisplay();\n this.firstInstall = false;\n }\n\n // Return extension definition\n return {\n id: 'videoSensing',\n name: formatMessage({\n id: 'videoSensing.categoryName',\n default: 'Video Sensing',\n description: 'Label for the video sensing extension category'\n }),\n blockIconURI: blockIconURI,\n menuIconURI: menuIconURI,\n blocks: [\n {\n // @todo this hat needs to be set itself to restart existing\n // threads like Scratch 2's behaviour.\n opcode: 'whenMotionGreaterThan',\n text: formatMessage({\n id: 'videoSensing.whenMotionGreaterThan',\n default: 'when video motion > [REFERENCE]',\n description: 'Event that triggers when the amount of motion is greater than [REFERENCE]'\n }),\n blockType: BlockType.HAT,\n arguments: {\n REFERENCE: {\n type: ArgumentType.NUMBER,\n defaultValue: 10\n }\n }\n },\n {\n opcode: 'videoOn',\n blockType: BlockType.REPORTER,\n text: formatMessage({\n id: 'videoSensing.videoOn',\n default: 'video [ATTRIBUTE] on [SUBJECT]',\n description: 'Reporter that returns the amount of [ATTRIBUTE] for the selected [SUBJECT]'\n }),\n arguments: {\n ATTRIBUTE: {\n type: ArgumentType.NUMBER,\n menu: 'ATTRIBUTE',\n defaultValue: SensingAttribute.MOTION\n },\n SUBJECT: {\n type: ArgumentType.NUMBER,\n menu: 'SUBJECT',\n defaultValue: SensingSubject.SPRITE\n }\n }\n },\n {\n opcode: 'videoToggle',\n text: formatMessage({\n id: 'videoSensing.videoToggle',\n default: 'turn video [VIDEO_STATE]',\n description: 'Controls display of the video preview layer'\n }),\n arguments: {\n VIDEO_STATE: {\n type: ArgumentType.NUMBER,\n menu: 'VIDEO_STATE',\n defaultValue: VideoState.ON\n }\n }\n },\n {\n opcode: 'setVideoTransparency',\n text: formatMessage({\n id: 'videoSensing.setVideoTransparency',\n default: 'set video transparency to [TRANSPARENCY]',\n description: 'Controls transparency of the video preview layer'\n }),\n arguments: {\n TRANSPARENCY: {\n type: ArgumentType.NUMBER,\n defaultValue: 50\n }\n }\n }\n ],\n menus: {\n ATTRIBUTE: {\n acceptReporters: true,\n items: this._buildMenu(this.ATTRIBUTE_INFO)\n },\n SUBJECT: {\n acceptReporters: true,\n items: this._buildMenu(this.SUBJECT_INFO)\n },\n VIDEO_STATE: {\n acceptReporters: true,\n items: this._buildMenu(this.VIDEO_STATE_INFO)\n }\n }\n };\n }\n\n /**\n * Analyze a part of the frame that a target overlaps.\n * @param {Target} target - a target to determine where to analyze\n * @returns {MotionState} the motion state for the given target\n */\n _analyzeLocalMotion (target) {\n const drawable = this.runtime.renderer._allDrawables[target.drawableID];\n const state = this._getMotionState(target);\n this.detect.getLocalMotion(drawable, state);\n return state;\n }\n\n /**\n * A scratch reporter block handle that analyzes the last two frames and\n * depending on the arguments, returns the motion or direction for the\n * whole stage or just the target sprite.\n * @param {object} args - the block arguments\n * @param {BlockUtility} util - the block utility\n * @returns {number} the motion amount or direction of the stage or sprite\n */\n videoOn (args, util) {\n this.detect.analyzeFrame();\n\n let state = this.detect;\n if (args.SUBJECT === SensingSubject.SPRITE) {\n state = this._analyzeLocalMotion(util.target);\n }\n\n if (args.ATTRIBUTE === SensingAttribute.MOTION) {\n return state.motionAmount;\n }\n return state.motionDirection;\n }\n\n /**\n * A scratch hat block edge handle that analyzes the last two frames where\n * the target sprite overlaps and if it has more motion than the given\n * reference value.\n * @param {object} args - the block arguments\n * @param {BlockUtility} util - the block utility\n * @returns {boolean} true if the sprite overlaps more motion than the\n * reference\n */\n whenMotionGreaterThan (args, util) {\n this.detect.analyzeFrame();\n const state = this._analyzeLocalMotion(util.target);\n return state.motionAmount > Number(args.REFERENCE);\n }\n\n /**\n * A scratch command block handle that configures the video state from\n * passed arguments.\n * @param {object} args - the block arguments\n * @param {VideoState} args.VIDEO_STATE - the video state to set the device to\n */\n videoToggle (args) {\n const state = args.VIDEO_STATE;\n this.globalVideoState = state;\n if (state === VideoState.OFF) {\n this.runtime.ioDevices.video.disableVideo();\n } else {\n this.runtime.ioDevices.video.enableVideo();\n // Mirror if state is ON. Do not mirror if state is ON_FLIPPED.\n this.runtime.ioDevices.video.mirror = state === VideoState.ON;\n }\n }\n\n /**\n * A scratch command block handle that configures the video preview's\n * transparency from passed arguments.\n * @param {object} args - the block arguments\n * @param {number} args.TRANSPARENCY - the transparency to set the video\n * preview to\n */\n setVideoTransparency (args) {\n const transparency = Cast.toNumber(args.TRANSPARENCY);\n this.globalVideoTransparency = transparency;\n this.runtime.ioDevices.video.setPreviewGhost(transparency);\n }\n}\n\nmodule.exports = Scratch3VideoSensingBlocks;\n","const ArgumentType = require('../../extension-support/argument-type');\nconst BlockType = require('../../extension-support/block-type');\nconst Cast = require('../../util/cast');\nconst formatMessage = require('format-message');\nconst color = require('../../util/color');\nconst BLE = require('../../io/ble');\nconst Base64Util = require('../../util/base64-util');\nconst MathUtil = require('../../util/math-util');\nconst RateLimiter = require('../../util/rateLimiter.js');\nconst log = require('../../util/log');\n\n/**\n * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.\n * @type {string}\n */\n// eslint-disable-next-line max-len\nconst iconURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAACXBIWXMAABYlAAAWJQFJUiTwAAAF8klEQVR4Ae2cbWxTVRjH/7ctbVc2tyEMNpWBk0VIkLcEjSAQgglTE5HEaKqJi1E/mbCP/dJA0kQbvzgTQ0Ki2T7V6AeYGoEPLJmGKPiyzZDwEpYJCHSbQIcbdLvres1zOa13Xbvdu2eTDp9fst329Lnn5XfPPfece7tphmFAmDkuccdDBDIRgUxEIBMRyEQEMhGBTEQgExHIRAQyEYFMRCATEchEBDIRgUxEIBMRyEQEMhGBTEQgExHIxMPNIByNVQBoBUDb7kgo2KTS9wBoUmFNkVCwW6U3A1gP4JJKHwxHY/S+WcW2RkLBVhV7AMAOAIMAGlWstbyOSCh4QMU2Uoy1PBVL+a7IqZu1vOZIKNg20/azBarGvKxebw9HY22RULADwBFLTBcATQnZl4lVEimN4ssteXQrQfstebQpmW1q30xshyqvxRLbofYnYW9ZYgeV8C5LLOWlzbTxM3ouHI7GPgSwWx3Z0syBSBku6IYnlTbM+uQenJQaMnKHDaqAFnDrcCFbl3G1defEjas0a4N/Vz10OybyvapfrSX1sjpo+WIz0ME7QL3djgtHPTAcjb2mepw/b2ZaGh5NL5RnofR8R99dIC5fHusK5JsrCUpm7TSx21XvbcwTNwnbAsPR2GcA3qaG+H0LsHlDPZ7fca/ujZ+cRW9/Em5vCXzlNVhQUjFpf/3OTSRvXkKJz43Xt1bh1S1LUeq/5+njQ9/iVmLIfL1ieRU2b1iFtavztXNu6TrTi8PfnYI67WdPoOp5przV9Y8iuHdb9rOW9uumPI+vDIElddBckztPOqVn5X36Xj1WVQeynx1sOWbK83jc2PviM/dFXIYNax9H55leXLoyYHsfWwI14JCRRx7x5ckBU1oheYQ+1G9u39lVM0Hej7+cR7w/Yb7e9+5LqChfaLvixcK088BwNNZkAOV02ubK6+odwt3RcfOULSSPGEveG48bNj08If3kqXPmdtO6unkpDzYn0u/TLxrzcumJJ80Ut79sygzoFF6/siw75mUYupOEpmnY0/A0pw33FTsCa+hX5oJhZXgkZb5zub2O20CnL7EwkPeCPm+wI7CEBvi5wuOZ36tJW7X3uGXJXAgxk8P4eNpRPEvgskqfuR0Z/BNGejxvDM3/5gs0pboWv+motqybCc+tqUCzz43kaBJ/X+2eMjZ3ClNsjIzo5ioknXZ2b4AlkKYltLJoaY9jOJm/B0KJbtg4c4F/XOmH3+dF9dLKbBo1OD6QQGV56YQ55ODtO0jcHkZ1VSX8/n9nB9S7RkZ1rFy+NG8ZR9s70TeQQKDEh7vJUdt1Y9/OopXFB2/WcbMpyOexE9mlFS21aLlHMmKHfzBl0QT/hV2bzM9oLXv0xG8YGR0zpdLEn6RT2k+/XjDzoLX2G3u3TZBLUyral/Z5qCyAK1f/sl2/or+IWNel1Eji3MWrpjyCZHWqdNrSe6ieSHFERl4mP+q5GehgHGvvRGal5XI5uzU47f3A/R99YTgdF2wXrmkolr9ToZ5NvTjT4yOhoC2T057CJM/r9WDxoqmXa07R9THcuDVcMO8bt4ag6ynULKvkFjWBTLl0ugZKvNlyqLeSQKfYGgOpgXt2b5zVhlzrS+Dr451YvKg0b95txztxvS8xZ+VuXFuLJ5+oNgV+9c3PuHDxGs6cu+w4v//9RJo6x5bN9UgbBo4cPY1U6j+cSD8orFvzGFYuX4KxsRQGbth6FCICc9m5dY05HtN46AQRqPB5PWjY+ZT5RnMwkxGBFh5ZVmle9Z3MrGbjwfqccrC1vajrV7QCaVCfS6qrJj96nQlFK5CujPRT7MgYyEQEMhGBTGwJpAW4kJ9pBbo0zbx70X7y7AOv8HxP3LyB4YTpb2cZBt2iqL3QEwf9zDbX+waLca439QMeC7a+YBmOxugLiM/OTt2yaOoMoO+H6LOcNwf6xusrthsh/7mIh1yFmYhAJiKQiQhkIgKZiEAmIpCJCGQiApmIQCYikIkIZCICmYhAJiKQiQhkIgKZiEAmIpCJCGQiAjkA+AeOwQKMcWZqHgAAAABJRU5ErkJggg==';\n\n/**\n * A list of WeDo 2.0 BLE service UUIDs.\n * @enum\n */\nconst BLEService = {\n DEVICE_SERVICE: '00001523-1212-efde-1523-785feabcd123',\n IO_SERVICE: '00004f0e-1212-efde-1523-785feabcd123'\n};\n\n/**\n * A list of WeDo 2.0 BLE characteristic UUIDs.\n *\n * Characteristics on DEVICE_SERVICE:\n * - ATTACHED_IO\n *\n * Characteristics on IO_SERVICE:\n * - INPUT_VALUES\n * - INPUT_COMMAND\n * - OUTPUT_COMMAND\n *\n * @enum\n */\nconst BLECharacteristic = {\n ATTACHED_IO: '00001527-1212-efde-1523-785feabcd123',\n LOW_VOLTAGE_ALERT: '00001528-1212-efde-1523-785feabcd123',\n INPUT_VALUES: '00001560-1212-efde-1523-785feabcd123',\n INPUT_COMMAND: '00001563-1212-efde-1523-785feabcd123',\n OUTPUT_COMMAND: '00001565-1212-efde-1523-785feabcd123'\n};\n\n/**\n * A time interval to wait (in milliseconds) in between battery check calls.\n * @type {number}\n */\nconst BLEBatteryCheckInterval = 5000;\n\n/**\n * A time interval to wait (in milliseconds) while a block that sends a BLE message is running.\n * @type {number}\n */\nconst BLESendInterval = 100;\n\n/**\n * A maximum number of BLE message sends per second, to be enforced by the rate limiter.\n * @type {number}\n */\nconst BLESendRateMax = 20;\n\n/**\n * Enum for WeDo 2.0 sensor and output types.\n * @readonly\n * @enum {number}\n */\nconst WeDo2Device = {\n MOTOR: 1,\n PIEZO: 22,\n LED: 23,\n TILT: 34,\n DISTANCE: 35\n};\n\n/**\n * Enum for connection/port ids assigned to internal WeDo 2.0 output devices.\n * @readonly\n * @enum {number}\n */\n// TODO: Check for these more accurately at startup?\nconst WeDo2ConnectID = {\n LED: 6,\n PIEZO: 5\n};\n\n/**\n * Enum for ids for various output commands on the WeDo 2.0.\n * @readonly\n * @enum {number}\n */\nconst WeDo2Command = {\n MOTOR_POWER: 1,\n PLAY_TONE: 2,\n STOP_TONE: 3,\n WRITE_RGB: 4,\n SET_VOLUME: 255\n};\n\n/**\n * Enum for modes for input sensors on the WeDo 2.0.\n * @enum {number}\n */\nconst WeDo2Mode = {\n TILT: 0, // angle\n DISTANCE: 0, // detect\n LED: 1 // RGB\n};\n\n/**\n * Enum for units for input sensors on the WeDo 2.0.\n *\n * 0 = raw\n * 1 = percent\n *\n * @enum {number}\n */\nconst WeDo2Unit = {\n TILT: 0,\n DISTANCE: 1,\n LED: 0\n};\n\n/**\n * Manage power, direction, and timers for one WeDo 2.0 motor.\n */\nclass WeDo2Motor {\n /**\n * Construct a WeDo 2.0 Motor instance.\n * @param {WeDo2} parent - the WeDo 2.0 peripheral which owns this motor.\n * @param {int} index - the zero-based index of this motor on its parent peripheral.\n */\n constructor (parent, index) {\n /**\n * The WeDo 2.0 peripheral which owns this motor.\n * @type {WeDo2}\n * @private\n */\n this._parent = parent;\n\n /**\n * The zero-based index of this motor on its parent peripheral.\n * @type {int}\n * @private\n */\n this._index = index;\n\n /**\n * This motor's current direction: 1 for \"this way\" or -1 for \"that way\"\n * @type {number}\n * @private\n */\n this._direction = 1;\n\n /**\n * This motor's current power level, in the range [0,100].\n * @type {number}\n * @private\n */\n this._power = 100;\n\n /**\n * Is this motor currently moving?\n * @type {boolean}\n * @private\n */\n this._isOn = false;\n\n /**\n * If the motor has been turned on or is actively braking for a specific duration, this is the timeout ID for\n * the end-of-action handler. Cancel this when changing plans.\n * @type {Object}\n * @private\n */\n this._pendingTimeoutId = null;\n\n /**\n * The starting time for the pending timeout.\n * @type {Object}\n * @private\n */\n this._pendingTimeoutStartTime = null;\n\n /**\n * The delay/duration of the pending timeout.\n * @type {Object}\n * @private\n */\n this._pendingTimeoutDelay = null;\n\n this.startBraking = this.startBraking.bind(this);\n this.turnOff = this.turnOff.bind(this);\n }\n\n /**\n * @return {number} - the duration of active braking after a call to startBraking(). Afterward, turn the motor off.\n * @constructor\n */\n static get BRAKE_TIME_MS () {\n return 1000;\n }\n\n /**\n * @return {int} - this motor's current direction: 1 for \"this way\" or -1 for \"that way\"\n */\n get direction () {\n return this._direction;\n }\n\n /**\n * @param {int} value - this motor's new direction: 1 for \"this way\" or -1 for \"that way\"\n */\n set direction (value) {\n if (value < 0) {\n this._direction = -1;\n } else {\n this._direction = 1;\n }\n }\n\n /**\n * @return {int} - this motor's current power level, in the range [0,100].\n */\n get power () {\n return this._power;\n }\n\n /**\n * @param {int} value - this motor's new power level, in the range [0,100].\n */\n set power (value) {\n const p = Math.max(0, Math.min(value, 100));\n // Lego Wedo 2.0 hub only turns motors at power range [30 - 100], so\n // map value from [0 - 100] to [30 - 100].\n if (p === 0) {\n this._power = 0;\n } else {\n const delta = 100 / p;\n this._power = 30 + (70 / delta);\n }\n }\n\n /**\n * @return {boolean} - true if this motor is currently moving, false if this motor is off or braking.\n */\n get isOn () {\n return this._isOn;\n }\n\n /**\n * @return {boolean} - time, in milliseconds, of when the pending timeout began.\n */\n get pendingTimeoutStartTime () {\n return this._pendingTimeoutStartTime;\n }\n\n /**\n * @return {boolean} - delay, in milliseconds, of the pending timeout.\n */\n get pendingTimeoutDelay () {\n return this._pendingTimeoutDelay;\n }\n\n /**\n * Turn this motor on indefinitely.\n */\n turnOn () {\n if (this._power === 0) return;\n\n const cmd = this._parent.generateOutputCommand(\n this._index + 1,\n WeDo2Command.MOTOR_POWER,\n [this._power * this._direction] // power in range 0-100\n );\n\n this._parent.send(BLECharacteristic.OUTPUT_COMMAND, cmd);\n\n this._isOn = true;\n this._clearTimeout();\n }\n\n /**\n * Turn this motor on for a specific duration.\n * @param {number} milliseconds - run the motor for this long.\n */\n turnOnFor (milliseconds) {\n if (this._power === 0) return;\n\n milliseconds = Math.max(0, milliseconds);\n this.turnOn();\n this._setNewTimeout(this.startBraking, milliseconds);\n }\n\n /**\n * Start active braking on this motor. After a short time, the motor will turn off.\n */\n startBraking () {\n if (this._power === 0) return;\n\n const cmd = this._parent.generateOutputCommand(\n this._index + 1,\n WeDo2Command.MOTOR_POWER,\n [127] // 127 = break\n );\n\n this._parent.send(BLECharacteristic.OUTPUT_COMMAND, cmd);\n\n this._isOn = false;\n this._setNewTimeout(this.turnOff, WeDo2Motor.BRAKE_TIME_MS);\n }\n\n /**\n * Turn this motor off.\n * @param {boolean} [useLimiter=true] - if true, use the rate limiter\n */\n turnOff (useLimiter = true) {\n if (this._power === 0) return;\n\n const cmd = this._parent.generateOutputCommand(\n this._index + 1,\n WeDo2Command.MOTOR_POWER,\n [0] // 0 = stop\n );\n\n this._parent.send(BLECharacteristic.OUTPUT_COMMAND, cmd, useLimiter);\n\n this._isOn = false;\n }\n\n /**\n * Clear the motor action timeout, if any. Safe to call even when there is no pending timeout.\n * @private\n */\n _clearTimeout () {\n if (this._pendingTimeoutId !== null) {\n clearTimeout(this._pendingTimeoutId);\n this._pendingTimeoutId = null;\n this._pendingTimeoutStartTime = null;\n this._pendingTimeoutDelay = null;\n }\n }\n\n /**\n * Set a new motor action timeout, after clearing an existing one if necessary.\n * @param {Function} callback - to be called at the end of the timeout.\n * @param {int} delay - wait this many milliseconds before calling the callback.\n * @private\n */\n _setNewTimeout (callback, delay) {\n this._clearTimeout();\n const timeoutID = setTimeout(() => {\n if (this._pendingTimeoutId === timeoutID) {\n this._pendingTimeoutId = null;\n this._pendingTimeoutStartTime = null;\n this._pendingTimeoutDelay = null;\n }\n callback();\n }, delay);\n this._pendingTimeoutId = timeoutID;\n this._pendingTimeoutStartTime = Date.now();\n this._pendingTimeoutDelay = delay;\n }\n}\n\n/**\n * Manage communication with a WeDo 2.0 peripheral over a Bluetooth Low Energy client socket.\n */\nclass WeDo2 {\n\n constructor (runtime, extensionId) {\n\n /**\n * The Scratch 3.0 runtime used to trigger the green flag button.\n * @type {Runtime}\n * @private\n */\n this._runtime = runtime;\n this._runtime.on('PROJECT_STOP_ALL', this.stopAll.bind(this));\n\n /**\n * The id of the extension this peripheral belongs to.\n */\n this._extensionId = extensionId;\n\n /**\n * A list of the ids of the motors or sensors in ports 1 and 2.\n * @type {string[]}\n * @private\n */\n this._ports = ['none', 'none'];\n\n /**\n * The motors which this WeDo 2.0 could possibly have.\n * @type {WeDo2Motor[]}\n * @private\n */\n this._motors = [null, null];\n\n /**\n * The most recently received value for each sensor.\n * @type {Object.}\n * @private\n */\n this._sensors = {\n tiltX: 0,\n tiltY: 0,\n distance: 0\n };\n\n /**\n * The Bluetooth connection socket for reading/writing peripheral data.\n * @type {BLE}\n * @private\n */\n this._ble = null;\n this._runtime.registerPeripheralExtension(extensionId, this);\n\n /**\n * A rate limiter utility, to help limit the rate at which we send BLE messages\n * over the socket to Scratch Link to a maximum number of sends per second.\n * @type {RateLimiter}\n * @private\n */\n this._rateLimiter = new RateLimiter(BLESendRateMax);\n\n /**\n * An interval id for the battery check interval.\n * @type {number}\n * @private\n */\n this._batteryLevelIntervalId = null;\n\n this.reset = this.reset.bind(this);\n this._onConnect = this._onConnect.bind(this);\n this._onMessage = this._onMessage.bind(this);\n this._checkBatteryLevel = this._checkBatteryLevel.bind(this);\n }\n\n /**\n * @return {number} - the latest value received for the tilt sensor's tilt about the X axis.\n */\n get tiltX () {\n return this._sensors.tiltX;\n }\n\n /**\n * @return {number} - the latest value received for the tilt sensor's tilt about the Y axis.\n */\n get tiltY () {\n return this._sensors.tiltY;\n }\n\n /**\n * @return {number} - the latest value received from the distance sensor.\n */\n get distance () {\n return this._sensors.distance;\n }\n\n /**\n * Access a particular motor on this peripheral.\n * @param {int} index - the zero-based index of the desired motor.\n * @return {WeDo2Motor} - the WeDo2Motor instance, if any, at that index.\n */\n motor (index) {\n return this._motors[index];\n }\n\n /**\n * Stop all the motors that are currently running.\n */\n stopAllMotors () {\n this._motors.forEach(motor => {\n if (motor) {\n // Send the motor off command without using the rate limiter.\n // This allows the stop button to stop motors even if we are\n // otherwise flooded with commands.\n motor.turnOff(false);\n }\n });\n }\n\n /**\n * Set the WeDo 2.0 peripheral's LED to a specific color.\n * @param {int} inputRGB - a 24-bit RGB color in 0xRRGGBB format.\n * @return {Promise} - a promise of the completion of the set led send operation.\n */\n setLED (inputRGB) {\n const rgb = [\n (inputRGB >> 16) & 0x000000FF,\n (inputRGB >> 8) & 0x000000FF,\n (inputRGB) & 0x000000FF\n ];\n\n const cmd = this.generateOutputCommand(\n WeDo2ConnectID.LED,\n WeDo2Command.WRITE_RGB,\n rgb\n );\n\n return this.send(BLECharacteristic.OUTPUT_COMMAND, cmd);\n }\n\n /**\n * Sets the input mode of the LED to RGB.\n * @return {Promise} - a promise returned by the send operation.\n */\n setLEDMode () {\n const cmd = this.generateInputCommand(\n WeDo2ConnectID.LED,\n WeDo2Device.LED,\n WeDo2Mode.LED,\n 0,\n WeDo2Unit.LED,\n false\n );\n\n return this.send(BLECharacteristic.INPUT_COMMAND, cmd);\n }\n\n /**\n * Switch off the LED on the WeDo 2.0.\n * @return {Promise} - a promise of the completion of the stop led send operation.\n */\n stopLED () {\n const cmd = this.generateOutputCommand(\n WeDo2ConnectID.LED,\n WeDo2Command.WRITE_RGB,\n [0, 0, 0]\n );\n\n return this.send(BLECharacteristic.OUTPUT_COMMAND, cmd);\n }\n\n /**\n * Play a tone from the WeDo 2.0 peripheral for a specific amount of time.\n * @param {int} tone - the pitch of the tone, in Hz.\n * @param {int} milliseconds - the duration of the note, in milliseconds.\n * @return {Promise} - a promise of the completion of the play tone send operation.\n */\n playTone (tone, milliseconds) {\n const cmd = this.generateOutputCommand(\n WeDo2ConnectID.PIEZO,\n WeDo2Command.PLAY_TONE,\n [\n tone,\n tone >> 8,\n milliseconds,\n milliseconds >> 8\n ]\n );\n\n return this.send(BLECharacteristic.OUTPUT_COMMAND, cmd);\n }\n\n /**\n * Stop the tone playing from the WeDo 2.0 peripheral, if any.\n * @return {Promise} - a promise that the command sent.\n */\n stopTone () {\n const cmd = this.generateOutputCommand(\n WeDo2ConnectID.PIEZO,\n WeDo2Command.STOP_TONE\n );\n\n // Send this command without using the rate limiter, because it is\n // only triggered by the stop button.\n return this.send(BLECharacteristic.OUTPUT_COMMAND, cmd, false);\n }\n\n /**\n * Stop the tone playing and motors on the WeDo 2.0 peripheral.\n */\n stopAll () {\n if (!this.isConnected()) return;\n this.stopTone();\n this.stopAllMotors();\n }\n\n /**\n * Called by the runtime when user wants to scan for a WeDo 2.0 peripheral.\n */\n scan () {\n if (this._ble) {\n this._ble.disconnect();\n }\n this._ble = new BLE(this._runtime, this._extensionId, {\n filters: [{\n services: [BLEService.DEVICE_SERVICE]\n }],\n optionalServices: [BLEService.IO_SERVICE]\n }, this._onConnect, this.reset);\n }\n\n /**\n * Called by the runtime when user wants to connect to a certain WeDo 2.0 peripheral.\n * @param {number} id - the id of the peripheral to connect to.\n */\n connect (id) {\n if (this._ble) {\n this._ble.connectPeripheral(id);\n }\n }\n\n /**\n * Disconnects from the current BLE socket.\n */\n disconnect () {\n if (this._ble) {\n this._ble.disconnect();\n }\n\n this.reset();\n }\n\n /**\n * Reset all the state and timeout/interval ids.\n */\n reset () {\n this._ports = ['none', 'none'];\n this._motors = [null, null];\n this._sensors = {\n tiltX: 0,\n tiltY: 0,\n distance: 0\n };\n\n if (this._batteryLevelIntervalId) {\n window.clearInterval(this._batteryLevelIntervalId);\n this._batteryLevelIntervalId = null;\n }\n }\n\n /**\n * Called by the runtime to detect whether the WeDo 2.0 peripheral is connected.\n * @return {boolean} - the connected state.\n */\n isConnected () {\n let connected = false;\n if (this._ble) {\n connected = this._ble.isConnected();\n }\n return connected;\n }\n\n /**\n * Write a message to the WeDo 2.0 peripheral BLE socket.\n * @param {number} uuid - the UUID of the characteristic to write to\n * @param {Array} message - the message to write.\n * @param {boolean} [useLimiter=true] - if true, use the rate limiter\n * @return {Promise} - a promise result of the write operation\n */\n send (uuid, message, useLimiter = true) {\n if (!this.isConnected()) return Promise.resolve();\n\n if (useLimiter) {\n if (!this._rateLimiter.okayToSend()) return Promise.resolve();\n }\n\n return this._ble.write(\n BLEService.IO_SERVICE,\n uuid,\n Base64Util.uint8ArrayToBase64(message),\n 'base64'\n );\n }\n\n /**\n * Generate a WeDo 2.0 'Output Command' in the byte array format\n * (CONNECT ID, COMMAND ID, NUMBER OF BYTES, VALUES ...).\n *\n * This sends a command to the WeDo 2.0 to actuate the specified outputs.\n *\n * @param {number} connectID - the port (Connect ID) to send a command to.\n * @param {number} commandID - the id of the byte command.\n * @param {array} values - the list of values to write to the command.\n * @return {array} - a generated output command.\n */\n generateOutputCommand (connectID, commandID, values = null) {\n let command = [connectID, commandID];\n if (values) {\n command = command.concat(\n values.length\n ).concat(\n values\n );\n }\n return command;\n }\n\n /**\n * Generate a WeDo 2.0 'Input Command' in the byte array format\n * (COMMAND ID, COMMAND TYPE, CONNECT ID, TYPE ID, MODE, DELTA INTERVAL (4 BYTES),\n * UNIT, NOTIFICATIONS ENABLED).\n *\n * This sends a command to the WeDo 2.0 that sets that input format\n * of the specified inputs and sets value change notifications.\n *\n * @param {number} connectID - the port (Connect ID) to send a command to.\n * @param {number} type - the type of input sensor.\n * @param {number} mode - the mode of the input sensor.\n * @param {number} delta - the delta change needed to trigger notification.\n * @param {array} units - the unit of the input sensor value.\n * @param {boolean} enableNotifications - whether to enable notifications.\n * @return {array} - a generated input command.\n */\n generateInputCommand (connectID, type, mode, delta, units, enableNotifications) {\n const command = [\n 1, // Command ID = 1 = \"Sensor Format\"\n 2, // Command Type = 2 = \"Write\"\n connectID,\n type,\n mode,\n delta,\n 0, // Delta Interval Byte 2\n 0, // Delta Interval Byte 3\n 0, // Delta Interval Byte 4\n units,\n enableNotifications ? 1 : 0\n ];\n\n return command;\n }\n\n /**\n * Sets LED mode and initial color and starts reading data from peripheral after BLE has connected.\n * @private\n */\n _onConnect () {\n this.setLEDMode();\n this.setLED(0x0000FF);\n this._ble.startNotifications(\n BLEService.DEVICE_SERVICE,\n BLECharacteristic.ATTACHED_IO,\n this._onMessage\n );\n this._batteryLevelIntervalId = window.setInterval(this._checkBatteryLevel, BLEBatteryCheckInterval);\n }\n\n /**\n * Process the sensor data from the incoming BLE characteristic.\n * @param {object} base64 - the incoming BLE data.\n * @private\n */\n _onMessage (base64) {\n const data = Base64Util.base64ToUint8Array(base64);\n // log.info(data);\n\n /**\n * If first byte of data is '1' or '2', then either clear the\n * sensor present in ports 1 or 2 or set their format.\n *\n * If first byte of data is anything else, read incoming sensor value.\n */\n switch (data[0]) {\n case 1:\n case 2: {\n const connectID = data[0];\n if (data[1] === 0) {\n // clear sensor or motor\n this._clearPort(connectID);\n } else {\n // register sensor or motor\n this._registerSensorOrMotor(connectID, data[3]);\n }\n break;\n }\n default: {\n // read incoming sensor value\n const connectID = data[1];\n const type = this._ports[connectID - 1];\n if (type === WeDo2Device.DISTANCE) {\n this._sensors.distance = data[2];\n }\n if (type === WeDo2Device.TILT) {\n this._sensors.tiltX = data[2];\n this._sensors.tiltY = data[3];\n }\n break;\n }\n }\n }\n\n /**\n * Check the battery level on the WeDo 2.0. If the WeDo 2.0 has disconnected\n * for some reason, the BLE socket will get an error back and automatically\n * close the socket.\n */\n _checkBatteryLevel () {\n this._ble.read(\n BLEService.DEVICE_SERVICE,\n BLECharacteristic.LOW_VOLTAGE_ALERT,\n false\n );\n }\n\n /**\n * Register a new sensor or motor connected at a port. Store the type of\n * sensor or motor internally, and then register for notifications on input\n * values if it is a sensor.\n * @param {number} connectID - the port to register a sensor or motor on.\n * @param {number} type - the type ID of the sensor or motor\n * @private\n */\n _registerSensorOrMotor (connectID, type) {\n // Record which port is connected to what type of device\n this._ports[connectID - 1] = type;\n\n // Record motor port\n if (type === WeDo2Device.MOTOR) {\n this._motors[connectID - 1] = new WeDo2Motor(this, connectID - 1);\n } else {\n // Set input format for tilt or distance sensor\n const typeString = type === WeDo2Device.DISTANCE ? 'DISTANCE' : 'TILT';\n const cmd = this.generateInputCommand(\n connectID,\n type,\n WeDo2Mode[typeString],\n 1,\n WeDo2Unit[typeString],\n true\n );\n\n this.send(BLECharacteristic.INPUT_COMMAND, cmd);\n this._ble.startNotifications(\n BLEService.IO_SERVICE,\n BLECharacteristic.INPUT_VALUES,\n this._onMessage\n );\n }\n }\n\n /**\n * Clear the sensor or motor present at port 1 or 2.\n * @param {number} connectID - the port to clear.\n * @private\n */\n _clearPort (connectID) {\n const type = this._ports[connectID - 1];\n if (type === WeDo2Device.TILT) {\n this._sensors.tiltX = this._sensors.tiltY = 0;\n }\n if (type === WeDo2Device.DISTANCE) {\n this._sensors.distance = 0;\n }\n this._ports[connectID - 1] = 'none';\n this._motors[connectID - 1] = null;\n }\n}\n\n/**\n * Enum for motor specification.\n * @readonly\n * @enum {string}\n */\nconst WeDo2MotorLabel = {\n DEFAULT: 'motor',\n A: 'motor A',\n B: 'motor B',\n ALL: 'all motors'\n};\n\n/**\n * Enum for motor direction specification.\n * @readonly\n * @enum {string}\n */\nconst WeDo2MotorDirection = {\n FORWARD: 'this way',\n BACKWARD: 'that way',\n REVERSE: 'reverse'\n};\n\n/**\n * Enum for tilt sensor direction.\n * @readonly\n * @enum {string}\n */\nconst WeDo2TiltDirection = {\n UP: 'up',\n DOWN: 'down',\n LEFT: 'left',\n RIGHT: 'right',\n ANY: 'any'\n};\n\n/**\n * Scratch 3.0 blocks to interact with a LEGO WeDo 2.0 peripheral.\n */\nclass Scratch3WeDo2Blocks {\n\n /**\n * @return {string} - the ID of this extension.\n */\n static get EXTENSION_ID () {\n return 'wedo2';\n }\n\n /**\n * @return {number} - the tilt sensor counts as \"tilted\" if its tilt angle meets or exceeds this threshold.\n */\n static get TILT_THRESHOLD () {\n return 15;\n }\n\n /**\n * Construct a set of WeDo 2.0 blocks.\n * @param {Runtime} runtime - the Scratch 3.0 runtime.\n */\n constructor (runtime) {\n /**\n * The Scratch 3.0 runtime.\n * @type {Runtime}\n */\n this.runtime = runtime;\n\n // Create a new WeDo 2.0 peripheral instance\n this._peripheral = new WeDo2(this.runtime, Scratch3WeDo2Blocks.EXTENSION_ID);\n }\n\n /**\n * @returns {object} metadata for this extension and its blocks.\n */\n getInfo () {\n return {\n id: Scratch3WeDo2Blocks.EXTENSION_ID,\n name: 'WeDo 2.0',\n blockIconURI: iconURI,\n showStatusButton: true,\n blocks: [\n {\n opcode: 'motorOnFor',\n text: formatMessage({\n id: 'wedo2.motorOnFor',\n default: 'turn [MOTOR_ID] on for [DURATION] seconds',\n description: 'turn a motor on for some time'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n MOTOR_ID: {\n type: ArgumentType.STRING,\n menu: 'MOTOR_ID',\n defaultValue: WeDo2MotorLabel.DEFAULT\n },\n DURATION: {\n type: ArgumentType.NUMBER,\n defaultValue: 1\n }\n }\n },\n {\n opcode: 'motorOn',\n text: formatMessage({\n id: 'wedo2.motorOn',\n default: 'turn [MOTOR_ID] on',\n description: 'turn a motor on indefinitely'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n MOTOR_ID: {\n type: ArgumentType.STRING,\n menu: 'MOTOR_ID',\n defaultValue: WeDo2MotorLabel.DEFAULT\n }\n }\n },\n {\n opcode: 'motorOff',\n text: formatMessage({\n id: 'wedo2.motorOff',\n default: 'turn [MOTOR_ID] off',\n description: 'turn a motor off'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n MOTOR_ID: {\n type: ArgumentType.STRING,\n menu: 'MOTOR_ID',\n defaultValue: WeDo2MotorLabel.DEFAULT\n }\n }\n },\n {\n opcode: 'startMotorPower',\n text: formatMessage({\n id: 'wedo2.startMotorPower',\n default: 'set [MOTOR_ID] power to [POWER]',\n description: 'set the motor\\'s power and turn it on'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n MOTOR_ID: {\n type: ArgumentType.STRING,\n menu: 'MOTOR_ID',\n defaultValue: WeDo2MotorLabel.DEFAULT\n },\n POWER: {\n type: ArgumentType.NUMBER,\n defaultValue: 100\n }\n }\n },\n {\n opcode: 'setMotorDirection',\n text: formatMessage({\n id: 'wedo2.setMotorDirection',\n default: 'set [MOTOR_ID] direction to [MOTOR_DIRECTION]',\n description: 'set the motor\\'s turn direction'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n MOTOR_ID: {\n type: ArgumentType.STRING,\n menu: 'MOTOR_ID',\n defaultValue: WeDo2MotorLabel.DEFAULT\n },\n MOTOR_DIRECTION: {\n type: ArgumentType.STRING,\n menu: 'MOTOR_DIRECTION',\n defaultValue: WeDo2MotorDirection.FORWARD\n }\n }\n },\n {\n opcode: 'setLightHue',\n text: formatMessage({\n id: 'wedo2.setLightHue',\n default: 'set light color to [HUE]',\n description: 'set the LED color'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n HUE: {\n type: ArgumentType.NUMBER,\n defaultValue: 50\n }\n }\n },\n {\n opcode: 'playNoteFor',\n text: formatMessage({\n id: 'wedo2.playNoteFor',\n default: 'play note [NOTE] for [DURATION] seconds',\n description: 'play a certain note for some time'\n }),\n blockType: BlockType.COMMAND,\n arguments: {\n NOTE: {\n type: ArgumentType.NUMBER, // TODO: ArgumentType.MIDI_NOTE?\n defaultValue: 60\n },\n DURATION: {\n type: ArgumentType.NUMBER,\n defaultValue: 0.5\n }\n },\n hideFromPalette: true\n },\n {\n opcode: 'whenDistance',\n text: formatMessage({\n id: 'wedo2.whenDistance',\n default: 'when distance [OP] [REFERENCE]',\n description: 'check for when distance is < or > than reference'\n }),\n blockType: BlockType.HAT,\n arguments: {\n OP: {\n type: ArgumentType.STRING,\n menu: 'OP',\n defaultValue: '<'\n },\n REFERENCE: {\n type: ArgumentType.NUMBER,\n defaultValue: 50\n }\n }\n },\n {\n opcode: 'whenTilted',\n text: formatMessage({\n id: 'wedo2.whenTilted',\n default: 'when tilted [TILT_DIRECTION_ANY]',\n description: 'check when tilted in a certain direction'\n }),\n func: 'isTilted',\n blockType: BlockType.HAT,\n arguments: {\n TILT_DIRECTION_ANY: {\n type: ArgumentType.STRING,\n menu: 'TILT_DIRECTION_ANY',\n defaultValue: WeDo2TiltDirection.ANY\n }\n }\n },\n {\n opcode: 'getDistance',\n text: formatMessage({\n id: 'wedo2.getDistance',\n default: 'distance',\n description: 'the value returned by the distance sensor'\n }),\n blockType: BlockType.REPORTER\n },\n {\n opcode: 'isTilted',\n text: formatMessage({\n id: 'wedo2.isTilted',\n default: 'tilted [TILT_DIRECTION_ANY]?',\n description: 'whether the tilt sensor is tilted'\n }),\n blockType: BlockType.BOOLEAN,\n arguments: {\n TILT_DIRECTION_ANY: {\n type: ArgumentType.STRING,\n menu: 'TILT_DIRECTION_ANY',\n defaultValue: WeDo2TiltDirection.ANY\n }\n }\n },\n {\n opcode: 'getTiltAngle',\n text: formatMessage({\n id: 'wedo2.getTiltAngle',\n default: 'tilt angle [TILT_DIRECTION]',\n description: 'the angle returned by the tilt sensor'\n }),\n blockType: BlockType.REPORTER,\n arguments: {\n TILT_DIRECTION: {\n type: ArgumentType.STRING,\n menu: 'TILT_DIRECTION',\n defaultValue: WeDo2TiltDirection.UP\n }\n }\n }\n ],\n menus: {\n MOTOR_ID: {\n acceptReporters: true,\n items: [\n {\n text: formatMessage({\n id: 'wedo2.motorId.default',\n default: 'motor',\n description: 'label for motor element in motor menu for LEGO WeDo 2 extension'\n }),\n value: WeDo2MotorLabel.DEFAULT\n },\n {\n text: formatMessage({\n id: 'wedo2.motorId.a',\n default: 'motor A',\n description: 'label for motor A element in motor menu for LEGO WeDo 2 extension'\n }),\n value: WeDo2MotorLabel.A\n },\n {\n text: formatMessage({\n id: 'wedo2.motorId.b',\n default: 'motor B',\n description: 'label for motor B element in motor menu for LEGO WeDo 2 extension'\n }),\n value: WeDo2MotorLabel.B\n },\n {\n text: formatMessage({\n id: 'wedo2.motorId.all',\n default: 'all motors',\n description: 'label for all motors element in motor menu for LEGO WeDo 2 extension'\n }),\n value: WeDo2MotorLabel.ALL\n }\n ]\n },\n MOTOR_DIRECTION: {\n acceptReporters: true,\n items: [\n {\n text: formatMessage({\n id: 'wedo2.motorDirection.forward',\n default: 'this way',\n description:\n 'label for forward element in motor direction menu for LEGO WeDo 2 extension'\n }),\n value: WeDo2MotorDirection.FORWARD\n },\n {\n text: formatMessage({\n id: 'wedo2.motorDirection.backward',\n default: 'that way',\n description:\n 'label for backward element in motor direction menu for LEGO WeDo 2 extension'\n }),\n value: WeDo2MotorDirection.BACKWARD\n },\n {\n text: formatMessage({\n id: 'wedo2.motorDirection.reverse',\n default: 'reverse',\n description:\n 'label for reverse element in motor direction menu for LEGO WeDo 2 extension'\n }),\n value: WeDo2MotorDirection.REVERSE\n }\n ]\n },\n TILT_DIRECTION: {\n acceptReporters: true,\n items: [\n {\n text: formatMessage({\n id: 'wedo2.tiltDirection.up',\n default: 'up',\n description: 'label for up element in tilt direction menu for LEGO WeDo 2 extension'\n }),\n value: WeDo2TiltDirection.UP\n },\n {\n text: formatMessage({\n id: 'wedo2.tiltDirection.down',\n default: 'down',\n description: 'label for down element in tilt direction menu for LEGO WeDo 2 extension'\n }),\n value: WeDo2TiltDirection.DOWN\n },\n {\n text: formatMessage({\n id: 'wedo2.tiltDirection.left',\n default: 'left',\n description: 'label for left element in tilt direction menu for LEGO WeDo 2 extension'\n }),\n value: WeDo2TiltDirection.LEFT\n },\n {\n text: formatMessage({\n id: 'wedo2.tiltDirection.right',\n default: 'right',\n description: 'label for right element in tilt direction menu for LEGO WeDo 2 extension'\n }),\n value: WeDo2TiltDirection.RIGHT\n }\n ]\n },\n TILT_DIRECTION_ANY: {\n acceptReporters: true,\n items: [\n {\n text: formatMessage({\n id: 'wedo2.tiltDirection.up',\n default: 'up'\n }),\n value: WeDo2TiltDirection.UP\n },\n {\n text: formatMessage({\n id: 'wedo2.tiltDirection.down',\n default: 'down'\n }),\n value: WeDo2TiltDirection.DOWN\n },\n {\n text: formatMessage({\n id: 'wedo2.tiltDirection.left',\n default: 'left'\n }),\n value: WeDo2TiltDirection.LEFT\n },\n {\n text: formatMessage({\n id: 'wedo2.tiltDirection.right',\n default: 'right'\n }),\n value: WeDo2TiltDirection.RIGHT\n },\n {\n text: formatMessage({\n id: 'wedo2.tiltDirection.any',\n default: 'any',\n description: 'label for any element in tilt direction menu for LEGO WeDo 2 extension'\n }),\n value: WeDo2TiltDirection.ANY\n }\n ]\n },\n OP: {\n acceptReporters: true,\n items: ['<', '>']\n }\n }\n };\n }\n\n /**\n * Turn specified motor(s) on for a specified duration.\n * @param {object} args - the block's arguments.\n * @property {MotorID} MOTOR_ID - the motor(s) to activate.\n * @property {int} DURATION - the amount of time to run the motors.\n * @return {Promise} - a promise which will resolve at the end of the duration.\n */\n motorOnFor (args) {\n // TODO: cast args.MOTOR_ID?\n let durationMS = Cast.toNumber(args.DURATION) * 1000;\n durationMS = MathUtil.clamp(durationMS, 0, 15000);\n return new Promise(resolve => {\n this._forEachMotor(args.MOTOR_ID, motorIndex => {\n const motor = this._peripheral.motor(motorIndex);\n if (motor) {\n motor.turnOnFor(durationMS);\n }\n });\n\n // Run for some time even when no motor is connected\n setTimeout(resolve, durationMS);\n });\n }\n\n /**\n * Turn specified motor(s) on indefinitely.\n * @param {object} args - the block's arguments.\n * @property {MotorID} MOTOR_ID - the motor(s) to activate.\n * @return {Promise} - a Promise that resolves after some delay.\n */\n motorOn (args) {\n // TODO: cast args.MOTOR_ID?\n this._forEachMotor(args.MOTOR_ID, motorIndex => {\n const motor = this._peripheral.motor(motorIndex);\n if (motor) {\n motor.turnOn();\n }\n });\n\n return new Promise(resolve => {\n window.setTimeout(() => {\n resolve();\n }, BLESendInterval);\n });\n }\n\n /**\n * Turn specified motor(s) off.\n * @param {object} args - the block's arguments.\n * @property {MotorID} MOTOR_ID - the motor(s) to deactivate.\n * @return {Promise} - a Promise that resolves after some delay.\n */\n motorOff (args) {\n // TODO: cast args.MOTOR_ID?\n this._forEachMotor(args.MOTOR_ID, motorIndex => {\n const motor = this._peripheral.motor(motorIndex);\n if (motor) {\n motor.turnOff();\n }\n });\n\n return new Promise(resolve => {\n window.setTimeout(() => {\n resolve();\n }, BLESendInterval);\n });\n }\n\n /**\n * Turn specified motor(s) off.\n * @param {object} args - the block's arguments.\n * @property {MotorID} MOTOR_ID - the motor(s) to be affected.\n * @property {int} POWER - the new power level for the motor(s).\n * @return {Promise} - a Promise that resolves after some delay.\n */\n startMotorPower (args) {\n // TODO: cast args.MOTOR_ID?\n this._forEachMotor(args.MOTOR_ID, motorIndex => {\n const motor = this._peripheral.motor(motorIndex);\n if (motor) {\n motor.power = MathUtil.clamp(Cast.toNumber(args.POWER), 0, 100);\n motor.turnOn();\n }\n });\n\n return new Promise(resolve => {\n window.setTimeout(() => {\n resolve();\n }, BLESendInterval);\n });\n }\n\n /**\n * Set the direction of rotation for specified motor(s).\n * If the direction is 'reverse' the motor(s) will be reversed individually.\n * @param {object} args - the block's arguments.\n * @property {MotorID} MOTOR_ID - the motor(s) to be affected.\n * @property {MotorDirection} MOTOR_DIRECTION - the new direction for the motor(s).\n * @return {Promise} - a Promise that resolves after some delay.\n */\n setMotorDirection (args) {\n // TODO: cast args.MOTOR_ID?\n this._forEachMotor(args.MOTOR_ID, motorIndex => {\n const motor = this._peripheral.motor(motorIndex);\n if (motor) {\n switch (args.MOTOR_DIRECTION) {\n case WeDo2MotorDirection.FORWARD:\n motor.direction = 1;\n break;\n case WeDo2MotorDirection.BACKWARD:\n motor.direction = -1;\n break;\n case WeDo2MotorDirection.REVERSE:\n motor.direction = -motor.direction;\n break;\n default:\n log.warn(`Unknown motor direction in setMotorDirection: ${args.DIRECTION}`);\n break;\n }\n // keep the motor on if it's running, and update the pending timeout if needed\n if (motor.isOn) {\n if (motor.pendingTimeoutDelay) {\n motor.turnOnFor(motor.pendingTimeoutStartTime + motor.pendingTimeoutDelay - Date.now());\n } else {\n motor.turnOn();\n }\n }\n }\n });\n\n return new Promise(resolve => {\n window.setTimeout(() => {\n resolve();\n }, BLESendInterval);\n });\n }\n\n /**\n * Set the LED's hue.\n * @param {object} args - the block's arguments.\n * @property {number} HUE - the hue to set, in the range [0,100].\n * @return {Promise} - a Promise that resolves after some delay.\n */\n setLightHue (args) {\n // Convert from [0,100] to [0,360]\n let inputHue = Cast.toNumber(args.HUE);\n inputHue = MathUtil.wrapClamp(inputHue, 0, 100);\n const hue = inputHue * 360 / 100;\n\n const rgbObject = color.hsvToRgb({h: hue, s: 1, v: 1});\n\n const rgbDecimal = color.rgbToDecimal(rgbObject);\n\n this._peripheral.setLED(rgbDecimal);\n\n return new Promise(resolve => {\n window.setTimeout(() => {\n resolve();\n }, BLESendInterval);\n });\n }\n\n /**\n * Make the WeDo 2.0 peripheral play a MIDI note for the specified duration.\n * @param {object} args - the block's arguments.\n * @property {number} NOTE - the MIDI note to play.\n * @property {number} DURATION - the duration of the note, in seconds.\n * @return {Promise} - a promise which will resolve at the end of the duration.\n */\n playNoteFor (args) {\n let durationMS = Cast.toNumber(args.DURATION) * 1000;\n durationMS = MathUtil.clamp(durationMS, 0, 3000);\n const note = MathUtil.clamp(Cast.toNumber(args.NOTE), 25, 125); // valid WeDo 2.0 sounds\n if (durationMS === 0) return; // WeDo 2.0 plays duration '0' forever\n return new Promise(resolve => {\n const tone = this._noteToTone(note);\n this._peripheral.playTone(tone, durationMS);\n\n // Run for some time even when no piezo is connected\n setTimeout(resolve, durationMS);\n });\n }\n\n /**\n * Compare the distance sensor's value to a reference.\n * @param {object} args - the block's arguments.\n * @property {string} OP - the comparison operation: '<' or '>'.\n * @property {number} REFERENCE - the value to compare against.\n * @return {boolean} - the result of the comparison, or false on error.\n */\n whenDistance (args) {\n switch (args.OP) {\n case '<':\n return this._peripheral.distance < Cast.toNumber(args.REFERENCE);\n case '>':\n return this._peripheral.distance > Cast.toNumber(args.REFERENCE);\n default:\n log.warn(`Unknown comparison operator in whenDistance: ${args.OP}`);\n return false;\n }\n }\n\n /**\n * Test whether the tilt sensor is currently tilted.\n * @param {object} args - the block's arguments.\n * @property {TiltDirection} TILT_DIRECTION_ANY - the tilt direction to test (up, down, left, right, or any).\n * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction.\n */\n whenTilted (args) {\n return this._isTilted(args.TILT_DIRECTION_ANY);\n }\n\n /**\n * @return {number} - the distance sensor's value, scaled to the [0,100] range.\n */\n getDistance () {\n return this._peripheral.distance;\n }\n\n /**\n * Test whether the tilt sensor is currently tilted.\n * @param {object} args - the block's arguments.\n * @property {TiltDirection} TILT_DIRECTION_ANY - the tilt direction to test (up, down, left, right, or any).\n * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction.\n */\n isTilted (args) {\n return this._isTilted(args.TILT_DIRECTION_ANY);\n }\n\n /**\n * @param {object} args - the block's arguments.\n * @property {TiltDirection} TILT_DIRECTION - the direction (up, down, left, right) to check.\n * @return {number} - the tilt sensor's angle in the specified direction.\n * Note that getTiltAngle(up) = -getTiltAngle(down) and getTiltAngle(left) = -getTiltAngle(right).\n */\n getTiltAngle (args) {\n return this._getTiltAngle(args.TILT_DIRECTION);\n }\n\n /**\n * Test whether the tilt sensor is currently tilted.\n * @param {TiltDirection} direction - the tilt direction to test (up, down, left, right, or any).\n * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction.\n * @private\n */\n _isTilted (direction) {\n switch (direction) {\n case WeDo2TiltDirection.ANY:\n return this._getTiltAngle(WeDo2TiltDirection.UP) >= Scratch3WeDo2Blocks.TILT_THRESHOLD ||\n this._getTiltAngle(WeDo2TiltDirection.DOWN) >= Scratch3WeDo2Blocks.TILT_THRESHOLD ||\n this._getTiltAngle(WeDo2TiltDirection.LEFT) >= Scratch3WeDo2Blocks.TILT_THRESHOLD ||\n this._getTiltAngle(WeDo2TiltDirection.RIGHT) >= Scratch3WeDo2Blocks.TILT_THRESHOLD;\n default:\n return this._getTiltAngle(direction) >= Scratch3WeDo2Blocks.TILT_THRESHOLD;\n }\n }\n\n /**\n * @param {TiltDirection} direction - the direction (up, down, left, right) to check.\n * @return {number} - the tilt sensor's angle in the specified direction.\n * Note that getTiltAngle(up) = -getTiltAngle(down) and getTiltAngle(left) = -getTiltAngle(right).\n * @private\n */\n _getTiltAngle (direction) {\n switch (direction) {\n case WeDo2TiltDirection.UP:\n return this._peripheral.tiltY > 45 ? 256 - this._peripheral.tiltY : -this._peripheral.tiltY;\n case WeDo2TiltDirection.DOWN:\n return this._peripheral.tiltY > 45 ? this._peripheral.tiltY - 256 : this._peripheral.tiltY;\n case WeDo2TiltDirection.LEFT:\n return this._peripheral.tiltX > 45 ? 256 - this._peripheral.tiltX : -this._peripheral.tiltX;\n case WeDo2TiltDirection.RIGHT:\n return this._peripheral.tiltX > 45 ? this._peripheral.tiltX - 256 : this._peripheral.tiltX;\n default:\n log.warn(`Unknown tilt direction in _getTiltAngle: ${direction}`);\n }\n }\n\n /**\n * Call a callback for each motor indexed by the provided motor ID.\n * @param {MotorID} motorID - the ID specifier.\n * @param {Function} callback - the function to call with the numeric motor index for each motor.\n * @private\n */\n _forEachMotor (motorID, callback) {\n let motors;\n switch (motorID) {\n case WeDo2MotorLabel.A:\n motors = [0];\n break;\n case WeDo2MotorLabel.B:\n motors = [1];\n break;\n case WeDo2MotorLabel.ALL:\n case WeDo2MotorLabel.DEFAULT:\n motors = [0, 1];\n break;\n default:\n log.warn(`Invalid motor ID: ${motorID}`);\n motors = [];\n break;\n }\n for (const index of motors) {\n callback(index);\n }\n }\n\n /**\n * @param {number} midiNote - the MIDI note value to convert.\n * @return {number} - the frequency, in Hz, corresponding to that MIDI note value.\n * @private\n */\n _noteToTone (midiNote) {\n // Note that MIDI note 69 is A4, 440 Hz\n return 440 * Math.pow(2, (midiNote - 69) / 12);\n }\n}\n\nmodule.exports = Scratch3WeDo2Blocks;\n","const StringUtil = require('../util/string-util');\nconst log = require('../util/log');\nconst {loadSvgString, serializeSvgToString} = require('scratch-svg-renderer');\n\nconst loadVector_ = function (costume, runtime, rotationCenter, optVersion) {\n return new Promise(resolve => {\n let svgString = costume.asset.decodeText();\n // SVG Renderer load fixes \"quirks\" associated with Scratch 2 projects\n if (optVersion && optVersion === 2) {\n // scratch-svg-renderer fixes syntax that causes loading issues,\n // and if optVersion is 2, fixes \"quirks\" associated with Scratch 2 SVGs,\n const fixedSvgString = serializeSvgToString(loadSvgString(svgString, true /* fromVersion2 */));\n\n // If the string changed, put back into storage\n if (svgString !== fixedSvgString) {\n svgString = fixedSvgString;\n const storage = runtime.storage;\n costume.asset.encodeTextData(fixedSvgString, storage.DataFormat.SVG, true);\n costume.assetId = costume.asset.assetId;\n costume.md5 = `${costume.assetId}.${costume.dataFormat}`;\n }\n }\n\n // createSVGSkin does the right thing if rotationCenter isn't provided, so it's okay if it's\n // undefined here\n costume.skinId = runtime.renderer.createSVGSkin(svgString, rotationCenter);\n costume.size = runtime.renderer.getSkinSize(costume.skinId);\n // Now we should have a rotationCenter even if we didn't before\n if (!rotationCenter) {\n rotationCenter = runtime.renderer.getSkinRotationCenter(costume.skinId);\n costume.rotationCenterX = rotationCenter[0];\n costume.rotationCenterY = rotationCenter[1];\n costume.bitmapResolution = 1;\n }\n\n resolve(costume);\n });\n};\n\nconst canvasPool = (function () {\n /**\n * A pool of canvas objects that can be reused to reduce memory\n * allocations. And time spent in those allocations and the later garbage\n * collection.\n */\n class CanvasPool {\n constructor () {\n this.pool = [];\n this.clearSoon = null;\n }\n\n /**\n * After a short wait period clear the pool to let the VM collect\n * garbage.\n */\n clear () {\n if (!this.clearSoon) {\n this.clearSoon = new Promise(resolve => setTimeout(resolve, 1000))\n .then(() => {\n this.pool.length = 0;\n this.clearSoon = null;\n });\n }\n }\n\n /**\n * Return a canvas. Create the canvas if the pool is empty.\n * @returns {HTMLCanvasElement} A canvas element.\n */\n create () {\n return this.pool.pop() || document.createElement('canvas');\n }\n\n /**\n * Release the canvas to be reused.\n * @param {HTMLCanvasElement} canvas A canvas element.\n */\n release (canvas) {\n this.clear();\n this.pool.push(canvas);\n }\n }\n\n return new CanvasPool();\n}());\n\n/**\n * Return a promise to fetch a bitmap from storage and return it as a canvas\n * If the costume has bitmapResolution 1, it will be converted to bitmapResolution 2 here (the standard for Scratch 3)\n * If the costume has a text layer asset, which is a text part from Scratch 1.4, then this function\n * will merge the two image assets. See the issue LLK/scratch-vm#672 for more information.\n * @param {!object} costume - the Scratch costume object.\n * @param {!Runtime} runtime - Scratch runtime, used to access the v2BitmapAdapter\n * @param {?object} rotationCenter - optionally passed in coordinates for the center of rotation for the image. If\n * none is given, the rotation center of the costume will be set to the middle of the costume later on.\n * @property {number} costume.bitmapResolution - the resolution scale for a bitmap costume.\n * @returns {?Promise} - a promise which will resolve to an object {canvas, rotationCenter, assetMatchesBase},\n * or reject on error.\n * assetMatchesBase is true if the asset matches the base layer; false if it required adjustment\n */\nconst fetchBitmapCanvas_ = function (costume, runtime, rotationCenter) {\n if (!costume || !costume.asset) { // TODO: We can probably remove this check...\n // TODO: reject with an Error (breaking API change!)\n // eslint-disable-next-line prefer-promise-reject-errors\n return Promise.reject('Costume load failed. Assets were missing.');\n }\n if (!runtime.v2BitmapAdapter) {\n // TODO: reject with an Error (breaking API change!)\n // eslint-disable-next-line prefer-promise-reject-errors\n return Promise.reject('No V2 Bitmap adapter present.');\n }\n\n return Promise.all([costume.asset, costume.textLayerAsset].map(asset => {\n if (!asset) {\n return null;\n }\n\n if (typeof createImageBitmap !== 'undefined') {\n return createImageBitmap(\n new Blob([asset.data], {type: asset.assetType.contentType})\n );\n }\n\n return new Promise((resolve, reject) => {\n const image = new Image();\n image.onload = function () {\n resolve(image);\n image.onload = null;\n image.onerror = null;\n };\n image.onerror = function () {\n // TODO: reject with an Error (breaking API change!)\n // eslint-disable-next-line prefer-promise-reject-errors\n reject('Costume load failed. Asset could not be read.');\n image.onload = null;\n image.onerror = null;\n };\n image.src = asset.encodeDataURI();\n });\n }))\n .then(([baseImageElement, textImageElement]) => {\n const mergeCanvas = canvasPool.create();\n\n const scale = costume.bitmapResolution === 1 ? 2 : 1;\n mergeCanvas.width = baseImageElement.width;\n mergeCanvas.height = baseImageElement.height;\n\n const ctx = mergeCanvas.getContext('2d');\n ctx.drawImage(baseImageElement, 0, 0);\n if (textImageElement) {\n ctx.drawImage(textImageElement, 0, 0);\n }\n // Track the canvas we merged the bitmaps onto separately from the\n // canvas that we receive from resize if scale is not 1. We know\n // resize treats mergeCanvas as read only data. We don't know when\n // resize may use or modify the canvas. So we'll only release the\n // mergeCanvas back into the canvas pool. Reusing the canvas from\n // resize may cause errors.\n let canvas = mergeCanvas;\n if (scale !== 1) {\n canvas = runtime.v2BitmapAdapter.resize(mergeCanvas, canvas.width * scale, canvas.height * scale);\n }\n\n // By scaling, we've converted it to bitmap resolution 2\n if (rotationCenter) {\n rotationCenter[0] = rotationCenter[0] * scale;\n rotationCenter[1] = rotationCenter[1] * scale;\n costume.rotationCenterX = rotationCenter[0];\n costume.rotationCenterY = rotationCenter[1];\n }\n costume.bitmapResolution = 2;\n\n // Clean up the costume object\n delete costume.textLayerMD5;\n delete costume.textLayerAsset;\n\n return {\n canvas,\n mergeCanvas,\n rotationCenter,\n // True if the asset matches the base layer; false if it required adjustment\n assetMatchesBase: scale === 1 && !textImageElement\n };\n })\n .finally(() => {\n // Clean up the text layer properties if it fails to load\n delete costume.textLayerMD5;\n delete costume.textLayerAsset;\n });\n};\n\nconst loadBitmap_ = function (costume, runtime, _rotationCenter) {\n return fetchBitmapCanvas_(costume, runtime, _rotationCenter)\n .then(fetched => {\n const updateCostumeAsset = function (dataURI) {\n if (!runtime.v2BitmapAdapter) {\n // TODO: This might be a bad practice since the returned\n // promise isn't acted on. If this is something we should be\n // creating a rejected promise for we should also catch it\n // somewhere and act on that error (like logging).\n //\n // Return a rejection to stop executing updateCostumeAsset.\n // TODO: reject with an Error (breaking API change!)\n // eslint-disable-next-line prefer-promise-reject-errors\n return Promise.reject('No V2 Bitmap adapter present.');\n }\n\n const storage = runtime.storage;\n costume.asset = storage.createAsset(\n storage.AssetType.ImageBitmap,\n storage.DataFormat.PNG,\n runtime.v2BitmapAdapter.convertDataURIToBinary(dataURI),\n null,\n true // generate md5\n );\n costume.dataFormat = storage.DataFormat.PNG;\n costume.assetId = costume.asset.assetId;\n costume.md5 = `${costume.assetId}.${costume.dataFormat}`;\n };\n\n if (!fetched.assetMatchesBase) {\n updateCostumeAsset(fetched.canvas.toDataURL());\n }\n\n return fetched;\n })\n .then(({canvas, mergeCanvas, rotationCenter}) => {\n // createBitmapSkin does the right thing if costume.rotationCenter is undefined.\n // That will be the case if you upload a bitmap asset or create one by taking a photo.\n let center;\n if (rotationCenter) {\n // fetchBitmapCanvas will ensure that the costume's bitmap resolution is 2 and its rotation center is\n // scaled to match, so it's okay to always divide by 2.\n center = [\n rotationCenter[0] / 2,\n rotationCenter[1] / 2\n ];\n }\n\n // TODO: costume.bitmapResolution will always be 2 at this point because of fetchBitmapCanvas_, so we don't\n // need to pass it in here.\n costume.skinId = runtime.renderer.createBitmapSkin(canvas, costume.bitmapResolution, center);\n canvasPool.release(mergeCanvas);\n const renderSize = runtime.renderer.getSkinSize(costume.skinId);\n costume.size = [renderSize[0] * 2, renderSize[1] * 2]; // Actual size, since all bitmaps are resolution 2\n\n if (!rotationCenter) {\n rotationCenter = runtime.renderer.getSkinRotationCenter(costume.skinId);\n // Actual rotation center, since all bitmaps are resolution 2\n costume.rotationCenterX = rotationCenter[0] * 2;\n costume.rotationCenterY = rotationCenter[1] * 2;\n costume.bitmapResolution = 2;\n }\n return costume;\n });\n};\n\n// Handle all manner of costume errors with a Gray Question Mark (default costume)\n// and preserve as much of the original costume data as possible\n// Returns a promise of a costume\nconst handleCostumeLoadError = function (costume, runtime) {\n // Keep track of the old asset information until we're done loading the default costume\n const oldAsset = costume.asset; // could be null\n const oldAssetId = costume.assetId;\n const oldRotationX = costume.rotationCenterX;\n const oldRotationY = costume.rotationCenterY;\n const oldBitmapResolution = costume.bitmapResolution;\n const oldDataFormat = costume.dataFormat;\n\n const AssetType = runtime.storage.AssetType;\n const isVector = costume.dataFormat === AssetType.ImageVector.runtimeFormat;\n\n // Use default asset if original fails to load\n costume.assetId = isVector ?\n runtime.storage.defaultAssetId.ImageVector :\n runtime.storage.defaultAssetId.ImageBitmap;\n costume.asset = runtime.storage.get(costume.assetId);\n costume.md5 = `${costume.assetId}.${costume.asset.dataFormat}`;\n\n const defaultCostumePromise = (isVector) ?\n loadVector_(costume, runtime) : loadBitmap_(costume, runtime);\n\n return defaultCostumePromise.then(loadedCostume => {\n loadedCostume.broken = {};\n loadedCostume.broken.assetId = oldAssetId;\n loadedCostume.broken.md5 = `${oldAssetId}.${oldDataFormat}`;\n\n // Should be null if we got here because the costume was missing\n loadedCostume.broken.asset = oldAsset;\n loadedCostume.broken.dataFormat = oldDataFormat;\n\n loadedCostume.broken.rotationCenterX = oldRotationX;\n loadedCostume.broken.rotationCenterY = oldRotationY;\n loadedCostume.broken.bitmapResolution = oldBitmapResolution;\n return loadedCostume;\n });\n};\n\n/**\n * Initialize a costume from an asset asynchronously.\n * Do not call this unless there is a renderer attached.\n * @param {!object} costume - the Scratch costume object.\n * @property {int} skinId - the ID of the costume's render skin, once installed.\n * @property {number} rotationCenterX - the X component of the costume's origin.\n * @property {number} rotationCenterY - the Y component of the costume's origin.\n * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume.\n * @property {!Asset} costume.asset - the asset of the costume loaded from storage.\n * @param {!Runtime} runtime - Scratch runtime, used to access the storage module.\n * @param {?int} optVersion - Version of Scratch that the costume comes from. If this is set\n * to 2, scratch 3 will perform an upgrade step to handle quirks in SVGs from Scratch 2.0.\n * @returns {?Promise} - a promise which will resolve after skinId is set, or null on error.\n */\nconst loadCostumeFromAsset = function (costume, runtime, optVersion) {\n costume.assetId = costume.asset.assetId;\n const renderer = runtime.renderer;\n if (!renderer) {\n log.warn('No rendering module present; cannot load costume: ', costume.name);\n return Promise.resolve(costume);\n }\n const AssetType = runtime.storage.AssetType;\n let rotationCenter;\n // Use provided rotation center and resolution if they are defined. Bitmap resolution\n // should only ever be 1 or 2.\n if (typeof costume.rotationCenterX === 'number' && !isNaN(costume.rotationCenterX) &&\n typeof costume.rotationCenterY === 'number' && !isNaN(costume.rotationCenterY)) {\n rotationCenter = [costume.rotationCenterX, costume.rotationCenterY];\n }\n if (costume.asset.assetType.runtimeFormat === AssetType.ImageVector.runtimeFormat) {\n return loadVector_(costume, runtime, rotationCenter, optVersion)\n .catch(error => {\n log.warn(`Error loading vector image: ${error}`);\n return handleCostumeLoadError(costume, runtime);\n\n });\n }\n return loadBitmap_(costume, runtime, rotationCenter, optVersion)\n .catch(error => {\n log.warn(`Error loading bitmap image: ${error}`);\n return handleCostumeLoadError(costume, runtime);\n });\n};\n\n\n/**\n * Load a costume's asset into memory asynchronously.\n * Do not call this unless there is a renderer attached.\n * @param {!string} md5ext - the MD5 and extension of the costume to be loaded.\n * @param {!object} costume - the Scratch costume object.\n * @property {int} skinId - the ID of the costume's render skin, once installed.\n * @property {number} rotationCenterX - the X component of the costume's origin.\n * @property {number} rotationCenterY - the Y component of the costume's origin.\n * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume.\n * @param {!Runtime} runtime - Scratch runtime, used to access the storage module.\n * @param {?int} optVersion - Version of Scratch that the costume comes from. If this is set\n * to 2, scratch 3 will perform an upgrade step to handle quirks in SVGs from Scratch 2.0.\n * @returns {?Promise} - a promise which will resolve after skinId is set, or null on error.\n */\nconst loadCostume = function (md5ext, costume, runtime, optVersion) {\n const idParts = StringUtil.splitFirst(md5ext, '.');\n const md5 = idParts[0];\n const ext = idParts[1].toLowerCase();\n costume.dataFormat = ext;\n\n if (costume.asset) {\n // Costume comes with asset. It could be coming from image upload, drag and drop, or file\n return loadCostumeFromAsset(costume, runtime, optVersion);\n }\n\n // Need to load the costume from storage. The server should have a reference to this md5.\n if (!runtime.storage) {\n log.warn('No storage module present; cannot load costume asset: ', md5ext);\n return Promise.resolve(costume);\n }\n\n if (!runtime.storage.defaultAssetId) {\n log.warn(`No default assets found`);\n return Promise.resolve(costume);\n }\n\n const AssetType = runtime.storage.AssetType;\n const assetType = (ext === 'svg') ? AssetType.ImageVector : AssetType.ImageBitmap;\n\n const costumePromise = runtime.storage.load(assetType, md5, ext);\n\n let textLayerPromise;\n if (costume.textLayerMD5) {\n textLayerPromise = runtime.storage.load(AssetType.ImageBitmap, costume.textLayerMD5, 'png');\n } else {\n textLayerPromise = Promise.resolve(null);\n }\n\n return Promise.all([costumePromise, textLayerPromise])\n .then(assetArray => {\n if (assetArray[0]) {\n costume.asset = assetArray[0];\n } else {\n return handleCostumeLoadError(costume, runtime);\n }\n\n if (assetArray[1]) {\n costume.textLayerAsset = assetArray[1];\n }\n return loadCostumeFromAsset(costume, runtime, optVersion);\n })\n .catch(error => {\n // Handle case where storage.load rejects with errors\n // instead of resolving null\n log.warn('Error loading costume: ', error);\n return handleCostumeLoadError(costume, runtime);\n });\n};\n\nmodule.exports = {\n loadCostume,\n loadCostumeFromAsset\n};\n","const StringUtil = require('../util/string-util');\nconst log = require('../util/log');\n\n/**\n * Initialize a sound from an asset asynchronously.\n * @param {!object} sound - the Scratch sound object.\n * @property {string} md5 - the MD5 and extension of the sound to be loaded.\n * @property {Buffer} data - sound data will be written here once loaded.\n * @param {!Asset} soundAsset - the asset loaded from storage.\n * @param {!Runtime} runtime - Scratch runtime, used to access the storage module.\n * @param {SoundBank} soundBank - Scratch Audio SoundBank to add sounds to.\n * @returns {!Promise} - a promise which will resolve to the sound when ready.\n */\nconst loadSoundFromAsset = function (sound, soundAsset, runtime, soundBank) {\n sound.assetId = soundAsset.assetId;\n if (!runtime.audioEngine) {\n log.warn('No audio engine present; cannot load sound asset: ', sound.md5);\n return Promise.resolve(sound);\n }\n return runtime.audioEngine.decodeSoundPlayer(Object.assign(\n {},\n sound,\n {data: soundAsset.data}\n )).then(soundPlayer => {\n sound.soundId = soundPlayer.id;\n // Set the sound sample rate and sample count based on the\n // the audio buffer from the audio engine since the sound\n // gets resampled by the audio engine\n const soundBuffer = soundPlayer.buffer;\n sound.rate = soundBuffer.sampleRate;\n sound.sampleCount = soundBuffer.length;\n\n if (soundBank !== null) {\n soundBank.addSoundPlayer(soundPlayer);\n }\n\n return sound;\n });\n};\n\n// Handle sound loading errors by replacing the runtime sound with the\n// default sound from storage, but keeping track of the original sound metadata\n// in a `broken` field\nconst handleSoundLoadError = function (sound, runtime, soundBank) {\n // Keep track of the old asset information until we're done loading the default sound\n const oldAsset = sound.asset; // could be null\n const oldAssetId = sound.assetId;\n const oldSample = sound.sampleCount;\n const oldRate = sound.rate;\n const oldFormat = sound.format;\n const oldDataFormat = sound.dataFormat;\n \n // Use default asset if original fails to load\n sound.assetId = runtime.storage.defaultAssetId.Sound;\n sound.asset = runtime.storage.get(sound.assetId);\n sound.md5 = `${sound.assetId}.${sound.asset.dataFormat}`;\n\n return loadSoundFromAsset(sound, sound.asset, runtime, soundBank).then(loadedSound => {\n loadedSound.broken = {};\n loadedSound.broken.assetId = oldAssetId;\n loadedSound.broken.md5 = `${oldAssetId}.${oldDataFormat}`;\n\n // Should be null if we got here because the sound was missing\n loadedSound.broken.asset = oldAsset;\n \n loadedSound.broken.sampleCount = oldSample;\n loadedSound.broken.rate = oldRate;\n loadedSound.broken.format = oldFormat;\n loadedSound.broken.dataFormat = oldDataFormat;\n \n return loadedSound;\n });\n};\n\n/**\n * Load a sound's asset into memory asynchronously.\n * @param {!object} sound - the Scratch sound object.\n * @property {string} md5 - the MD5 and extension of the sound to be loaded.\n * @property {Buffer} data - sound data will be written here once loaded.\n * @param {!Runtime} runtime - Scratch runtime, used to access the storage module.\n * @param {SoundBank} soundBank - Scratch Audio SoundBank to add sounds to.\n * @returns {!Promise} - a promise which will resolve to the sound when ready.\n */\nconst loadSound = function (sound, runtime, soundBank) {\n if (!runtime.storage) {\n log.warn('No storage module present; cannot load sound asset: ', sound.md5);\n return Promise.resolve(sound);\n }\n const idParts = StringUtil.splitFirst(sound.md5, '.');\n const md5 = idParts[0];\n const ext = idParts[1].toLowerCase();\n sound.dataFormat = ext;\n return (\n (sound.asset && Promise.resolve(sound.asset)) ||\n runtime.storage.load(runtime.storage.AssetType.Sound, md5, ext)\n )\n .then(soundAsset => {\n sound.asset = soundAsset;\n\n if (!soundAsset) {\n log.warn('Failed to find sound data: ', sound.md5);\n return handleSoundLoadError(sound, runtime, soundBank);\n }\n\n return loadSoundFromAsset(sound, soundAsset, runtime, soundBank);\n })\n .catch(e => {\n log.warn(`Failed to load sound: ${sound.md5} with error: ${e}`);\n return handleSoundLoadError(sound, runtime, soundBank);\n });\n};\n\nmodule.exports = {\n loadSound,\n loadSoundFromAsset\n};\n","const JSONRPC = require('../util/jsonrpc');\n\nclass BLE extends JSONRPC {\n\n /**\n * A BLE peripheral socket object. It handles connecting, over web sockets, to\n * BLE peripherals, and reading and writing data to them.\n * @param {Runtime} runtime - the Runtime for sending/receiving GUI update events.\n * @param {string} extensionId - the id of the extension using this socket.\n * @param {object} peripheralOptions - the list of options for peripheral discovery.\n * @param {object} connectCallback - a callback for connection.\n * @param {object} resetCallback - a callback for resetting extension state.\n */\n constructor (runtime, extensionId, peripheralOptions, connectCallback, resetCallback = null) {\n super();\n\n this._socket = runtime.getScratchLinkSocket('BLE');\n this._socket.setOnOpen(this.requestPeripheral.bind(this));\n this._socket.setOnClose(this.handleDisconnectError.bind(this));\n this._socket.setOnError(this._handleRequestError.bind(this));\n this._socket.setHandleMessage(this._handleMessage.bind(this));\n\n this._sendMessage = this._socket.sendMessage.bind(this._socket);\n\n this._availablePeripherals = {};\n this._connectCallback = connectCallback;\n this._connected = false;\n this._characteristicDidChangeCallback = null;\n this._resetCallback = resetCallback;\n this._discoverTimeoutID = null;\n this._extensionId = extensionId;\n this._peripheralOptions = peripheralOptions;\n this._runtime = runtime;\n\n this._socket.open();\n }\n\n /**\n * Request connection to the peripheral.\n * If the web socket is not yet open, request when the socket promise resolves.\n */\n requestPeripheral () {\n this._availablePeripherals = {};\n if (this._discoverTimeoutID) {\n window.clearTimeout(this._discoverTimeoutID);\n }\n this._discoverTimeoutID = window.setTimeout(this._handleDiscoverTimeout.bind(this), 15000);\n this.sendRemoteRequest('discover', this._peripheralOptions)\n .catch(e => {\n this._handleRequestError(e);\n });\n }\n\n /**\n * Try connecting to the input peripheral id, and then call the connect\n * callback if connection is successful.\n * @param {number} id - the id of the peripheral to connect to\n */\n connectPeripheral (id) {\n this.sendRemoteRequest('connect', {peripheralId: id})\n .then(() => {\n this._connected = true;\n this._runtime.emit(this._runtime.constructor.PERIPHERAL_CONNECTED);\n this._connectCallback();\n })\n .catch(e => {\n this._handleRequestError(e);\n });\n }\n\n /**\n * Close the websocket.\n */\n disconnect () {\n if (this._connected) {\n this._connected = false;\n }\n\n if (this._socket.isOpen()) {\n this._socket.close();\n }\n\n if (this._discoverTimeoutID) {\n window.clearTimeout(this._discoverTimeoutID);\n }\n\n // Sets connection status icon to orange\n this._runtime.emit(this._runtime.constructor.PERIPHERAL_DISCONNECTED);\n }\n\n /**\n * @return {bool} whether the peripheral is connected.\n */\n isConnected () {\n return this._connected;\n }\n\n /**\n * Start receiving notifications from the specified ble service.\n * @param {number} serviceId - the ble service to read.\n * @param {number} characteristicId - the ble characteristic to get notifications from.\n * @param {object} onCharacteristicChanged - callback for characteristic change notifications.\n * @return {Promise} - a promise from the remote startNotifications request.\n */\n startNotifications (serviceId, characteristicId, onCharacteristicChanged = null) {\n const params = {\n serviceId,\n characteristicId\n };\n this._characteristicDidChangeCallback = onCharacteristicChanged;\n return this.sendRemoteRequest('startNotifications', params)\n .catch(e => {\n this.handleDisconnectError(e);\n });\n }\n\n /**\n * Read from the specified ble service.\n * @param {number} serviceId - the ble service to read.\n * @param {number} characteristicId - the ble characteristic to read.\n * @param {boolean} optStartNotifications - whether to start receiving characteristic change notifications.\n * @param {object} onCharacteristicChanged - callback for characteristic change notifications.\n * @return {Promise} - a promise from the remote read request.\n */\n read (serviceId, characteristicId, optStartNotifications = false, onCharacteristicChanged = null) {\n const params = {\n serviceId,\n characteristicId\n };\n if (optStartNotifications) {\n params.startNotifications = true;\n }\n if (onCharacteristicChanged) {\n this._characteristicDidChangeCallback = onCharacteristicChanged;\n }\n return this.sendRemoteRequest('read', params)\n .catch(e => {\n this.handleDisconnectError(e);\n });\n }\n\n /**\n * Write data to the specified ble service.\n * @param {number} serviceId - the ble service to write.\n * @param {number} characteristicId - the ble characteristic to write.\n * @param {string} message - the message to send.\n * @param {string} encoding - the message encoding type.\n * @param {boolean} withResponse - if true, resolve after peripheral's response.\n * @return {Promise} - a promise from the remote send request.\n */\n write (serviceId, characteristicId, message, encoding = null, withResponse = null) {\n const params = {serviceId, characteristicId, message};\n if (encoding) {\n params.encoding = encoding;\n }\n if (withResponse !== null) {\n params.withResponse = withResponse;\n }\n return this.sendRemoteRequest('write', params)\n .catch(e => {\n this.handleDisconnectError(e);\n });\n }\n\n /**\n * Handle a received call from the socket.\n * @param {string} method - a received method label.\n * @param {object} params - a received list of parameters.\n * @return {object} - optional return value.\n */\n didReceiveCall (method, params) {\n switch (method) {\n case 'didDiscoverPeripheral':\n this._availablePeripherals[params.peripheralId] = params;\n this._runtime.emit(\n this._runtime.constructor.PERIPHERAL_LIST_UPDATE,\n this._availablePeripherals\n );\n if (this._discoverTimeoutID) {\n window.clearTimeout(this._discoverTimeoutID);\n }\n break;\n case 'userDidPickPeripheral':\n this._availablePeripherals[params.peripheralId] = params;\n this._runtime.emit(\n this._runtime.constructor.USER_PICKED_PERIPHERAL,\n this._availablePeripherals\n );\n if (this._discoverTimeoutID) {\n window.clearTimeout(this._discoverTimeoutID);\n }\n break;\n case 'userDidNotPickPeripheral':\n this._runtime.emit(\n this._runtime.constructor.PERIPHERAL_SCAN_TIMEOUT\n );\n if (this._discoverTimeoutID) {\n window.clearTimeout(this._discoverTimeoutID);\n }\n break;\n case 'characteristicDidChange':\n if (this._characteristicDidChangeCallback) {\n this._characteristicDidChangeCallback(params.message);\n }\n break;\n case 'ping':\n return 42;\n }\n }\n\n /**\n * Handle an error resulting from losing connection to a peripheral.\n *\n * This could be due to:\n * - battery depletion\n * - going out of bluetooth range\n * - being powered down\n *\n * Disconnect the socket, and if the extension using this socket has a\n * reset callback, call it. Finally, emit an error to the runtime.\n */\n handleDisconnectError (/* e */) {\n // log.error(`BLE error: ${JSON.stringify(e)}`);\n\n if (!this._connected) return;\n\n this.disconnect();\n\n if (this._resetCallback) {\n this._resetCallback();\n }\n\n this._runtime.emit(this._runtime.constructor.PERIPHERAL_CONNECTION_LOST_ERROR, {\n message: `Scratch lost connection to`,\n extensionId: this._extensionId\n });\n }\n\n _handleRequestError (/* e */) {\n // log.error(`BLE error: ${JSON.stringify(e)}`);\n\n this._runtime.emit(this._runtime.constructor.PERIPHERAL_REQUEST_ERROR, {\n message: `Scratch lost connection to`,\n extensionId: this._extensionId\n });\n }\n\n _handleDiscoverTimeout () {\n if (this._discoverTimeoutID) {\n window.clearTimeout(this._discoverTimeoutID);\n }\n this._runtime.emit(this._runtime.constructor.PERIPHERAL_SCAN_TIMEOUT);\n }\n}\n\nmodule.exports = BLE;\n","const JSONRPC = require('../util/jsonrpc');\n\nclass BT extends JSONRPC {\n\n /**\n * A BT peripheral socket object. It handles connecting, over web sockets, to\n * BT peripherals, and reading and writing data to them.\n * @param {Runtime} runtime - the Runtime for sending/receiving GUI update events.\n * @param {string} extensionId - the id of the extension using this socket.\n * @param {object} peripheralOptions - the list of options for peripheral discovery.\n * @param {object} connectCallback - a callback for connection.\n * @param {object} resetCallback - a callback for resetting extension state.\n * @param {object} messageCallback - a callback for message sending.\n */\n constructor (runtime, extensionId, peripheralOptions, connectCallback, resetCallback = null, messageCallback) {\n super();\n\n this._socket = runtime.getScratchLinkSocket('BT');\n this._socket.setOnOpen(this.requestPeripheral.bind(this));\n this._socket.setOnError(this._handleRequestError.bind(this));\n this._socket.setOnClose(this.handleDisconnectError.bind(this));\n this._socket.setHandleMessage(this._handleMessage.bind(this));\n\n this._sendMessage = this._socket.sendMessage.bind(this._socket);\n\n this._availablePeripherals = {};\n this._connectCallback = connectCallback;\n this._connected = false;\n this._characteristicDidChangeCallback = null;\n this._resetCallback = resetCallback;\n this._discoverTimeoutID = null;\n this._extensionId = extensionId;\n this._peripheralOptions = peripheralOptions;\n this._messageCallback = messageCallback;\n this._runtime = runtime;\n\n this._socket.open();\n }\n\n /**\n * Request connection to the peripheral.\n * If the web socket is not yet open, request when the socket promise resolves.\n */\n requestPeripheral () {\n this._availablePeripherals = {};\n if (this._discoverTimeoutID) {\n window.clearTimeout(this._discoverTimeoutID);\n }\n this._discoverTimeoutID = window.setTimeout(this._handleDiscoverTimeout.bind(this), 15000);\n this.sendRemoteRequest('discover', this._peripheralOptions)\n .catch(\n e => this._handleRequestError(e)\n );\n }\n\n /**\n * Try connecting to the input peripheral id, and then call the connect\n * callback if connection is successful.\n * @param {number} id - the id of the peripheral to connect to\n * @param {string} pin - an optional pin for pairing\n */\n connectPeripheral (id, pin = null) {\n const params = {peripheralId: id};\n if (pin) {\n params.pin = pin;\n }\n this.sendRemoteRequest('connect', params)\n .then(() => {\n this._connected = true;\n this._runtime.emit(this._runtime.constructor.PERIPHERAL_CONNECTED);\n this._connectCallback();\n })\n .catch(e => {\n this._handleRequestError(e);\n });\n }\n\n /**\n * Close the websocket.\n */\n disconnect () {\n if (this._connected) {\n this._connected = false;\n }\n\n if (this._socket.isOpen()) {\n this._socket.close();\n }\n\n if (this._discoverTimeoutID) {\n window.clearTimeout(this._discoverTimeoutID);\n }\n\n // Sets connection status icon to orange\n this._runtime.emit(this._runtime.constructor.PERIPHERAL_DISCONNECTED);\n }\n\n /**\n * @return {bool} whether the peripheral is connected.\n */\n isConnected () {\n return this._connected;\n }\n\n sendMessage (options) {\n return this.sendRemoteRequest('send', options)\n .catch(e => {\n this.handleDisconnectError(e);\n });\n }\n\n /**\n * Handle a received call from the socket.\n * @param {string} method - a received method label.\n * @param {object} params - a received list of parameters.\n * @return {object} - optional return value.\n */\n didReceiveCall (method, params) {\n // TODO: Add peripheral 'undiscover' handling\n switch (method) {\n case 'didDiscoverPeripheral':\n this._availablePeripherals[params.peripheralId] = params;\n this._runtime.emit(\n this._runtime.constructor.PERIPHERAL_LIST_UPDATE,\n this._availablePeripherals\n );\n if (this._discoverTimeoutID) {\n window.clearTimeout(this._discoverTimeoutID);\n }\n break;\n case 'userDidPickPeripheral':\n this._availablePeripherals[params.peripheralId] = params;\n this._runtime.emit(\n this._runtime.constructor.USER_PICKED_PERIPHERAL,\n this._availablePeripherals\n );\n if (this._discoverTimeoutID) {\n window.clearTimeout(this._discoverTimeoutID);\n }\n break;\n case 'userDidNotPickPeripheral':\n this._runtime.emit(\n this._runtime.constructor.PERIPHERAL_SCAN_TIMEOUT\n );\n if (this._discoverTimeoutID) {\n window.clearTimeout(this._discoverTimeoutID);\n }\n break;\n case 'didReceiveMessage':\n this._messageCallback(params); // TODO: refine?\n break;\n default:\n return 'nah';\n }\n }\n\n /**\n * Handle an error resulting from losing connection to a peripheral.\n *\n * This could be due to:\n * - battery depletion\n * - going out of bluetooth range\n * - being powered down\n *\n * Disconnect the socket, and if the extension using this socket has a\n * reset callback, call it. Finally, emit an error to the runtime.\n */\n handleDisconnectError (/* e */) {\n // log.error(`BT error: ${JSON.stringify(e)}`);\n\n if (!this._connected) return;\n\n this.disconnect();\n\n if (this._resetCallback) {\n this._resetCallback();\n }\n\n this._runtime.emit(this._runtime.constructor.PERIPHERAL_CONNECTION_LOST_ERROR, {\n message: `Scratch lost connection to`,\n extensionId: this._extensionId\n });\n }\n\n _handleRequestError (/* e */) {\n // log.error(`BT error: ${JSON.stringify(e)}`);\n\n this._runtime.emit(this._runtime.constructor.PERIPHERAL_REQUEST_ERROR, {\n message: `Scratch lost connection to`,\n extensionId: this._extensionId\n });\n }\n\n _handleDiscoverTimeout () {\n if (this._discoverTimeoutID) {\n window.clearTimeout(this._discoverTimeoutID);\n }\n this._runtime.emit(this._runtime.constructor.PERIPHERAL_SCAN_TIMEOUT);\n }\n}\n\nmodule.exports = BT;\n","const Timer = require('../util/timer');\n\nclass Clock {\n constructor (runtime) {\n this._projectTimer = new Timer({now: () => runtime.currentMSecs});\n this._projectTimer.start();\n this._pausedTime = null;\n this._paused = false;\n /**\n * Reference to the owning Runtime.\n * @type{!Runtime}\n */\n this.runtime = runtime;\n }\n\n projectTimer () {\n if (this._paused) {\n return this._pausedTime / 1000;\n }\n return this._projectTimer.timeElapsed() / 1000;\n }\n\n pause () {\n this._paused = true;\n this._pausedTime = this._projectTimer.timeElapsed();\n }\n\n resume () {\n this._paused = false;\n const dt = this._projectTimer.timeElapsed() - this._pausedTime;\n this._projectTimer.startTime += dt;\n }\n\n resetProjectTimer () {\n this._projectTimer.start();\n }\n}\n\nmodule.exports = Clock;\n","const Variable = require('../engine/variable');\nconst log = require('../util/log');\n\nclass Cloud {\n /**\n * @typedef updateVariable\n * @param {string} name The name of the cloud variable to update on the server\n * @param {(string | number)} value The value to update the cloud variable with.\n */\n\n /**\n * A cloud data provider, responsible for managing the connection to the\n * cloud data server and for posting data about cloud data activity to\n * this IO device.\n * @typedef {object} CloudProvider\n * @property {updateVariable} updateVariable A function which sends a cloud variable\n * update to the cloud data server.\n * @property {Function} requestCloseConnection A function which closes\n * the connection to the cloud data server.\n */\n\n /**\n * Part of a cloud io data post indicating a cloud variable update.\n * @typedef {object} VarUpdateData\n * @property {string} name The name of the variable to update\n * @property {(number | string)} value The scalar value to update the variable with\n */\n\n /**\n * A cloud io data post message.\n * @typedef {object} CloudIOData\n * @property {VarUpdateData} varUpdate A {@link VarUpdateData} message indicating\n * a cloud variable update\n */\n\n /**\n * Cloud IO Device responsible for sending and receiving messages from\n * cloud provider (mananging the cloud server connection) and interacting\n * with cloud variables in the current project.\n * @param {Runtime} runtime The runtime context for this cloud io device.\n */\n constructor (runtime) {\n /**\n * Reference to the cloud data provider, responsible for mananging\n * the web socket connection to the cloud data server.\n * @type {?CloudProvider}\n */\n this.provider = null;\n\n /**\n * Reference to the runtime that owns this cloud io device.\n * @type {!Runtime}\n */\n this.runtime = runtime;\n\n /**\n * Reference to the stage target which owns the cloud variables\n * in the project.\n * @type {?Target}\n */\n this.stage = null;\n }\n\n /**\n * Set a reference to the cloud data provider.\n * @param {CloudProvider} provider The cloud data provider\n */\n setProvider (provider) {\n this.provider = provider;\n }\n\n /**\n * Set a reference to the stage target which owns the\n * cloud variables in the project.\n * @param {Target} stage The stage target\n */\n setStage (stage) {\n this.stage = stage;\n }\n\n /**\n * Handle incoming data to this io device.\n * @param {CloudIOData} data The {@link CloudIOData} object to process\n */\n postData (data) {\n if (data.varUpdate) {\n this.updateCloudVariable(data.varUpdate);\n }\n }\n\n requestCreateVariable (variable) {\n if (this.runtime.canAddCloudVariable()) {\n if (this.provider) {\n this.provider.createVariable(variable.name, variable.value);\n // We'll set the cloud flag and update the\n // cloud variable limit when we actually\n // get a confirmation from the cloud data server\n }\n } // TODO else track creation for later\n }\n\n /**\n * Request the cloud data provider to update the given variable with\n * the given value. Does nothing if this io device does not have a provider set.\n * @param {string} name The name of the variable to update\n * @param {string | number} value The value to update the variable with\n */\n requestUpdateVariable (name, value) {\n if (this.provider) {\n this.provider.updateVariable(name, value);\n }\n }\n\n /**\n * Request the cloud data provider to rename the variable with the given name\n * to the given new name. Does nothing if this io device does not have a provider set.\n * @param {string} oldName The name of the variable to rename\n * @param {string | number} newName The new name for the variable\n */\n requestRenameVariable (oldName, newName) {\n if (this.provider) {\n this.provider.renameVariable(oldName, newName);\n }\n }\n\n /**\n * Request the cloud data provider to delete the variable with the given name\n * Does nothing if this io device does not have a provider set.\n * @param {string} name The name of the variable to delete\n */\n requestDeleteVariable (name) {\n if (this.provider) {\n this.provider.deleteVariable(name);\n }\n }\n\n /**\n * Update a cloud variable in the runtime based on the message received\n * from the cloud provider.\n * @param {VarData} varUpdate A {@link VarData} object describing\n * a cloud variable update received from the cloud data provider.\n */\n updateCloudVariable (varUpdate) {\n const varName = varUpdate.name;\n\n const variable = this.stage.lookupVariableByNameAndType(varName, Variable.SCALAR_TYPE);\n if (!variable || !variable.isCloud) {\n log.warn(`Received an update for a cloud variable that does not exist: ${varName}`);\n return;\n }\n\n variable.value = varUpdate.value;\n }\n\n /**\n * Request the cloud data provider to close the web socket connection and\n * clear this io device of references to the cloud data provider and the\n * stage.\n */\n clear () {\n if (!this.provider) return;\n\n this.provider.requestCloseConnection();\n this.provider = null;\n this.stage = null;\n }\n}\n\nmodule.exports = Cloud;\n","const Cast = require('../util/cast');\n\n/**\n * Names used internally for keys used in scratch, also known as \"scratch keys\".\n * @enum {string}\n */\nconst KEY_NAME = {\n SPACE: 'space',\n LEFT: 'left arrow',\n UP: 'up arrow',\n RIGHT: 'right arrow',\n DOWN: 'down arrow',\n ENTER: 'enter'\n};\n\n/**\n * An array of the names of scratch keys.\n * @type {Array}\n */\nconst KEY_NAME_LIST = Object.keys(KEY_NAME).map(name => KEY_NAME[name]);\n\nclass Keyboard {\n constructor (runtime) {\n /**\n * List of currently pressed scratch keys.\n * A scratch key is:\n * A key you can press on a keyboard, excluding modifier keys.\n * An uppercase string of length one;\n * except for special key names for arrow keys and space (e.g. 'left arrow').\n * Can be a non-english unicode letter like: æ ø ש נ 手 廿.\n * @type{Array.}\n */\n this._keysPressed = [];\n /**\n * Reference to the owning Runtime.\n * Can be used, for example, to activate hats.\n * @type{!Runtime}\n */\n this.runtime = runtime;\n }\n\n /**\n * Convert from a keyboard event key name to a Scratch key name.\n * @param {string} keyString the input key string.\n * @return {string} the corresponding Scratch key, or an empty string.\n */\n _keyStringToScratchKey (keyString) {\n keyString = Cast.toString(keyString);\n // Convert space and arrow keys to their Scratch key names.\n switch (keyString) {\n case ' ': return KEY_NAME.SPACE;\n case 'ArrowLeft':\n case 'Left': return KEY_NAME.LEFT;\n case 'ArrowUp':\n case 'Up': return KEY_NAME.UP;\n case 'Right':\n case 'ArrowRight': return KEY_NAME.RIGHT;\n case 'Down':\n case 'ArrowDown': return KEY_NAME.DOWN;\n case 'Enter': return KEY_NAME.ENTER;\n }\n // Ignore modifier keys\n if (keyString.length > 1) {\n return '';\n }\n return keyString.toUpperCase();\n }\n\n /**\n * Convert from a block argument to a Scratch key name.\n * @param {string} keyArg the input arg.\n * @return {string} the corresponding Scratch key.\n */\n _keyArgToScratchKey (keyArg) {\n // If a number was dropped in, try to convert from ASCII to Scratch key.\n if (typeof keyArg === 'number') {\n // Check for the ASCII range containing numbers, some punctuation,\n // and uppercase letters.\n if (keyArg >= 48 && keyArg <= 90) {\n return String.fromCharCode(keyArg);\n }\n switch (keyArg) {\n case 32: return KEY_NAME.SPACE;\n case 37: return KEY_NAME.LEFT;\n case 38: return KEY_NAME.UP;\n case 39: return KEY_NAME.RIGHT;\n case 40: return KEY_NAME.DOWN;\n }\n }\n\n keyArg = Cast.toString(keyArg);\n\n // If the arg matches a special key name, return it.\n if (KEY_NAME_LIST.includes(keyArg)) {\n return keyArg;\n }\n\n // Use only the first character.\n if (keyArg.length > 1) {\n keyArg = keyArg[0];\n }\n\n // Check for the space character.\n if (keyArg === ' ') {\n return KEY_NAME.SPACE;\n }\n\n return keyArg.toUpperCase();\n }\n\n /**\n * Keyboard DOM event handler.\n * @param {object} data Data from DOM event.\n */\n postData (data) {\n if (!data.key) return;\n const scratchKey = this._keyStringToScratchKey(data.key);\n if (scratchKey === '') return;\n const index = this._keysPressed.indexOf(scratchKey);\n if (data.isDown) {\n this.runtime.emit('KEY_PRESSED', scratchKey);\n // If not already present, add to the list.\n if (index < 0) {\n this._keysPressed.push(scratchKey);\n }\n } else if (index > -1) {\n // If already present, remove from the list.\n this._keysPressed.splice(index, 1);\n }\n }\n\n /**\n * Get key down state for a specified key.\n * @param {Any} keyArg key argument.\n * @return {boolean} Is the specified key down?\n */\n getKeyIsDown (keyArg) {\n if (keyArg === 'any') {\n return this._keysPressed.length > 0;\n }\n const scratchKey = this._keyArgToScratchKey(keyArg);\n return this._keysPressed.indexOf(scratchKey) > -1;\n }\n}\n\nmodule.exports = Keyboard;\n","const MathUtil = require('../util/math-util');\n\nclass Mouse {\n constructor (runtime) {\n this._x = 0;\n this._y = 0;\n this._isDown = false;\n /**\n * Reference to the owning Runtime.\n * Can be used, for example, to activate hats.\n * @type{!Runtime}\n */\n this.runtime = runtime;\n }\n\n /**\n * Activate \"event_whenthisspriteclicked\" hats.\n * @param {Target} target to trigger hats on.\n * @private\n */\n _activateClickHats (target) {\n // Activate both \"this sprite clicked\" and \"stage clicked\"\n // They were separated into two opcodes for labeling,\n // but should act the same way.\n // Intentionally not checking isStage to make it work when sharing blocks.\n // @todo the blocks should be converted from one to another when shared\n this.runtime.startHats('event_whenthisspriteclicked',\n null, target);\n this.runtime.startHats('event_whenstageclicked',\n null, target);\n }\n\n /**\n * Find a target by XY location\n * @param {number} x X position to be sent to the renderer.\n * @param {number} y Y position to be sent to the renderer.\n * @return {Target} the target at that location\n * @private\n */\n _pickTarget (x, y) {\n if (this.runtime.renderer) {\n const drawableID = this.runtime.renderer.pick(x, y);\n for (let i = 0; i < this.runtime.targets.length; i++) {\n const target = this.runtime.targets[i];\n if (Object.prototype.hasOwnProperty.call(target, 'drawableID') &&\n target.drawableID === drawableID) {\n return target;\n }\n }\n }\n // Return the stage if no target was found\n return this.runtime.getTargetForStage();\n }\n\n /**\n * Mouse DOM event handler.\n * @param {object} data Data from DOM event.\n */\n postData (data) {\n if (data.x) {\n this._clientX = data.x;\n this._scratchX = Math.round(MathUtil.clamp(\n 480 * ((data.x / data.canvasWidth) - 0.5),\n -240,\n 240\n ));\n }\n if (data.y) {\n this._clientY = data.y;\n this._scratchY = Math.round(MathUtil.clamp(\n -360 * ((data.y / data.canvasHeight) - 0.5),\n -180,\n 180\n ));\n }\n if (typeof data.isDown !== 'undefined') {\n const previousDownState = this._isDown;\n this._isDown = data.isDown;\n\n // Do not trigger if down state has not changed\n if (previousDownState === this._isDown) return;\n\n // Never trigger click hats at the end of a drag\n if (data.wasDragged) return;\n\n // Do not activate click hats for clicks outside canvas bounds\n if (!(data.x > 0 && data.x < data.canvasWidth &&\n data.y > 0 && data.y < data.canvasHeight)) return;\n\n const target = this._pickTarget(data.x, data.y);\n const isNewMouseDown = !previousDownState && this._isDown;\n const isNewMouseUp = previousDownState && !this._isDown;\n\n // Draggable targets start click hats on mouse up.\n // Non-draggable targets start click hats on mouse down.\n if (target.draggable && isNewMouseUp) {\n this._activateClickHats(target);\n } else if (!target.draggable && isNewMouseDown) {\n this._activateClickHats(target);\n }\n }\n }\n\n /**\n * Get the X position of the mouse in client coordinates.\n * @return {number} Non-clamped X position of the mouse cursor.\n */\n getClientX () {\n return this._clientX;\n }\n\n /**\n * Get the Y position of the mouse in client coordinates.\n * @return {number} Non-clamped Y position of the mouse cursor.\n */\n getClientY () {\n return this._clientY;\n }\n\n /**\n * Get the X position of the mouse in scratch coordinates.\n * @return {number} Clamped and integer rounded X position of the mouse cursor.\n */\n getScratchX () {\n return this._scratchX;\n }\n\n /**\n * Get the Y position of the mouse in scratch coordinates.\n * @return {number} Clamped and integer rounded Y position of the mouse cursor.\n */\n getScratchY () {\n return this._scratchY;\n }\n\n /**\n * Get the down state of the mouse.\n * @return {boolean} Is the mouse down?\n */\n getIsDown () {\n return this._isDown;\n }\n}\n\nmodule.exports = Mouse;\n","class MouseWheel {\n constructor (runtime) {\n /**\n * Reference to the owning Runtime.\n * @type{!Runtime}\n */\n this.runtime = runtime;\n }\n\n /**\n * Mouse wheel DOM event handler.\n * @param {object} data Data from DOM event.\n */\n postData (data) {\n const matchFields = {};\n if (data.deltaY < 0) {\n matchFields.KEY_OPTION = 'up arrow';\n } else if (data.deltaY > 0) {\n matchFields.KEY_OPTION = 'down arrow';\n } else {\n return;\n }\n\n this.runtime.startHats('event_whenkeypressed', matchFields);\n }\n}\n\nmodule.exports = MouseWheel;\n","class UserData {\n constructor () {\n this._username = '';\n }\n\n /**\n * Handler for updating the username\n * @param {object} data Data posted to this ioDevice.\n * @property {!string} username The new username.\n */\n postData (data) {\n this._username = data.username;\n }\n\n /**\n * Getter for username. Initially empty string, until set via postData.\n * @returns {!string} The current username\n */\n getUsername () {\n return this._username;\n }\n}\n\nmodule.exports = UserData;\n","const StageLayering = require('../engine/stage-layering');\n\nclass Video {\n constructor (runtime) {\n this.runtime = runtime;\n\n /**\n * @typedef VideoProvider\n * @property {Function} enableVideo - Requests camera access from the user, and upon success,\n * enables the video feed\n * @property {Function} disableVideo - Turns off the video feed\n * @property {Function} getFrame - Return frame data from the video feed in\n * specified dimensions, format, and mirroring.\n */\n this.provider = null;\n\n /**\n * Id representing a Scratch Renderer skin the video is rendered to for\n * previewing.\n * @type {number}\n */\n this._skinId = -1;\n\n /**\n * Id for a drawable using the video's skin that will render as a video\n * preview.\n * @type {Drawable}\n */\n this._drawable = -1;\n\n /**\n * Store the last state of the video transparency ghost effect\n * @type {number}\n */\n this._ghost = 0;\n\n /**\n * Store a flag that allows the preview to be forced transparent.\n * @type {number}\n */\n this._forceTransparentPreview = false;\n }\n\n static get FORMAT_IMAGE_DATA () {\n return 'image-data';\n }\n\n static get FORMAT_CANVAS () {\n return 'canvas';\n }\n\n /**\n * Dimensions the video stream is analyzed at after its rendered to the\n * sample canvas.\n * @type {Array.}\n */\n static get DIMENSIONS () {\n return [480, 360];\n }\n\n /**\n * Order preview drawable is inserted at in the renderer.\n * @type {number}\n */\n static get ORDER () {\n return 1;\n }\n\n /**\n * Set a video provider for this device. A default implementation of\n * a video provider can be found in scratch-gui/src/lib/video/video-provider\n * @param {VideoProvider} provider - Video provider to use\n */\n setProvider (provider) {\n this.provider = provider;\n }\n\n /**\n * Request video be enabled. Sets up video, creates video skin and enables preview.\n *\n * ioDevices.video.requestVideo()\n *\n * @return {Promise.