From 2bd148f8c128c666a2919ecd86d4f0465e2df247 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 15 Sep 2016 00:50:28 -0400 Subject: [PATCH 0001/1971] Initial commit --- packages/scratch-gui/package.json | 35 +++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 packages/scratch-gui/package.json diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json new file mode 100644 index 0000000000..65b347cd3f --- /dev/null +++ b/packages/scratch-gui/package.json @@ -0,0 +1,35 @@ +{ + "name": "scratch-gui", + "version": "0.1.0", + "description": "GraphicaL User Interface for creating and running Scratch 3.0 projects", + "main": "./src/index.js", + "scripts": { + "start": "make serve" + }, + "author": "Massachusetts Institute of Technology", + "license": "BSD-3-Clause", + "homepage": "https://github.com/LLK/scratch-gui#readme", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/LLK/scratch-gui.git" + }, + "devDependencies": { + "babel-core": "6.14.0", + "babel-eslint": "6.1.2", + "babel-loader": "6.2.5", + "babel-preset-es2015": "6.14.0", + "babel-preset-react": "6.11.1", + "eslint": "3.5.0", + "eslint-plugin-react": "6.2.1", + "exports-loader": "0.6.3", + "imports-loader": "0.6.5", + "json-loader": "0.5.4", + "react": "15.3.1", + "react-dom": "15.3.1", + "scratch-blocks": "github:llk/scratch-blocks#develop", + "scratch-render": "github:llk/scratch-render#develop", + "scratch-vm": "github:llk/scratch-vm#develop", + "webpack": "1.13.2", + "webpack-dev-server": "1.15.2" + } +} From c54701786090ebd500192976347ccc4d4322f7de Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 15 Sep 2016 12:49:07 -0400 Subject: [PATCH 0002/1971] Add default blocks options and validation Add skeleton toolbox component --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 65b347cd3f..0b15879fb8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -24,6 +24,7 @@ "exports-loader": "0.6.3", "imports-loader": "0.6.5", "json-loader": "0.5.4", + "lodash.defaultsdeep": "4.4.0", "react": "15.3.1", "react-dom": "15.3.1", "scratch-blocks": "github:llk/scratch-blocks#develop", From 0e2182ff915d4d733388cff4ba6562e7ef346a17 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 15 Sep 2016 12:55:58 -0400 Subject: [PATCH 0003/1971] Set up Travis --- packages/scratch-gui/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0b15879fb8..be2cd7560a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -4,7 +4,8 @@ "description": "GraphicaL User Interface for creating and running Scratch 3.0 projects", "main": "./src/index.js", "scripts": { - "start": "make serve" + "start": "make serve", + "test": "make test" }, "author": "Massachusetts Institute of Technology", "license": "BSD-3-Clause", From cd11232adcd0d7eb03eb30b18f3291fdc8b172e4 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 15 Sep 2016 16:50:10 -0400 Subject: [PATCH 0004/1971] The playground is the product --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index be2cd7560a..90887ace22 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -23,6 +23,7 @@ "eslint": "3.5.0", "eslint-plugin-react": "6.2.1", "exports-loader": "0.6.3", + "html-webpack-plugin": "2.22.0", "imports-loader": "0.6.5", "json-loader": "0.5.4", "lodash.defaultsdeep": "4.4.0", From f9310e63fe096bfc48c8a76d24a46fbda1631e42 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 16 Sep 2016 18:25:44 -0400 Subject: [PATCH 0005/1971] Un-shim scratch-blocks, it shims itself now --- packages/scratch-gui/package.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 90887ace22..d9d8e3cf22 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -22,9 +22,7 @@ "babel-preset-react": "6.11.1", "eslint": "3.5.0", "eslint-plugin-react": "6.2.1", - "exports-loader": "0.6.3", "html-webpack-plugin": "2.22.0", - "imports-loader": "0.6.5", "json-loader": "0.5.4", "lodash.defaultsdeep": "4.4.0", "react": "15.3.1", From 7d58067f8acd21a72a3e7ea7ce1e014780aa2004 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 16 Sep 2016 18:29:52 -0400 Subject: [PATCH 0006/1971] Make React a peer dependency Multiple installations of React can cause issues if the package is used just for the components. --- packages/scratch-gui/package.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d9d8e3cf22..a1c0397e3c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -14,6 +14,10 @@ "type": "git", "url": "git+ssh://git@github.com/LLK/scratch-gui.git" }, + "peerDependencies": { + "react": "15.3.1", + "react-dom": "15.3.1" + }, "devDependencies": { "babel-core": "6.14.0", "babel-eslint": "6.1.2", @@ -25,8 +29,6 @@ "html-webpack-plugin": "2.22.0", "json-loader": "0.5.4", "lodash.defaultsdeep": "4.4.0", - "react": "15.3.1", - "react-dom": "15.3.1", "scratch-blocks": "github:llk/scratch-blocks#develop", "scratch-render": "github:llk/scratch-render#develop", "scratch-vm": "github:llk/scratch-vm#develop", From e4361c60262669ef57f3a59854b61d2dd86517cb Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 22 Sep 2016 12:42:10 -0400 Subject: [PATCH 0007/1971] Deploy to gh-pages --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a1c0397e3c..6a2f3f702f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -26,6 +26,7 @@ "babel-preset-react": "6.11.1", "eslint": "3.5.0", "eslint-plugin-react": "6.2.1", + "gh-pages": "0.11.0", "html-webpack-plugin": "2.22.0", "json-loader": "0.5.4", "lodash.defaultsdeep": "4.4.0", From fbfef003bf2d0217aa52046c8648435d7f46d91b Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Sun, 25 Sep 2016 14:57:04 -0400 Subject: [PATCH 0008/1971] Switch to published packages Now they don't have to be built and rebuilt every npm install --- packages/scratch-gui/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6a2f3f702f..3b24511470 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -30,9 +30,9 @@ "html-webpack-plugin": "2.22.0", "json-loader": "0.5.4", "lodash.defaultsdeep": "4.4.0", - "scratch-blocks": "github:llk/scratch-blocks#develop", - "scratch-render": "github:llk/scratch-render#develop", - "scratch-vm": "github:llk/scratch-vm#develop", + "scratch-blocks": "0.1.0-27fbe", + "scratch-render": "0.1.0-9f26a", + "scratch-vm": "0.1.0-9f59e", "webpack": "1.13.2", "webpack-dev-server": "1.15.2" } From 38598300a961ce4874dcf363f5b3a0806fecb428 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 26 Sep 2016 10:21:39 -0400 Subject: [PATCH 0009/1971] Always use the most recent scratch-* packages --- packages/scratch-gui/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3b24511470..c1aa6d627d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -30,9 +30,9 @@ "html-webpack-plugin": "2.22.0", "json-loader": "0.5.4", "lodash.defaultsdeep": "4.4.0", - "scratch-blocks": "0.1.0-27fbe", - "scratch-render": "0.1.0-9f26a", - "scratch-vm": "0.1.0-9f59e", + "scratch-blocks": "*", + "scratch-render": "*", + "scratch-vm": "*", "webpack": "1.13.2", "webpack-dev-server": "1.15.2" } From 69600fff940f9dcd52ea7deabf80868bfe50b334 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 26 Sep 2016 11:02:26 -0400 Subject: [PATCH 0010/1971] Don't be strict about the React dependencies Because these are peer dependencies, we should be lenient about the required version of React required to use the components. I didn't use the caret because the x's work and it's obvious what they mean, unlike ^ and ~. --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c1aa6d627d..866d0dec64 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -15,8 +15,8 @@ "url": "git+ssh://git@github.com/LLK/scratch-gui.git" }, "peerDependencies": { - "react": "15.3.1", - "react-dom": "15.3.1" + "react": "15.x.x", + "react-dom": "15.x.x" }, "devDependencies": { "babel-core": "6.14.0", From 4e460d482ac7a86117bf015434206d3d3e660d79 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 27 Sep 2016 10:42:55 -0400 Subject: [PATCH 0011/1971] Build in same node environments as scratch-* --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 866d0dec64..5b60df0974 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -33,6 +33,7 @@ "scratch-blocks": "*", "scratch-render": "*", "scratch-vm": "*", + "travis-after-all": "1.4.4", "webpack": "1.13.2", "webpack-dev-server": "1.15.2" } From b54812144a9f47c37b1c31bc6bf5bb187d1e3e11 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 27 Sep 2016 10:46:22 -0400 Subject: [PATCH 0012/1971] Use semver-friendly version format for scratch-* --- packages/scratch-gui/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5b60df0974..dc16551730 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -30,9 +30,9 @@ "html-webpack-plugin": "2.22.0", "json-loader": "0.5.4", "lodash.defaultsdeep": "4.4.0", - "scratch-blocks": "*", - "scratch-render": "*", - "scratch-vm": "*", + "scratch-blocks": "^0.1.0-prerelease", + "scratch-render": "^0.1.0-prerelease", + "scratch-vm": "^0.1.0-prerelease", "travis-after-all": "1.4.4", "webpack": "1.13.2", "webpack-dev-server": "1.15.2" From 1ebc93b8cb7882f63f033476cfc8a2c5bb4f1963 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 27 Sep 2016 16:41:01 -0400 Subject: [PATCH 0013/1971] Use travis_after_all fork for private repos --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index dc16551730..3f16f50625 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -33,7 +33,7 @@ "scratch-blocks": "^0.1.0-prerelease", "scratch-render": "^0.1.0-prerelease", "scratch-vm": "^0.1.0-prerelease", - "travis-after-all": "1.4.4", + "travis-after-all": "jamesarosen/travis-after-all#override-api-urls", "webpack": "1.13.2", "webpack-dev-server": "1.15.2" } From ea6af325e5d7d5e3d57d23af4f625d465b8101b5 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 28 Sep 2016 09:48:39 -0400 Subject: [PATCH 0014/1971] Provide Blockly media to playground --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3f16f50625..6ea3edebda 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -24,6 +24,7 @@ "babel-loader": "6.2.5", "babel-preset-es2015": "6.14.0", "babel-preset-react": "6.11.1", + "copy-webpack-plugin": "3.0.1", "eslint": "3.5.0", "eslint-plugin-react": "6.2.1", "gh-pages": "0.11.0", From 18ecc465fe574781e4ea638faea44ac9dbb614b5 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 28 Sep 2016 10:53:37 -0400 Subject: [PATCH 0015/1971] Add project loading --- packages/scratch-gui/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6ea3edebda..410302db78 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -36,6 +36,7 @@ "scratch-vm": "^0.1.0-prerelease", "travis-after-all": "jamesarosen/travis-after-all#override-api-urls", "webpack": "1.13.2", - "webpack-dev-server": "1.15.2" + "webpack-dev-server": "1.15.2", + "xhr": "2.2.2" } } From aa064c01a0348272c92c5b17b32511bda96fc5c0 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 12 Oct 2016 23:22:15 -0400 Subject: [PATCH 0016/1971] Update project on hash change While doing so: - Use ProjectLoader for loading projects - Stop using loadEmptyProject and supply empty project data - Move project loading to App, GUI now receives data (as a string - the vm should receive an object eventually) - Add logging --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 410302db78..deb574bff3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -31,6 +31,7 @@ "html-webpack-plugin": "2.22.0", "json-loader": "0.5.4", "lodash.defaultsdeep": "4.4.0", + "minilog": "3.0.1", "scratch-blocks": "^0.1.0-prerelease", "scratch-render": "^0.1.0-prerelease", "scratch-vm": "^0.1.0-prerelease", From 6aba7426e63d011e2552e9c6fe3467560975eb22 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 13 Oct 2016 16:48:40 -0400 Subject: [PATCH 0017/1971] Install React as a dev dependency Once another project uses this package, these should become peerDependencies. Until then install automatically. --- packages/scratch-gui/package.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index deb574bff3..50f7f7ac0f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -14,10 +14,6 @@ "type": "git", "url": "git+ssh://git@github.com/LLK/scratch-gui.git" }, - "peerDependencies": { - "react": "15.x.x", - "react-dom": "15.x.x" - }, "devDependencies": { "babel-core": "6.14.0", "babel-eslint": "6.1.2", @@ -32,6 +28,8 @@ "json-loader": "0.5.4", "lodash.defaultsdeep": "4.4.0", "minilog": "3.0.1", + "react": "15.x.x", + "react-dom": "15.x.x", "scratch-blocks": "^0.1.0-prerelease", "scratch-render": "^0.1.0-prerelease", "scratch-vm": "^0.1.0-prerelease", From 84f4f80a1753e18a1db07a39271378d5224ca4f0 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 14 Oct 2016 00:37:48 -0400 Subject: [PATCH 0018/1971] Decoupling refactor Use fewer refs, these had weird behavior that caused the order of components to matter. Attach events eagerly, as soon as the necessary objects are available. Make VMManager an actual class instantiated with a VM so we don't have to pass it to every method. Use bindAll everywhere for convenience. --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 50f7f7ac0f..0e1444ce0d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -26,6 +26,7 @@ "gh-pages": "0.11.0", "html-webpack-plugin": "2.22.0", "json-loader": "0.5.4", + "lodash.bindall": "4.4.0", "lodash.defaultsdeep": "4.4.0", "minilog": "3.0.1", "react": "15.x.x", From 0ee3c12c5c18363fc0da7c1a4175fea63d799704 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Sun, 16 Oct 2016 23:47:20 -0400 Subject: [PATCH 0019/1971] Move Makefile into npm scripts --- packages/scratch-gui/package.json | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0e1444ce0d..91a9c31080 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -3,9 +3,16 @@ "version": "0.1.0", "description": "GraphicaL User Interface for creating and running Scratch 3.0 projects", "main": "./src/index.js", + "config": { + "port": 8061 + }, "scripts": { - "start": "make serve", - "test": "make test" + "build": "npm run clean && webpack --progress --colors --bail", + "clean": "rm -rf ./build && mkdir -p build", + "deploy": "gh-pages -x -r $GH_PAGES_URL -d build -m $DEPLOY_MESSAGE", + "lint": "eslint .", + "start": "webpack-dev-server --port $npm_package_config_port --content-base=./build", + "test": "npm run lint && npm run build" }, "author": "Massachusetts Institute of Technology", "license": "BSD-3-Clause", From 79f6c4000adee92b2b31c2b9c14e3ae26bcaba67 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Sun, 16 Oct 2016 23:53:05 -0400 Subject: [PATCH 0020/1971] Add git hooks to avoid pushing broken builds --- packages/scratch-gui/package.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 91a9c31080..3e59332d19 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -11,6 +11,10 @@ "clean": "rm -rf ./build && mkdir -p build", "deploy": "gh-pages -x -r $GH_PAGES_URL -d build -m $DEPLOY_MESSAGE", "lint": "eslint .", + "postmerge": "npm install", + "postrewrite": "npm install", + "precommit": "npm run lint", + "prepush": "npm run test", "start": "webpack-dev-server --port $npm_package_config_port --content-base=./build", "test": "npm run lint && npm run build" }, @@ -32,6 +36,7 @@ "eslint-plugin-react": "6.2.1", "gh-pages": "0.11.0", "html-webpack-plugin": "2.22.0", + "husky": "0.11.9", "json-loader": "0.5.4", "lodash.bindall": "4.4.0", "lodash.defaultsdeep": "4.4.0", From 16f1306df51033d8fc3fa2fb2ceb30c9dbf4131d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 17 Oct 2016 00:07:46 -0400 Subject: [PATCH 0021/1971] Opt in to git hooks --- packages/scratch-gui/package.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3e59332d19..c09278ba5a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -11,10 +11,10 @@ "clean": "rm -rf ./build && mkdir -p build", "deploy": "gh-pages -x -r $GH_PAGES_URL -d build -m $DEPLOY_MESSAGE", "lint": "eslint .", - "postmerge": "npm install", - "postrewrite": "npm install", - "precommit": "npm run lint", - "prepush": "npm run test", + "postmerge": "opt --in postmerge --exec 'npm install'", + "postrewrite": "opt --in postrewrite --exec 'npm install'", + "precommit": "opt --in precommit --exec 'npm run lint'", + "prepush": "opt --in prepush --exec 'npm run test'", "start": "webpack-dev-server --port $npm_package_config_port --content-base=./build", "test": "npm run lint && npm run build" }, @@ -41,6 +41,7 @@ "lodash.bindall": "4.4.0", "lodash.defaultsdeep": "4.4.0", "minilog": "3.0.1", + "opt-cli": "1.5.1", "react": "15.x.x", "react-dom": "15.x.x", "scratch-blocks": "^0.1.0-prerelease", From a85df5c8214a82d95dc844458ff9887a2b21a473 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 17 Oct 2016 11:04:54 -0400 Subject: [PATCH 0022/1971] Fix default port config --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c09278ba5a..6f3b384495 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -4,7 +4,7 @@ "description": "GraphicaL User Interface for creating and running Scratch 3.0 projects", "main": "./src/index.js", "config": { - "port": 8061 + "port": 8601 }, "scripts": { "build": "npm run clean && webpack --progress --colors --bail", From 95461b2d272a1ea4898a4ed7b019cf9b5d521494 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 19 Oct 2016 08:36:23 -0400 Subject: [PATCH 0023/1971] Fix scratch-* dependency version updates `npm update` does what we want and is less hacky than removing the packages entirely first. Specify latest as the version so we always get the latest version even though I erroneously installed scratch-blocks@0.1.0. --- packages/scratch-gui/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6f3b384495..5fd9e464c0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -44,9 +44,9 @@ "opt-cli": "1.5.1", "react": "15.x.x", "react-dom": "15.x.x", - "scratch-blocks": "^0.1.0-prerelease", - "scratch-render": "^0.1.0-prerelease", - "scratch-vm": "^0.1.0-prerelease", + "scratch-blocks": "latest", + "scratch-render": "latest", + "scratch-vm": "latest", "travis-after-all": "jamesarosen/travis-after-all#override-api-urls", "webpack": "1.13.2", "webpack-dev-server": "1.15.2", From e680868dfc574b1ae42501e9fc9fbb406897e4b7 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 19 Oct 2016 09:26:30 -0400 Subject: [PATCH 0024/1971] Allow configuration of GUI subcomponent props So that you can adjust them without redefining the entire GUI. Also allow adjusting props passed to GUIComponent. --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5fd9e464c0..d85b534bd9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -29,6 +29,7 @@ "babel-core": "6.14.0", "babel-eslint": "6.1.2", "babel-loader": "6.2.5", + "babel-plugin-transform-object-rest-spread": "6.16.0", "babel-preset-es2015": "6.14.0", "babel-preset-react": "6.11.1", "copy-webpack-plugin": "3.0.1", From 8e0b9c542b19b373aab5858d90bf1b2337f1fb8d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 19 Oct 2016 09:31:46 -0400 Subject: [PATCH 0025/1971] Update deploy script to match vm Makes local deployment easier --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d85b534bd9..919cd38652 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -9,7 +9,7 @@ "scripts": { "build": "npm run clean && webpack --progress --colors --bail", "clean": "rm -rf ./build && mkdir -p build", - "deploy": "gh-pages -x -r $GH_PAGES_URL -d build -m $DEPLOY_MESSAGE", + "deploy": "gh-pages -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", "lint": "eslint .", "postmerge": "opt --in postmerge --exec 'npm install'", "postrewrite": "opt --in postrewrite --exec 'npm install'", From 07290dc65ea72b75c3acd6f4f55221354df93d62 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Fri, 21 Oct 2016 09:54:41 -0400 Subject: [PATCH 0026/1971] Merge pull request #7 from tmickel/feature/libraries Initial rough sprite/costume/backdrop libraries --- packages/scratch-gui/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 919cd38652..e892dc02d5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -45,9 +45,11 @@ "opt-cli": "1.5.1", "react": "15.x.x", "react-dom": "15.x.x", + "react-modal": "1.5.2", "scratch-blocks": "latest", "scratch-render": "latest", "scratch-vm": "latest", + "svg-to-image": "1.1.3", "travis-after-all": "jamesarosen/travis-after-all#override-api-urls", "webpack": "1.13.2", "webpack-dev-server": "1.15.2", From 63dfc300ee15325e81543a8389b253a38707020b Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 21 Oct 2016 12:14:02 -0400 Subject: [PATCH 0027/1971] Merge pull request #8 from rschamp/react-peers Pin React devDependencies, add peerDependencies --- packages/scratch-gui/package.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e892dc02d5..633b2a27cb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -25,6 +25,10 @@ "type": "git", "url": "git+ssh://git@github.com/LLK/scratch-gui.git" }, + "peerDependencies": { + "react": "15.x.x", + "react-dom": "15.x.x" + }, "devDependencies": { "babel-core": "6.14.0", "babel-eslint": "6.1.2", @@ -43,8 +47,8 @@ "lodash.defaultsdeep": "4.4.0", "minilog": "3.0.1", "opt-cli": "1.5.1", - "react": "15.x.x", - "react-dom": "15.x.x", + "react": "15.3.2", + "react-dom": "15.3.2", "react-modal": "1.5.2", "scratch-blocks": "latest", "scratch-render": "latest", From 8bfeac2446f58bd0b4b525cc542215bffeba34f2 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 24 Oct 2016 09:43:49 -0400 Subject: [PATCH 0028/1971] Merge pull request #9 from rschamp/lint eslint-config-scratch: React + ES6 Edition --- packages/scratch-gui/package.json | 9 +- .../scratch-gui/src/containers/blocks.jsx | 135 ++++++++++++++++++ packages/scratch-gui/src/containers/stage.jsx | 108 ++++++++++++++ 3 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 packages/scratch-gui/src/containers/blocks.jsx create mode 100644 packages/scratch-gui/src/containers/stage.jsx diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 633b2a27cb..9658b79dca 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -18,6 +18,9 @@ "start": "webpack-dev-server --port $npm_package_config_port --content-base=./build", "test": "npm run lint && npm run build" }, + "eslintConfig": { + "extends": ["scratch"] + }, "author": "Massachusetts Institute of Technology", "license": "BSD-3-Clause", "homepage": "https://github.com/LLK/scratch-gui#readme", @@ -31,14 +34,15 @@ }, "devDependencies": { "babel-core": "6.14.0", - "babel-eslint": "6.1.2", + "babel-eslint": "7.0.0", "babel-loader": "6.2.5", "babel-plugin-transform-object-rest-spread": "6.16.0", "babel-preset-es2015": "6.14.0", "babel-preset-react": "6.11.1", "copy-webpack-plugin": "3.0.1", "eslint": "3.5.0", - "eslint-plugin-react": "6.2.1", + "eslint-config-scratch": "^1.0.0", + "eslint-plugin-react": "6.4.1", "gh-pages": "0.11.0", "html-webpack-plugin": "2.22.0", "husky": "0.11.9", @@ -50,6 +54,7 @@ "react": "15.3.2", "react-dom": "15.3.2", "react-modal": "1.5.2", + "react-style-proptype": "1.2.0", "scratch-blocks": "latest", "scratch-render": "latest", "scratch-vm": "latest", diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx new file mode 100644 index 0000000000..a5977a9f76 --- /dev/null +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -0,0 +1,135 @@ +const bindAll = require('lodash.bindall'); +const defaultsDeep = require('lodash.defaultsdeep'); +const React = require('react'); +const ScratchBlocks = require('scratch-blocks'); +const VM = require('scratch-vm'); + +const BlocksComponent = require('../components/blocks.jsx'); + +class Blocks extends React.Component { + constructor (props) { + super(props); + bindAll(this, [ + 'attachVM', + 'detachVM', + 'onStackGlowOn', + 'onStackGlowOff', + 'onBlockGlowOn', + 'onBlockGlowOff', + 'onVisualReport', + 'onWorkspaceUpdate' + ]); + } + componentDidMount () { + const workspaceConfig = defaultsDeep({}, Blocks.defaultOptions, this.props.options); + this.workspace = ScratchBlocks.inject(this.blocks, workspaceConfig); + this.attachVM(); + } + componentWillUnmount () { + this.detachVM(); + this.workspace.dispose(); + } + attachVM () { + this.workspace.addChangeListener(this.props.vm.blockListener); + this.workspace + .getFlyout() + .getWorkspace() + .addChangeListener(this.props.vm.flyoutBlockListener); + this.props.vm.on('STACK_GLOW_ON', this.onStackGlowOn); + this.props.vm.on('STACK_GLOW_OFF', this.onStackGlowOff); + this.props.vm.on('BLOCK_GLOW_ON', this.onBlockGlowOn); + this.props.vm.on('BLOCK_GLOW_OFF', this.onBlockGlowOff); + this.props.vm.on('VISUAL_REPORT', this.onVisualReport); + this.props.vm.on('workspaceUpdate', this.onWorkspaceUpdate); + } + detachVM () { + this.props.vm.off('STACK_GLOW_ON', this.onStackGlowOn); + this.props.vm.off('STACK_GLOW_OFF', this.onStackGlowOff); + this.props.vm.off('BLOCK_GLOW_ON', this.onBlockGlowOn); + this.props.vm.off('BLOCK_GLOW_OFF', this.onBlockGlowOff); + this.props.vm.off('VISUAL_REPORT', this.onVisualReport); + this.props.vm.off('workspaceUpdate', this.onWorkspaceUpdate); + } + onStackGlowOn (data) { + this.workspace.glowStack(data.id, true); + } + onStackGlowOff (data) { + this.workspace.glowStack(data.id, false); + } + onBlockGlowOn (data) { + this.workspace.glowBlock(data.id, true); + } + onBlockGlowOff (data) { + this.workspace.glowBlock(data.id, false); + } + onVisualReport (data) { + this.workspace.reportValue(data.id, data.value); + } + onWorkspaceUpdate (data) { + ScratchBlocks.Events.disable(); + this.workspace.clear(); + const dom = ScratchBlocks.Xml.textToDom(data.xml); + ScratchBlocks.Xml.domToWorkspace(dom, this.workspace); + ScratchBlocks.Events.enable(); + } + render () { + const { + options, // eslint-disable-line no-unused-vars + vm, // eslint-disable-line no-unused-vars + ...props + } = this.props; + return ( + (this.blocks = c)} + {...props} + /> + ); + } +} + +Blocks.propTypes = { + options: React.PropTypes.shape({ + media: React.PropTypes.string, + zoom: React.PropTypes.shape({ + controls: React.PropTypes.boolean, + wheel: React.PropTypes.boolean, + startScale: React.PropTypes.number + }), + colours: React.PropTypes.shape({ + workspace: React.PropTypes.string, + flyout: React.PropTypes.string, + scrollbar: React.PropTypes.string, + scrollbarHover: React.PropTypes.string, + insertionMarker: React.PropTypes.string, + insertionMarkerOpacity: React.PropTypes.number, + fieldShadow: React.PropTypes.string, + dragShadowOpacity: React.PropTypes.number + }) + }), + vm: React.PropTypes.instanceOf(VM) +}; + +Blocks.defaultOptions = { + zoom: { + controls: true, + wheel: true, + startScale: 0.75 + }, + colours: { + workspace: '#334771', + flyout: '#283856', + scrollbar: '#24324D', + scrollbarHover: '#0C111A', + insertionMarker: '#FFFFFF', + insertionMarkerOpacity: 0.3, + fieldShadow: 'rgba(255, 255, 255, 0.3)', + dragShadowOpacity: 0.6 + } +}; + +Blocks.defaultProps = { + options: Blocks.defaultOptions, + vm: new VM() +}; + +module.exports = Blocks; diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx new file mode 100644 index 0000000000..b65ea90cb6 --- /dev/null +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -0,0 +1,108 @@ +const bindAll = require('lodash.bindall'); +const React = require('react'); +const Renderer = require('scratch-render'); +const VM = require('scratch-vm'); + +const StageComponent = require('../components/stage.jsx'); + +class Stage extends React.Component { + constructor (props) { + super(props); + bindAll(this, [ + 'attachMouseEvents', + 'detachMouseEvents', + 'onMouseUp', + 'onMouseMove', + 'onMouseDown', + 'animate', + 'startAnimation', + 'stopAnimation' + ]); + } + componentDidMount () { + this.renderer = new Renderer(this.canvas); + this.props.vm.attachRenderer(this.renderer); + this.attachMouseEvents(this.canvas); + this.startAnimation(); + } + componentWillUnmount () { + this.detachMouseEvents(this.canvas); + this.stopAnimation(); + } + attachMouseEvents (canvas) { + document.addEventListener('mousemove', this.onMouseMove); + canvas.addEventListener('mouseup', this.onMouseUp); + canvas.addEventListener('mousedown', this.onMouseDown); + } + detachMouseEvents (canvas) { + document.removeEventListener('mousemove', this.onMouseMove); + canvas.removeEventListener('mouseup', this.onMouseUp); + canvas.removeEventListener('mousedown', this.onMouseDown); + } + onMouseMove (e) { + const rect = this.canvas.getBoundingClientRect(); + const coordinates = { + x: e.clientX - rect.left, + y: e.clientY - rect.top, + canvasWidth: rect.width, + canvasHeight: rect.height + }; + this.props.vm.postIOData('mouse', coordinates); + } + onMouseUp (e) { + const rect = this.canvas.getBoundingClientRect(); + const data = { + isDown: false, + x: e.clientX - rect.left, + y: e.clientY - rect.top, + canvasWidth: rect.width, + canvasHeight: rect.height + }; + this.props.vm.postIOData('mouse', data); + e.preventDefault(); + } + onMouseDown (e) { + const rect = this.canvas.getBoundingClientRect(); + const data = { + isDown: true, + x: e.clientX - rect.left, + y: e.clientY - rect.top, + canvasWidth: rect.width, + canvasHeight: rect.height + }; + this.props.vm.postIOData('mouse', data); + e.preventDefault(); + } + startAnimation () { + this.animationFrame = requestAnimationFrame(this.animate); + } + stopAnimation () { + cancelAnimationFrame(this.animationFrame); + } + animate () { + this.props.vm.animationFrame(); + this.animationFrame = requestAnimationFrame(this.animate); + } + render () { + const { + vm, // eslint-disable-line no-unused-vars + ...props + } = this.props; + return ( + (this.canvas = canvas)} + {...props} + /> + ); + } +} + +Stage.propTypes = { + vm: React.PropTypes.instanceOf(VM) +}; + +Stage.defaultProps = { + vm: new VM() +}; + +module.exports = Stage; From a1e1de3ddb7c18465d1b4a8e15e53f7e0699b26b Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 24 Oct 2016 15:01:15 -0400 Subject: [PATCH 0029/1971] Merge pull request #10 from rschamp/lint-2 Actually lint React files --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9658b79dca..440c0acbd8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -10,7 +10,7 @@ "build": "npm run clean && webpack --progress --colors --bail", "clean": "rm -rf ./build && mkdir -p build", "deploy": "gh-pages -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", - "lint": "eslint .", + "lint": "eslint . --ext .js,.jsx", "postmerge": "opt --in postmerge --exec 'npm install'", "postrewrite": "opt --in postrewrite --exec 'npm install'", "precommit": "opt --in precommit --exec 'npm run lint'", @@ -41,7 +41,7 @@ "babel-preset-react": "6.11.1", "copy-webpack-plugin": "3.0.1", "eslint": "3.5.0", - "eslint-config-scratch": "^1.0.0", + "eslint-config-scratch": "^2.0.0", "eslint-plugin-react": "6.4.1", "gh-pages": "0.11.0", "html-webpack-plugin": "2.22.0", From 9a4bee0a527abf372240b84d06c0f54afd532902 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 24 Oct 2016 17:37:55 -0400 Subject: [PATCH 0030/1971] Merge pull request #11 from rschamp/lint-package-json Set up base eslint config in .eslintrc.js --- packages/scratch-gui/package.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 440c0acbd8..cccb6e1d20 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -18,9 +18,6 @@ "start": "webpack-dev-server --port $npm_package_config_port --content-base=./build", "test": "npm run lint && npm run build" }, - "eslintConfig": { - "extends": ["scratch"] - }, "author": "Massachusetts Institute of Technology", "license": "BSD-3-Clause", "homepage": "https://github.com/LLK/scratch-gui#readme", @@ -40,7 +37,7 @@ "babel-preset-es2015": "6.14.0", "babel-preset-react": "6.11.1", "copy-webpack-plugin": "3.0.1", - "eslint": "3.5.0", + "eslint": "3.8.1", "eslint-config-scratch": "^2.0.0", "eslint-plugin-react": "6.4.1", "gh-pages": "0.11.0", From 37dbbfaf7525ed74699d98659da450b67fe5ec35 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 26 Oct 2016 22:48:56 -0400 Subject: [PATCH 0031/1971] Add green flag and stop icons --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cccb6e1d20..d398b4c7a1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -56,6 +56,7 @@ "scratch-render": "latest", "scratch-vm": "latest", "svg-to-image": "1.1.3", + "svg-url-loader": "1.1.0", "travis-after-all": "jamesarosen/travis-after-all#override-api-urls", "webpack": "1.13.2", "webpack-dev-server": "1.15.2", From ab232e7d364391deb957059d57e4f4374135e59d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 4 Nov 2016 13:41:34 -0400 Subject: [PATCH 0032/1971] Remove animationFrame --- packages/scratch-gui/src/containers/stage.jsx | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index b65ea90cb6..736c5d00e4 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -13,21 +13,16 @@ class Stage extends React.Component { 'detachMouseEvents', 'onMouseUp', 'onMouseMove', - 'onMouseDown', - 'animate', - 'startAnimation', - 'stopAnimation' + 'onMouseDown' ]); } componentDidMount () { this.renderer = new Renderer(this.canvas); this.props.vm.attachRenderer(this.renderer); this.attachMouseEvents(this.canvas); - this.startAnimation(); } componentWillUnmount () { this.detachMouseEvents(this.canvas); - this.stopAnimation(); } attachMouseEvents (canvas) { document.addEventListener('mousemove', this.onMouseMove); @@ -73,16 +68,6 @@ class Stage extends React.Component { this.props.vm.postIOData('mouse', data); e.preventDefault(); } - startAnimation () { - this.animationFrame = requestAnimationFrame(this.animate); - } - stopAnimation () { - cancelAnimationFrame(this.animationFrame); - } - animate () { - this.props.vm.animationFrame(); - this.animationFrame = requestAnimationFrame(this.animate); - } render () { const { vm, // eslint-disable-line no-unused-vars From 956cfb7c62607095e6121fab11631adb211d2f6b Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 4 Nov 2016 19:09:48 -0400 Subject: [PATCH 0033/1971] Prevent gh-pages from being processed with Jekyll This removed our vendor files before. Prevent anything else from happening in the future --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d398b4c7a1..15073fc193 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -9,7 +9,7 @@ "scripts": { "build": "npm run clean && webpack --progress --colors --bail", "clean": "rm -rf ./build && mkdir -p build", - "deploy": "gh-pages -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", + "deploy": "touch build/.nojekyll && gh-pages -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", "lint": "eslint . --ext .js,.jsx", "postmerge": "opt --in postmerge --exec 'npm install'", "postrewrite": "opt --in postrewrite --exec 'npm install'", From bfc813973622e1ca9693055aea196b355c393d95 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 8 Nov 2016 17:11:58 -0500 Subject: [PATCH 0034/1971] Update travis-after-all for the public repo --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 15073fc193..27178caef6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -57,7 +57,7 @@ "scratch-vm": "latest", "svg-to-image": "1.1.3", "svg-url-loader": "1.1.0", - "travis-after-all": "jamesarosen/travis-after-all#override-api-urls", + "travis-after-all": "1.4.4", "webpack": "1.13.2", "webpack-dev-server": "1.15.2", "xhr": "2.2.2" From e97c074b6ae8071dc1c6f868f973a6b4ca5b9e34 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 10 Nov 2016 15:13:19 -0500 Subject: [PATCH 0035/1971] Don't ignore .nojekyll in gh-pages --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 27178caef6..d5b05b928b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -9,7 +9,7 @@ "scripts": { "build": "npm run clean && webpack --progress --colors --bail", "clean": "rm -rf ./build && mkdir -p build", - "deploy": "touch build/.nojekyll && gh-pages -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", + "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", "lint": "eslint . --ext .js,.jsx", "postmerge": "opt --in postmerge --exec 'npm install'", "postrewrite": "opt --in postrewrite --exec 'npm install'", From 786a22679e0ea3c9ad9dacc4c0d0efdafce6fe1a Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 17 Nov 2016 16:06:45 -0500 Subject: [PATCH 0036/1971] Fix lint warnings --- packages/scratch-gui/src/containers/blocks.jsx | 8 ++++++-- packages/scratch-gui/src/containers/stage.jsx | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index a5977a9f76..f1bb945f42 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -17,7 +17,8 @@ class Blocks extends React.Component { 'onBlockGlowOn', 'onBlockGlowOff', 'onVisualReport', - 'onWorkspaceUpdate' + 'onWorkspaceUpdate', + 'setBlocks' ]); } componentDidMount () { @@ -72,6 +73,9 @@ class Blocks extends React.Component { ScratchBlocks.Xml.domToWorkspace(dom, this.workspace); ScratchBlocks.Events.enable(); } + setBlocks (blocks) { + this.blocks = blocks; + } render () { const { options, // eslint-disable-line no-unused-vars @@ -80,7 +84,7 @@ class Blocks extends React.Component { } = this.props; return ( (this.blocks = c)} + componentRef={this.setBlocks} {...props} /> ); diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 736c5d00e4..53f2aa8699 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -13,7 +13,8 @@ class Stage extends React.Component { 'detachMouseEvents', 'onMouseUp', 'onMouseMove', - 'onMouseDown' + 'onMouseDown', + 'setCanvas' ]); } componentDidMount () { @@ -68,6 +69,9 @@ class Stage extends React.Component { this.props.vm.postIOData('mouse', data); e.preventDefault(); } + setCanvas (canvas) { + this.canvas = canvas; + } render () { const { vm, // eslint-disable-line no-unused-vars @@ -75,7 +79,7 @@ class Stage extends React.Component { } = this.props; return ( (this.canvas = canvas)} + canvasRef={this.setCanvas} {...props} /> ); From f3959191158b5d270b6a356b6c8dc452b523917d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 28 Nov 2016 11:51:28 -0500 Subject: [PATCH 0037/1971] Merge pull request #22 from rschamp/feature/flag-glow Glow flag/stop button when scripts are running --- packages/scratch-gui/src/containers/blocks.jsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index f1bb945f42..f0ac707e9b 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -12,8 +12,8 @@ class Blocks extends React.Component { bindAll(this, [ 'attachVM', 'detachVM', - 'onStackGlowOn', - 'onStackGlowOff', + 'onScriptGlowOn', + 'onScriptGlowOff', 'onBlockGlowOn', 'onBlockGlowOff', 'onVisualReport', @@ -36,25 +36,25 @@ class Blocks extends React.Component { .getFlyout() .getWorkspace() .addChangeListener(this.props.vm.flyoutBlockListener); - this.props.vm.on('STACK_GLOW_ON', this.onStackGlowOn); - this.props.vm.on('STACK_GLOW_OFF', this.onStackGlowOff); + this.props.vm.on('SCRIPT_GLOW_ON', this.onScriptGlowOn); + this.props.vm.on('SCRIPT_GLOW_OFF', this.onScriptGlowOff); this.props.vm.on('BLOCK_GLOW_ON', this.onBlockGlowOn); this.props.vm.on('BLOCK_GLOW_OFF', this.onBlockGlowOff); this.props.vm.on('VISUAL_REPORT', this.onVisualReport); this.props.vm.on('workspaceUpdate', this.onWorkspaceUpdate); } detachVM () { - this.props.vm.off('STACK_GLOW_ON', this.onStackGlowOn); - this.props.vm.off('STACK_GLOW_OFF', this.onStackGlowOff); + this.props.vm.off('SCRIPT_GLOW_ON', this.onScriptGlowOn); + this.props.vm.off('SCRIPT_GLOW_OFF', this.onScriptGlowOff); this.props.vm.off('BLOCK_GLOW_ON', this.onBlockGlowOn); this.props.vm.off('BLOCK_GLOW_OFF', this.onBlockGlowOff); this.props.vm.off('VISUAL_REPORT', this.onVisualReport); this.props.vm.off('workspaceUpdate', this.onWorkspaceUpdate); } - onStackGlowOn (data) { + onScriptGlowOn (data) { this.workspace.glowStack(data.id, true); } - onStackGlowOff (data) { + onScriptGlowOff (data) { this.workspace.glowStack(data.id, false); } onBlockGlowOn (data) { From f9badddce02cdad7c66c23593feb37ba06168b62 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 12 Dec 2016 11:27:43 -0500 Subject: [PATCH 0038/1971] Merge pull request #25 from rschamp/feature/sprite-selector Sprite selector, CSS and Redux overhaul --- packages/scratch-gui/package.json | 10 +- .../scratch-gui/src/containers/blocks.jsx | 2 +- packages/scratch-gui/src/containers/stage.jsx | 2 +- .../scratch-gui/src/lib/vm-listener-hoc.jsx | 112 ++++++++++++++++++ 4 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 packages/scratch-gui/src/lib/vm-listener-hoc.jsx diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d5b05b928b..a094b12769 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -30,15 +30,18 @@ "react-dom": "15.x.x" }, "devDependencies": { + "autoprefixer": "6.5.3", "babel-core": "6.14.0", "babel-eslint": "7.0.0", "babel-loader": "6.2.5", "babel-plugin-transform-object-rest-spread": "6.16.0", "babel-preset-es2015": "6.14.0", "babel-preset-react": "6.11.1", + "classnames": "2.2.5", "copy-webpack-plugin": "3.0.1", + "css-loader": "0.26.1", "eslint": "3.8.1", - "eslint-config-scratch": "^2.0.0", + "eslint-config-scratch": "^3.0.0", "eslint-plugin-react": "6.4.1", "gh-pages": "0.11.0", "html-webpack-plugin": "2.22.0", @@ -48,17 +51,22 @@ "lodash.defaultsdeep": "4.4.0", "minilog": "3.0.1", "opt-cli": "1.5.1", + "postcss-loader": "1.2.0", "react": "15.3.2", "react-dom": "15.3.2", "react-modal": "1.5.2", + "react-redux": "4.4.6", "react-style-proptype": "1.2.0", + "redux": "3.6.0", "scratch-blocks": "latest", "scratch-render": "latest", "scratch-vm": "latest", + "style-loader": "0.13.1", "svg-to-image": "1.1.3", "svg-url-loader": "1.1.0", "travis-after-all": "1.4.4", "webpack": "1.13.2", + "webpack-combine-loaders": "2.0.3", "webpack-dev-server": "1.15.2", "xhr": "2.2.2" } diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index f0ac707e9b..5a579f6e45 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -4,7 +4,7 @@ const React = require('react'); const ScratchBlocks = require('scratch-blocks'); const VM = require('scratch-vm'); -const BlocksComponent = require('../components/blocks.jsx'); +const BlocksComponent = require('../components/blocks/blocks.jsx'); class Blocks extends React.Component { constructor (props) { diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 53f2aa8699..55c3607248 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -3,7 +3,7 @@ const React = require('react'); const Renderer = require('scratch-render'); const VM = require('scratch-vm'); -const StageComponent = require('../components/stage.jsx'); +const StageComponent = require('../components/stage/stage.jsx'); class Stage extends React.Component { constructor (props) { diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx new file mode 100644 index 0000000000..87c76c56ae --- /dev/null +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -0,0 +1,112 @@ +const bindAll = require('lodash.bindall'); +const React = require('react'); +const VM = require('scratch-vm'); + +const {connect} = require('react-redux'); + +const targets = require('../reducers/targets'); + +/* + * Higher Order Component to manage events emitted by the VM + * @param {React.Component} WrappedComponent component to manage VM events for + * @returns {React.Component} connected component with vm events bound to redux + */ +const vmListenerHOC = function (WrappedComponent) { + class VMListener extends React.Component { + constructor (props) { + super(props); + bindAll(this, [ + 'handleKeyDown', + 'handleKeyUp' + ]); + // We have to start listening to the vm here rather than in + // componentDidMount because the HOC mounts the wrapped component, + // so the HOC componentDidMount triggers after the wrapped component + // mounts. + // If the wrapped component uses the vm in componentDidMount, then + // we need to start listening before mounting the wrapped component. + this.props.vm.on('targetsUpdate', this.props.onTargetsUpdate); + this.props.vm.on('SPRITE_INFO_REPORT', this.props.onSpriteInfoReport); + } + componentDidMount () { + if (this.props.attachKeyboardEvents) { + document.addEventListener('keydown', this.handleKeyDown); + document.addEventListener('keyup', this.handleKeyUp); + } + } + componentWillUnmount () { + if (this.props.attachKeyboardEvents) { + document.removeEventListener('keydown', this.handleKeyDown); + document.removeEventListener('keyup', this.handleKeyUp); + } + } + handleKeyDown (e) { + // Don't capture keys intended for Blockly inputs. + if (e.target !== document && e.target !== document.body) return; + + this.props.vm.postIOData('keyboard', { + keyCode: e.keyCode, + isDown: true + }); + + // Don't stop browser keyboard shortcuts + if (e.metaKey || e.altKey || e.ctrlKey) return; + + e.preventDefault(); + } + handleKeyUp (e) { + // Always capture up events, + // even those that have switched to other targets. + this.props.vm.postIOData('keyboard', { + keyCode: e.keyCode, + isDown: false + }); + + // E.g., prevent scroll. + if (e.target !== document && e.target !== document.body) { + e.preventDefault(); + } + } + render () { + const { + /* eslint-disable no-unused-vars */ + attachKeyboardEvents, + onKeyDown, + onKeyUp, + onSpriteInfoReport, + onTargetsUpdate, + /* eslint-enable no-unused-vars */ + ...props + } = this.props; + return ; + } + } + VMListener.propTypes = { + attachKeyboardEvents: React.PropTypes.bool, + onKeyDown: React.PropTypes.func, + onKeyUp: React.PropTypes.func, + onSpriteInfoReport: React.PropTypes.func, + onTargetsUpdate: React.PropTypes.func, + vm: React.PropTypes.instanceOf(VM).isRequired + }; + VMListener.defaultProps = { + attachKeyboardEvents: true, + vm: new VM() + }; + const mapStateToProps = () => ({}); + const mapDispatchToProps = dispatch => ({ + onTargetsUpdate: data => { + dispatch(targets.updateEditingTarget(data.editingTarget)); + dispatch(targets.updateTargets(data.targetList)); + }, + onSpriteInfoReport: spriteInfo => { + dispatch(targets.updateTarget(spriteInfo)); + } + }); + return connect( + mapStateToProps, + mapDispatchToProps + )(VMListener); +}; + +module.exports = vmListenerHOC; From e5fda66d7b8845a8e41583af885ccce9b8ec825c Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 15 Dec 2016 15:59:19 -0500 Subject: [PATCH 0039/1971] Merge pull request #27 from rschamp/hooks Make git hooks a bit less cumbersome --- packages/scratch-gui/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a094b12769..16b2293a87 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -13,8 +13,7 @@ "lint": "eslint . --ext .js,.jsx", "postmerge": "opt --in postmerge --exec 'npm install'", "postrewrite": "opt --in postrewrite --exec 'npm install'", - "precommit": "opt --in precommit --exec 'npm run lint'", - "prepush": "opt --in prepush --exec 'npm run test'", + "prepush": "opt --in prepush --exec 'npm run lint'", "start": "webpack-dev-server --port $npm_package_config_port --content-base=./build", "test": "npm run lint && npm run build" }, From 11097e43dbb9a4273b504a1a1dd2730103eab8ad Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 5 Jan 2017 10:56:16 -0500 Subject: [PATCH 0040/1971] Merge pull request #31 from rschamp/bugfix/20-windows-port-var Configure port through webpack, not npm --- packages/scratch-gui/package.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 16b2293a87..1db504e3cd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -3,9 +3,6 @@ "version": "0.1.0", "description": "GraphicaL User Interface for creating and running Scratch 3.0 projects", "main": "./src/index.js", - "config": { - "port": 8601 - }, "scripts": { "build": "npm run clean && webpack --progress --colors --bail", "clean": "rm -rf ./build && mkdir -p build", @@ -14,7 +11,7 @@ "postmerge": "opt --in postmerge --exec 'npm install'", "postrewrite": "opt --in postrewrite --exec 'npm install'", "prepush": "opt --in prepush --exec 'npm run lint'", - "start": "webpack-dev-server --port $npm_package_config_port --content-base=./build", + "start": "webpack-dev-server", "test": "npm run lint && npm run build" }, "author": "Massachusetts Institute of Technology", From 3bddadffe662aedd85705207ba0ef12aa2f915a2 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 6 Jan 2017 16:34:21 -0500 Subject: [PATCH 0041/1971] Merge pull request #33 from ericrosenbaum/master add audio engine --- packages/scratch-gui/package.json | 1 + packages/scratch-gui/src/containers/stage.jsx | 3 +++ 2 files changed, 4 insertions(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1db504e3cd..a0d4e529e2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -54,6 +54,7 @@ "react-redux": "4.4.6", "react-style-proptype": "1.2.0", "redux": "3.6.0", + "scratch-audio": "latest", "scratch-blocks": "latest", "scratch-render": "latest", "scratch-vm": "latest", diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 55c3607248..8448115ea2 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -1,6 +1,7 @@ const bindAll = require('lodash.bindall'); const React = require('react'); const Renderer = require('scratch-render'); +const AudioEngine = require('scratch-audio'); const VM = require('scratch-vm'); const StageComponent = require('../components/stage/stage.jsx'); @@ -20,6 +21,8 @@ class Stage extends React.Component { componentDidMount () { this.renderer = new Renderer(this.canvas); this.props.vm.attachRenderer(this.renderer); + this.audioEngine = new AudioEngine(); + this.props.vm.attachAudioEngine(this.audioEngine); this.attachMouseEvents(this.canvas); } componentWillUnmount () { From 99e71d3b4450ff476c92c60bda3f2a4f5c65ad08 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Fri, 27 Jan 2017 11:00:28 -0500 Subject: [PATCH 0042/1971] Change ScratchBlocks Colors to match concept colors from #1 (#46) Change Toolbox Categories to match new design color --- packages/scratch-gui/src/containers/blocks.jsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 5a579f6e45..48e40a570f 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -102,6 +102,8 @@ Blocks.propTypes = { colours: React.PropTypes.shape({ workspace: React.PropTypes.string, flyout: React.PropTypes.string, + toolbox: React.PropTypes.string, + toolboxSelected: React.PropTypes.string, scrollbar: React.PropTypes.string, scrollbarHover: React.PropTypes.string, insertionMarker: React.PropTypes.string, @@ -120,10 +122,12 @@ Blocks.defaultOptions = { startScale: 0.75 }, colours: { - workspace: '#334771', - flyout: '#283856', - scrollbar: '#24324D', - scrollbarHover: '#0C111A', + workspace: '#F9F9F9', + flyout: '#F9F9F9', + toolbox: '#FFFFFF', + toolboxSelected: '#E9EEF2', + scrollbar: '#CECDCE', + scrollbarHover: '#CECDCE', insertionMarker: '#FFFFFF', insertionMarkerOpacity: 0.3, fieldShadow: 'rgba(255, 255, 255, 0.3)', From 248e9b71af9e9497909a6396df2f4025083b7dda Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 30 Jan 2017 14:36:03 -0500 Subject: [PATCH 0043/1971] Merge pull request #49 from TheBrokenRail/patch-3 Make Insertion Marker Visible --- packages/scratch-gui/src/containers/blocks.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 48e40a570f..ecafed937a 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -128,8 +128,8 @@ Blocks.defaultOptions = { toolboxSelected: '#E9EEF2', scrollbar: '#CECDCE', scrollbarHover: '#CECDCE', - insertionMarker: '#FFFFFF', - insertionMarkerOpacity: 0.3, + insertionMarker: '#000000', + insertionMarkerOpacity: 0.2, fieldShadow: 'rgba(255, 255, 255, 0.3)', dragShadowOpacity: 0.6 } From f51ef3fc3e61b0e64384f8b322e2a5b533fa4d18 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 2 Feb 2017 14:16:13 -0500 Subject: [PATCH 0044/1971] Merge pull request #61 from rschamp/perf Cache stage bounding box and avoid unnecessary library rendering --- packages/scratch-gui/src/containers/stage.jsx | 44 ++++++++++++------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 8448115ea2..7136f56d31 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -15,18 +15,22 @@ class Stage extends React.Component { 'onMouseUp', 'onMouseMove', 'onMouseDown', + 'updateRect', 'setCanvas' ]); } componentDidMount () { + this.attachRectEvents(); + this.attachMouseEvents(this.canvas); + this.updateRect(); this.renderer = new Renderer(this.canvas); this.props.vm.attachRenderer(this.renderer); this.audioEngine = new AudioEngine(); this.props.vm.attachAudioEngine(this.audioEngine); - this.attachMouseEvents(this.canvas); } componentWillUnmount () { this.detachMouseEvents(this.canvas); + this.detachRectEvents(); } attachMouseEvents (canvas) { document.addEventListener('mousemove', this.onMouseMove); @@ -38,36 +42,44 @@ class Stage extends React.Component { canvas.removeEventListener('mouseup', this.onMouseUp); canvas.removeEventListener('mousedown', this.onMouseDown); } + attachRectEvents () { + window.addEventListener('resize', this.updateRect); + window.addEventListener('scroll', this.updateRect); + } + detachRectEvents () { + window.removeEventListener('resize', this.updateRect); + window.removeEventListener('scroll', this.updateRect); + } + updateRect () { + this.rect = this.canvas.getBoundingClientRect(); + } onMouseMove (e) { - const rect = this.canvas.getBoundingClientRect(); const coordinates = { - x: e.clientX - rect.left, - y: e.clientY - rect.top, - canvasWidth: rect.width, - canvasHeight: rect.height + x: e.clientX - this.rect.left, + y: e.clientY - this.rect.top, + canvasWidth: this.rect.width, + canvasHeight: this.rect.height }; this.props.vm.postIOData('mouse', coordinates); } onMouseUp (e) { - const rect = this.canvas.getBoundingClientRect(); const data = { isDown: false, - x: e.clientX - rect.left, - y: e.clientY - rect.top, - canvasWidth: rect.width, - canvasHeight: rect.height + x: e.clientX - this.rect.left, + y: e.clientY - this.rect.top, + canvasWidth: this.rect.width, + canvasHeight: this.rect.height }; this.props.vm.postIOData('mouse', data); e.preventDefault(); } onMouseDown (e) { - const rect = this.canvas.getBoundingClientRect(); const data = { isDown: true, - x: e.clientX - rect.left, - y: e.clientY - rect.top, - canvasWidth: rect.width, - canvasHeight: rect.height + x: e.clientX - this.rect.left, + y: e.clientY - this.rect.top, + canvasWidth: this.rect.width, + canvasHeight: this.rect.height }; this.props.vm.postIOData('mouse', data); e.preventDefault(); From cd6c44c986e253eb2af4dace1f2b984bedf9b57a Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 2 Feb 2017 15:02:09 -0500 Subject: [PATCH 0045/1971] Merge pull request #60 from LLK/greenkeeper/initial MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update dependencies to enable Greenkeeper 🌴 --- packages/scratch-gui/package.json | 51 +++++++++++++++---------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a0d4e529e2..4d462138e8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -26,33 +26,32 @@ "react-dom": "15.x.x" }, "devDependencies": { - "autoprefixer": "6.5.3", - "babel-core": "6.14.0", - "babel-eslint": "7.0.0", - "babel-loader": "6.2.5", - "babel-plugin-transform-object-rest-spread": "6.16.0", - "babel-preset-es2015": "6.14.0", - "babel-preset-react": "6.11.1", + "autoprefixer": "6.7.2", + "babel-core": "6.22.1", + "babel-eslint": "7.1.1", + "babel-loader": "6.2.10", + "babel-plugin-transform-object-rest-spread": "6.22.0", + "babel-preset-es2015": "6.22.0", + "babel-preset-react": "6.22.0", "classnames": "2.2.5", - "copy-webpack-plugin": "3.0.1", + "copy-webpack-plugin": "4.0.1", "css-loader": "0.26.1", - "eslint": "3.8.1", + "eslint": "3.14.1", "eslint-config-scratch": "^3.0.0", - "eslint-plugin-react": "6.4.1", - "gh-pages": "0.11.0", - "html-webpack-plugin": "2.22.0", - "husky": "0.11.9", - "json-loader": "0.5.4", + "eslint-plugin-react": "6.9.0", + "gh-pages": "0.12.0", + "html-webpack-plugin": "2.28.0", + "husky": "0.13.1", "lodash.bindall": "4.4.0", - "lodash.defaultsdeep": "4.4.0", - "minilog": "3.0.1", + "lodash.defaultsdeep": "4.6.0", + "minilog": "3.1.0", "opt-cli": "1.5.1", - "postcss-loader": "1.2.0", - "react": "15.3.2", - "react-dom": "15.3.2", - "react-modal": "1.5.2", - "react-redux": "4.4.6", - "react-style-proptype": "1.2.0", + "postcss-loader": "1.2.2", + "react": "15.4.2", + "react-dom": "15.4.2", + "react-modal": "1.6.5", + "react-redux": "5.0.2", + "react-style-proptype": "2.0.0", "redux": "3.6.0", "scratch-audio": "latest", "scratch-blocks": "latest", @@ -60,11 +59,11 @@ "scratch-vm": "latest", "style-loader": "0.13.1", "svg-to-image": "1.1.3", - "svg-url-loader": "1.1.0", + "svg-url-loader": "2.0.1", "travis-after-all": "1.4.4", - "webpack": "1.13.2", + "webpack": "2.2.1", "webpack-combine-loaders": "2.0.3", - "webpack-dev-server": "1.15.2", - "xhr": "2.2.2" + "webpack-dev-server": "1.16.3", + "xhr": "2.3.3" } } From 8296e0354ff338e142645016cb24c09266d087b2 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 3 Feb 2017 11:31:07 -0500 Subject: [PATCH 0046/1971] Merge pull request #63 from LLK/greenkeeper/webpack-dev-server-2.3.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update webpack-dev-server to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4d462138e8..2df763fd2a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -63,7 +63,7 @@ "travis-after-all": "1.4.4", "webpack": "2.2.1", "webpack-combine-loaders": "2.0.3", - "webpack-dev-server": "1.16.3", + "webpack-dev-server": "2.3.0", "xhr": "2.3.3" } } From 9fdce600b654283d9a737248c993dc026d475c2b Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 6 Feb 2017 10:43:23 -0500 Subject: [PATCH 0047/1971] Merge pull request #71 from LLK/greenkeeper/eslint-3.15.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update eslint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2df763fd2a..944ff28f15 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -36,7 +36,7 @@ "classnames": "2.2.5", "copy-webpack-plugin": "4.0.1", "css-loader": "0.26.1", - "eslint": "3.14.1", + "eslint": "3.15.0", "eslint-config-scratch": "^3.0.0", "eslint-plugin-react": "6.9.0", "gh-pages": "0.12.0", From fb2fb0e2cf26bfef7cc082688801bea3cb14cd00 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 7 Feb 2017 14:02:02 -0500 Subject: [PATCH 0048/1971] Merge pull request #72 from rschamp/perf-target-pane Implement shouldComponentUpdate on TargetPane --- packages/scratch-gui/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 944ff28f15..591a9d4a01 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -44,6 +44,9 @@ "husky": "0.13.1", "lodash.bindall": "4.4.0", "lodash.defaultsdeep": "4.6.0", + "lodash.isequal": "4.5.0", + "lodash.omit": "4.5.0", + "lodash.pick": "4.4.0", "minilog": "3.1.0", "opt-cli": "1.5.1", "postcss-loader": "1.2.2", From c2784f086a3130026f05defecbe2a5943480d1cc Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 7 Feb 2017 17:33:08 -0500 Subject: [PATCH 0049/1971] Merge pull request #76 from ericrosenbaum/sound-menu Monkeypatch dynamic sound menu into ScratchBlocks --- packages/scratch-gui/src/containers/blocks.jsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index ecafed937a..e77cf3cc75 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -1,7 +1,7 @@ const bindAll = require('lodash.bindall'); const defaultsDeep = require('lodash.defaultsdeep'); const React = require('react'); -const ScratchBlocks = require('scratch-blocks'); +const VMScratchBlocks = require('../lib/blocks'); const VM = require('scratch-vm'); const BlocksComponent = require('../components/blocks/blocks.jsx'); @@ -9,6 +9,7 @@ const BlocksComponent = require('../components/blocks/blocks.jsx'); class Blocks extends React.Component { constructor (props) { super(props); + this.ScratchBlocks = VMScratchBlocks(props.vm); bindAll(this, [ 'attachVM', 'detachVM', @@ -23,7 +24,7 @@ class Blocks extends React.Component { } componentDidMount () { const workspaceConfig = defaultsDeep({}, Blocks.defaultOptions, this.props.options); - this.workspace = ScratchBlocks.inject(this.blocks, workspaceConfig); + this.workspace = this.ScratchBlocks.inject(this.blocks, workspaceConfig); this.attachVM(); } componentWillUnmount () { @@ -67,11 +68,11 @@ class Blocks extends React.Component { this.workspace.reportValue(data.id, data.value); } onWorkspaceUpdate (data) { - ScratchBlocks.Events.disable(); + this.ScratchBlocks.Events.disable(); this.workspace.clear(); - const dom = ScratchBlocks.Xml.textToDom(data.xml); - ScratchBlocks.Xml.domToWorkspace(dom, this.workspace); - ScratchBlocks.Events.enable(); + const dom = this.ScratchBlocks.Xml.textToDom(data.xml); + this.ScratchBlocks.Xml.domToWorkspace(dom, this.workspace); + this.ScratchBlocks.Events.enable(); } setBlocks (blocks) { this.blocks = blocks; From 05c36da99da4100fd702521240a02d5bc9b60ca4 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 8 Feb 2017 14:15:46 -0500 Subject: [PATCH 0050/1971] Merge pull request #77 from ericrosenbaum/sound-menu-update PygmyMarmosetPatch for sound menu updates on workspace update --- packages/scratch-gui/src/containers/blocks.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index e77cf3cc75..d27e91668c 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -73,6 +73,7 @@ class Blocks extends React.Component { const dom = this.ScratchBlocks.Xml.textToDom(data.xml); this.ScratchBlocks.Xml.domToWorkspace(dom, this.workspace); this.ScratchBlocks.Events.enable(); + this.workspace.toolbox_.refreshSelection(); } setBlocks (blocks) { this.blocks = blocks; From bd467d840d7b55ec1b7aab416c30383650b7bd9b Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Tue, 14 Feb 2017 10:55:55 -0500 Subject: [PATCH 0051/1971] chore(package): update babel-loader to version 6.3.0 (#93) https://greenkeeper.io/ --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 591a9d4a01..95935783b6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -29,7 +29,7 @@ "autoprefixer": "6.7.2", "babel-core": "6.22.1", "babel-eslint": "7.1.1", - "babel-loader": "6.2.10", + "babel-loader": "6.3.0", "babel-plugin-transform-object-rest-spread": "6.22.0", "babel-preset-es2015": "6.22.0", "babel-preset-react": "6.22.0", From 00141e307cac19d27af770db80c1d5d5542f95f5 Mon Sep 17 00:00:00 2001 From: Steven Dale Date: Wed, 15 Feb 2017 08:46:16 -0500 Subject: [PATCH 0052/1971] Design pass: layout panes, sprite libraries, info area, import buttons, + menu bar (#83) * Covers parts of these issues: GUI layout panes (#69), sprite libraries (#12), import buttons (#64), menu bar (#67), and sprite info area (#53) ~ Refactored layout pane structure by removing `display: flex` from `.box`, by default. Declaring flex instead only where specifically needed. This helped fix layout quirks, and should help us avoid hard to find issues related to nested flex boxes ~ Added descriptive classnames to boxes/containers/wrappers. Moved more padding, margins + layout into these wrappers to help with component reusability. ~ Set up a spacer unit variable inside `gui.css`, to start keeping consistent padding, margins and rounded corners between panes. Used [CSS/PostCSS Modules](https://github.com/css-modules/postcss-modules-values) to avoid SCSS dependency. ~ Refactored inline styles from JSX, into CSS files. 2 reasons: a) prepping for reuse of CSS Module variables. b) Feels easier to debug complex flex layouts when all the styles are in one place vs JS/HTML/CSS syntax mixed together ~ Added menu bar into layout via new component, and sprite info header in sprite selector pane, with stubs for the labels + forms. Sprite items are correctly scrolling, while leaving the header fixed, in Chrome. Mostly working in FFx. Not working in Safari, IE yet. ~ Style pass on library modal ~ Experimenting with a few different transition styles: Logo in menu bar, sprite library items, Add buttons. --- packages/scratch-gui/package.json | 1 + .../src/components/menu-bar/menu-bar.jsx | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 packages/scratch-gui/src/components/menu-bar/menu-bar.jsx diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 95935783b6..db233ca878 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -50,6 +50,7 @@ "minilog": "3.1.0", "opt-cli": "1.5.1", "postcss-loader": "1.2.2", + "postcss-modules-values": "1.2.2", "react": "15.4.2", "react-dom": "15.4.2", "react-modal": "1.6.5", diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx new file mode 100644 index 0000000000..43f2a8ac01 --- /dev/null +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -0,0 +1,27 @@ +const classNames = require('classnames'); +const React = require('react'); + +const Box = require('../box/box.jsx'); +const styles = require('./menu-bar.css'); +const scratchLogo = require('./scratch-logo.svg'); + +const MenuBar = function MenuBar () { + return ( + +
+ +
+ + Animation Playtest Prototype +
+ ); +}; + +module.exports = MenuBar; From 9b120ffeae3c5835538c4573aba03ffd7eec6320 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 20 Feb 2017 15:41:37 -0500 Subject: [PATCH 0053/1971] Merge pull request #103 from LLK/greenkeeper/eslint-plugin-react-6.10.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update eslint-plugin-react to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index db233ca878..bae38dbac7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -38,7 +38,7 @@ "css-loader": "0.26.1", "eslint": "3.15.0", "eslint-config-scratch": "^3.0.0", - "eslint-plugin-react": "6.9.0", + "eslint-plugin-react": "6.10.0", "gh-pages": "0.12.0", "html-webpack-plugin": "2.28.0", "husky": "0.13.1", From 92c916e15ceff787b9d72d2fd0f148c8de694fd5 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 20 Feb 2017 15:43:23 -0500 Subject: [PATCH 0054/1971] Merge pull request #115 from LLK/greenkeeper/babel-loader-6.3.2 chore(package): update babel-loader to version 6.3.2 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index bae38dbac7..1ea0d789f6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -29,7 +29,7 @@ "autoprefixer": "6.7.2", "babel-core": "6.22.1", "babel-eslint": "7.1.1", - "babel-loader": "6.3.0", + "babel-loader": "6.3.2", "babel-plugin-transform-object-rest-spread": "6.22.0", "babel-preset-es2015": "6.22.0", "babel-preset-react": "6.22.0", From 8a335ef21721c7c4b31bbd84d87b8d94bb734e2e Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 20 Feb 2017 15:43:37 -0500 Subject: [PATCH 0055/1971] Merge pull request #117 from LLK/greenkeeper/babel-core-6.23.1 chore(package): update babel-core to version 6.23.1 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1ea0d789f6..8cd723b610 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -27,7 +27,7 @@ }, "devDependencies": { "autoprefixer": "6.7.2", - "babel-core": "6.22.1", + "babel-core": "6.23.1", "babel-eslint": "7.1.1", "babel-loader": "6.3.2", "babel-plugin-transform-object-rest-spread": "6.22.0", From f41d005b57a450ef30e3bf4e3996257d089877bf Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 20 Feb 2017 15:43:50 -0500 Subject: [PATCH 0056/1971] Merge pull request #116 from LLK/greenkeeper/postcss-loader-1.3.1 chore(package): update postcss-loader to version 1.3.1 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8cd723b610..be8302cb7f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -49,7 +49,7 @@ "lodash.pick": "4.4.0", "minilog": "3.1.0", "opt-cli": "1.5.1", - "postcss-loader": "1.2.2", + "postcss-loader": "1.3.1", "postcss-modules-values": "1.2.2", "react": "15.4.2", "react-dom": "15.4.2", From 914176482e25673d4e6268fbd257087f44b5afd4 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 20 Feb 2017 15:44:58 -0500 Subject: [PATCH 0057/1971] Merge pull request #104 from LLK/greenkeeper/svg-url-loader-2.0.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update svg-url-loader to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index be8302cb7f..4c0968cce0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -63,7 +63,7 @@ "scratch-vm": "latest", "style-loader": "0.13.1", "svg-to-image": "1.1.3", - "svg-url-loader": "2.0.1", + "svg-url-loader": "2.0.2", "travis-after-all": "1.4.4", "webpack": "2.2.1", "webpack-combine-loaders": "2.0.3", From eedf492762ac2019b94796f53925edbba94c01d0 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 20 Feb 2017 15:47:07 -0500 Subject: [PATCH 0058/1971] Merge pull request #118 from LLK/greenkeeper/webpack-dev-server-2.4.1 chore(package): update webpack-dev-server to version 2.4.1 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4c0968cce0..7b3fa1c084 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -67,7 +67,7 @@ "travis-after-all": "1.4.4", "webpack": "2.2.1", "webpack-combine-loaders": "2.0.3", - "webpack-dev-server": "2.3.0", + "webpack-dev-server": "2.4.1", "xhr": "2.3.3" } } From 2d317f5ec8e7f131600636a8d257424b1d6892aa Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 20 Feb 2017 15:48:00 -0500 Subject: [PATCH 0059/1971] Merge pull request #113 from LLK/greenkeeper/eslint-3.16.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update eslint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7b3fa1c084..6afdda8ce4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -36,7 +36,7 @@ "classnames": "2.2.5", "copy-webpack-plugin": "4.0.1", "css-loader": "0.26.1", - "eslint": "3.15.0", + "eslint": "3.16.0", "eslint-config-scratch": "^3.0.0", "eslint-plugin-react": "6.10.0", "gh-pages": "0.12.0", From 88d17d8d4b88b1cce4bd776d5a4556ae35170c17 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 20 Feb 2017 15:57:49 -0500 Subject: [PATCH 0060/1971] Merge pull request #109 from LLK/greenkeeper/xhr-2.4.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update xhr to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6afdda8ce4..e1872dc699 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -68,6 +68,6 @@ "webpack": "2.2.1", "webpack-combine-loaders": "2.0.3", "webpack-dev-server": "2.4.1", - "xhr": "2.3.3" + "xhr": "2.4.0" } } From 421fba425c435808392b1718201812ce2c7ada23 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 20 Feb 2017 16:00:57 -0500 Subject: [PATCH 0061/1971] Merge pull request #119 from LLK/greenkeeper/autoprefixer-6.7.4 chore(package): update autoprefixer to version 6.7.4 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e1872dc699..4cb1e3d5b4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -26,7 +26,7 @@ "react-dom": "15.x.x" }, "devDependencies": { - "autoprefixer": "6.7.2", + "autoprefixer": "6.7.4", "babel-core": "6.23.1", "babel-eslint": "7.1.1", "babel-loader": "6.3.2", From 7a245589bfd5ecbfead052b8b93198b78cd993b0 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 21 Feb 2017 18:43:00 -0500 Subject: [PATCH 0062/1971] Use postcss-simple-vars for variables (#125) Using postcss-modules-values with imported values failed on our current basic usage; it is apparently not mature enough to be used yet. The syntax used by postcss-simple-vars is already familiar to most people familiar with modern CSS build systems. --- packages/scratch-gui/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4cb1e3d5b4..41feccd8a9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -49,8 +49,9 @@ "lodash.pick": "4.4.0", "minilog": "3.1.0", "opt-cli": "1.5.1", + "postcss-import": "9.1.0", "postcss-loader": "1.3.1", - "postcss-modules-values": "1.2.2", + "postcss-simple-vars": "3.0.0", "react": "15.4.2", "react-dom": "15.4.2", "react-modal": "1.6.5", From d4172a11eee4cef8db5b4943d5f6bc6ad9f46566 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 23 Feb 2017 08:47:23 -0500 Subject: [PATCH 0063/1971] Merge pull request #127 from LLK/greenkeeper/eslint-3.16.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update eslint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 41feccd8a9..8b02f2ba78 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -36,7 +36,7 @@ "classnames": "2.2.5", "copy-webpack-plugin": "4.0.1", "css-loader": "0.26.1", - "eslint": "3.16.0", + "eslint": "3.16.1", "eslint-config-scratch": "^3.0.0", "eslint-plugin-react": "6.10.0", "gh-pages": "0.12.0", From df9d30547ceb38c2c3b7fceba825762dde110d8c Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 23 Feb 2017 15:41:25 -0500 Subject: [PATCH 0064/1971] Use large range on true "dev" dependencies (#114) * Remove unnecessary dev dependencies * Use large range on true "dev" dependencies The exact versions of these dependencies are not important for building, so give them large ranges in order to quiet down greenkeeper. I did not split them between dependencies and devDependencies because a package's dependencies are installed whenever the package is installed as a dependency. Installing all of these would be obnoxious and unnecessary. --- packages/scratch-gui/package.json | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8b02f2ba78..c3d4520162 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -8,9 +8,6 @@ "clean": "rm -rf ./build && mkdir -p build", "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", "lint": "eslint . --ext .js,.jsx", - "postmerge": "opt --in postmerge --exec 'npm install'", - "postrewrite": "opt --in postrewrite --exec 'npm install'", - "prepush": "opt --in prepush --exec 'npm run lint'", "start": "webpack-dev-server", "test": "npm run lint && npm run build" }, @@ -28,7 +25,7 @@ "devDependencies": { "autoprefixer": "6.7.4", "babel-core": "6.23.1", - "babel-eslint": "7.1.1", + "babel-eslint": "^7.1.1", "babel-loader": "6.3.2", "babel-plugin-transform-object-rest-spread": "6.22.0", "babel-preset-es2015": "6.22.0", @@ -36,19 +33,17 @@ "classnames": "2.2.5", "copy-webpack-plugin": "4.0.1", "css-loader": "0.26.1", - "eslint": "3.16.1", + "eslint": "^3.16.1", "eslint-config-scratch": "^3.0.0", - "eslint-plugin-react": "6.10.0", - "gh-pages": "0.12.0", + "eslint-plugin-react": "^6.10.0", + "gh-pages": "^0.12.0", "html-webpack-plugin": "2.28.0", - "husky": "0.13.1", "lodash.bindall": "4.4.0", "lodash.defaultsdeep": "4.6.0", "lodash.isequal": "4.5.0", "lodash.omit": "4.5.0", "lodash.pick": "4.4.0", "minilog": "3.1.0", - "opt-cli": "1.5.1", "postcss-import": "9.1.0", "postcss-loader": "1.3.1", "postcss-simple-vars": "3.0.0", @@ -65,10 +60,9 @@ "style-loader": "0.13.1", "svg-to-image": "1.1.3", "svg-url-loader": "2.0.2", - "travis-after-all": "1.4.4", + "travis-after-all": "^1.4.4", "webpack": "2.2.1", - "webpack-combine-loaders": "2.0.3", - "webpack-dev-server": "2.4.1", + "webpack-dev-server": "^2.4.1", "xhr": "2.4.0" } } From 14073e9d850d0f11d5bd184f03a657da1e451649 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 23 Feb 2017 16:38:34 -0500 Subject: [PATCH 0065/1971] Merge pull request #129 from LLK/greenkeeper/react-style-proptype-2.0.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-style-proptype to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c3d4520162..aa4e81e459 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -51,7 +51,7 @@ "react-dom": "15.4.2", "react-modal": "1.6.5", "react-redux": "5.0.2", - "react-style-proptype": "2.0.0", + "react-style-proptype": "2.0.1", "redux": "3.6.0", "scratch-audio": "latest", "scratch-blocks": "latest", From db9750b301294d11a656e543e3d1bf0896d8e4a7 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 24 Feb 2017 10:03:42 -0500 Subject: [PATCH 0066/1971] Merge pull request #131 from LLK/greenkeeper/postcss-loader-1.3.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update postcss-loader to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index aa4e81e459..cb468a3bba 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -45,7 +45,7 @@ "lodash.pick": "4.4.0", "minilog": "3.1.0", "postcss-import": "9.1.0", - "postcss-loader": "1.3.1", + "postcss-loader": "1.3.2", "postcss-simple-vars": "3.0.0", "react": "15.4.2", "react-dom": "15.4.2", From 086734e840efd408d337a48e1cf58fd5896b4475 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 24 Feb 2017 10:06:25 -0500 Subject: [PATCH 0067/1971] Merge pull request #126 from LLK/greenkeeper/autoprefixer-6.7.5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update autoprefixer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cb468a3bba..644747a37c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -23,7 +23,7 @@ "react-dom": "15.x.x" }, "devDependencies": { - "autoprefixer": "6.7.4", + "autoprefixer": "6.7.5", "babel-core": "6.23.1", "babel-eslint": "^7.1.1", "babel-loader": "6.3.2", From 4380bfc7e7da6ee881fcad7d56713bffb946e08f Mon Sep 17 00:00:00 2001 From: Steven Dale Date: Mon, 27 Feb 2017 13:40:49 -0500 Subject: [PATCH 0068/1971] Configurable SpriteSelectorItems per row (#90). Standardized spacer variable (#84). MenuBar Pass (#130) * Standardized use of space units where appropriate * Reversed box classes for easier readability in Inspector * Sprite Area: removed unused info button * SpriteSelectorItems: fixed spacing between rows. Isolated layout + component CSS, now reusable in other context. Items per row is now easily configurable * MenuBar: refactored structure for clickable items --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 43f2a8ac01..b34a2e1900 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -12,14 +12,13 @@ const MenuBar = function MenuBar () { [styles.menuBar]: true })} > -
+
- - Animation Playtest Prototype +
Animation Playtest Prototype
); }; From 8f8de93d9730974caa2d124fdf6c5bf7f59eb231 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 28 Feb 2017 12:11:35 -0500 Subject: [PATCH 0069/1971] Merge pull request #136 from LLK/greenkeeper/autoprefixer-6.7.6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update autoprefixer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 644747a37c..3b7c7e967b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -23,7 +23,7 @@ "react-dom": "15.x.x" }, "devDependencies": { - "autoprefixer": "6.7.5", + "autoprefixer": "6.7.6", "babel-core": "6.23.1", "babel-eslint": "^7.1.1", "babel-loader": "6.3.2", From f954332e1fbf08560f58723e1c334184909087af Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 28 Feb 2017 14:00:30 -0500 Subject: [PATCH 0070/1971] Merge pull request #135 from rschamp/filter-toolbox Filter toolbox to blocks available to the VM --- packages/scratch-gui/src/containers/blocks.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index d27e91668c..9624372d4a 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -25,6 +25,8 @@ class Blocks extends React.Component { componentDidMount () { const workspaceConfig = defaultsDeep({}, Blocks.defaultOptions, this.props.options); this.workspace = this.ScratchBlocks.inject(this.blocks, workspaceConfig); + const filteredToolbox = this.props.vm.filterToolbox(this.workspace.options.languageTree); + this.workspace.updateToolbox(filteredToolbox); this.attachVM(); } componentWillUnmount () { From 3064a45769aa8e39c63c07d7bde28922f312b90b Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 2 Mar 2017 10:48:50 -0500 Subject: [PATCH 0071/1971] Merge pull request #152 from rschamp/wire-sprite-info Wire sprite info panel to VM --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3b7c7e967b..5ce6baf4f8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -53,6 +53,7 @@ "react-redux": "5.0.2", "react-style-proptype": "2.0.1", "redux": "3.6.0", + "redux-throttle": "0.1.1", "scratch-audio": "latest", "scratch-blocks": "latest", "scratch-render": "latest", From f12974c9a3e681d7c8102be796f2163bf5ac02c4 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 3 Mar 2017 10:06:14 -0500 Subject: [PATCH 0072/1971] Implement drag and drop for sprites on the stage (#159) * WIP: drag states in stage component * WIP: Hacky drag canvas * Update sprite position on drag Use VM to put sprite into drag state and update the sprite's position while dragging. Temporarily don't use the drawable data to drag a new canvas, this can happen whne we have somewhere to drag the sprite to. --- packages/scratch-gui/src/containers/stage.jsx | 103 +++++++++++++++--- 1 file changed, 88 insertions(+), 15 deletions(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 7136f56d31..3f693b78ca 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -11,13 +11,22 @@ class Stage extends React.Component { super(props); bindAll(this, [ 'attachMouseEvents', + 'cancelMouseDownTimeout', 'detachMouseEvents', 'onMouseUp', 'onMouseMove', 'onMouseDown', + 'onStartDrag', + 'onStopDrag', 'updateRect', 'setCanvas' ]); + this.state = { + mouseDownTimeoutId: null, + isDragging: false, + dragOffset: null, + dragId: null + }; } componentDidMount () { this.attachRectEvents(); @@ -28,18 +37,21 @@ class Stage extends React.Component { this.audioEngine = new AudioEngine(); this.props.vm.attachAudioEngine(this.audioEngine); } + shouldComponentUpdate () { + return false; + } componentWillUnmount () { this.detachMouseEvents(this.canvas); this.detachRectEvents(); } attachMouseEvents (canvas) { document.addEventListener('mousemove', this.onMouseMove); - canvas.addEventListener('mouseup', this.onMouseUp); + document.addEventListener('mouseup', this.onMouseUp); canvas.addEventListener('mousedown', this.onMouseDown); } detachMouseEvents (canvas) { document.removeEventListener('mousemove', this.onMouseMove); - canvas.removeEventListener('mouseup', this.onMouseUp); + document.removeEventListener('mouseup', this.onMouseUp); canvas.removeEventListener('mousedown', this.onMouseDown); } attachRectEvents () { @@ -53,37 +65,98 @@ class Stage extends React.Component { updateRect () { this.rect = this.canvas.getBoundingClientRect(); } + getScratchCoords (x, y) { + return [ + x - (this.rect.width / 2), + y - (this.rect.height / 2) + ]; + } onMouseMove (e) { + const mousePosition = [e.clientX - this.rect.left, e.clientY - this.rect.top]; + this.cancelMouseDownTimeout(); + if (this.state.mouseDown && !this.state.isDragging) { + this.onStartDrag(mousePosition[0], mousePosition[1]); + } + if (this.state.mouseDown && this.state.isDragging) { + const spritePosition = this.getScratchCoords(mousePosition[0], mousePosition[1]); + this.props.vm.postSpriteInfo({ + x: spritePosition[0] + this.state.dragOffset[0], + y: -(spritePosition[1] + this.state.dragOffset[1]), + force: true + }); + } const coordinates = { - x: e.clientX - this.rect.left, - y: e.clientY - this.rect.top, + x: mousePosition[0], + y: mousePosition[1], canvasWidth: this.rect.width, canvasHeight: this.rect.height }; this.props.vm.postIOData('mouse', coordinates); } onMouseUp (e) { - const data = { - isDown: false, - x: e.clientX - this.rect.left, - y: e.clientY - this.rect.top, - canvasWidth: this.rect.width, - canvasHeight: this.rect.height - }; - this.props.vm.postIOData('mouse', data); - e.preventDefault(); + this.cancelMouseDownTimeout(); + this.setState({ + mouseDown: false + }); + if (this.state.isDragging) { + this.onStopDrag(); + } else { + const data = { + isDown: false, + x: e.clientX - this.rect.left, + y: e.clientY - this.rect.top, + canvasWidth: this.rect.width, + canvasHeight: this.rect.height + }; + this.props.vm.postIOData('mouse', data); + } } onMouseDown (e) { + const mousePosition = [e.clientX - this.rect.left, e.clientY - this.rect.top]; + this.setState({ + mouseDown: true, + mouseDownTimeoutId: setTimeout( + this.onStartDrag.bind(this, mousePosition[0], mousePosition[1]), + 500 + ) + }); const data = { isDown: true, - x: e.clientX - this.rect.left, - y: e.clientY - this.rect.top, + x: mousePosition[0], + y: mousePosition[1], canvasWidth: this.rect.width, canvasHeight: this.rect.height }; this.props.vm.postIOData('mouse', data); e.preventDefault(); } + cancelMouseDownTimeout () { + if (this.state.mouseDownTimeoutId !== null) { + clearTimeout(this.state.mouseDownTimeoutId); + } + this.setState({mouseDownTimeoutId: null}); + } + onStartDrag (x, y) { + const drawableId = this.renderer.pick(x, y); + if (drawableId === null) return; + const drawableData = this.renderer.extractDrawable(drawableId, x, y); + const targetId = this.props.vm.getTargetIdForDrawableId(drawableId); + if (targetId === null) return; + this.props.vm.startDrag(targetId); + this.setState({ + isDragging: true, + dragId: targetId, + dragOffset: drawableData.scratchOffset + }); + } + onStopDrag () { + this.props.vm.stopDrag(this.state.dragId); + this.setState({ + isDragging: false, + dragOffset: null, + dragId: null + }); + } setCanvas (canvas) { this.canvas = canvas; } From 065d4f429d114d31e189485c7fe1a26213506fac Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 9 Mar 2017 11:55:14 -0500 Subject: [PATCH 0073/1971] Update dependencies (#228) * chore(package): update react-redux to version 5.0.3 https://greenkeeper.io/ * chore(package): update style-loader to version 0.13.2 https://greenkeeper.io/ * chore(package): update react-modal to version 1.7.2 Closes #151 https://greenkeeper.io/ * chore(package): update postcss-loader to version 1.3.3 https://greenkeeper.io/ * chore(package): update babel-loader to version 6.4.0 https://greenkeeper.io/ * chore(package): update css-loader to version 0.26.4 Closes #133 https://greenkeeper.io/ * Trust babel for the future --- packages/scratch-gui/package.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5ce6baf4f8..41888d82b5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -24,15 +24,15 @@ }, "devDependencies": { "autoprefixer": "6.7.6", - "babel-core": "6.23.1", + "babel-core": "^6.23.1", "babel-eslint": "^7.1.1", - "babel-loader": "6.3.2", - "babel-plugin-transform-object-rest-spread": "6.22.0", - "babel-preset-es2015": "6.22.0", - "babel-preset-react": "6.22.0", + "babel-loader": "^6.4.0", + "babel-plugin-transform-object-rest-spread": "^6.22.0", + "babel-preset-es2015": "^6.22.0", + "babel-preset-react": "^6.22.0", "classnames": "2.2.5", "copy-webpack-plugin": "4.0.1", - "css-loader": "0.26.1", + "css-loader": "0.26.4", "eslint": "^3.16.1", "eslint-config-scratch": "^3.0.0", "eslint-plugin-react": "^6.10.0", @@ -45,12 +45,12 @@ "lodash.pick": "4.4.0", "minilog": "3.1.0", "postcss-import": "9.1.0", - "postcss-loader": "1.3.2", + "postcss-loader": "1.3.3", "postcss-simple-vars": "3.0.0", "react": "15.4.2", "react-dom": "15.4.2", - "react-modal": "1.6.5", - "react-redux": "5.0.2", + "react-modal": "1.7.2", + "react-redux": "5.0.3", "react-style-proptype": "2.0.1", "redux": "3.6.0", "redux-throttle": "0.1.1", @@ -58,7 +58,7 @@ "scratch-blocks": "latest", "scratch-render": "latest", "scratch-vm": "latest", - "style-loader": "0.13.1", + "style-loader": "0.13.2", "svg-to-image": "1.1.3", "svg-url-loader": "2.0.2", "travis-after-all": "^1.4.4", From 98aae69cc802aa8ae6c3dc58b9bcd8048e041cb4 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 9 Mar 2017 14:08:16 -0500 Subject: [PATCH 0074/1971] Revert "Filter toolbox to blocks available to the VM" (#229) --- packages/scratch-gui/src/containers/blocks.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 9624372d4a..d27e91668c 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -25,8 +25,6 @@ class Blocks extends React.Component { componentDidMount () { const workspaceConfig = defaultsDeep({}, Blocks.defaultOptions, this.props.options); this.workspace = this.ScratchBlocks.inject(this.blocks, workspaceConfig); - const filteredToolbox = this.props.vm.filterToolbox(this.workspace.options.languageTree); - this.workspace.updateToolbox(filteredToolbox); this.attachVM(); } componentWillUnmount () { From bbf70e26cf868779f99634021511ecd0dc5048d8 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 10 Mar 2017 11:22:45 -0500 Subject: [PATCH 0075/1971] Merge pull request #232 from paulkaplan/per-target-workspace-view Store the scroll/zoom of the workspace on a per-target basis --- .../scratch-gui/src/containers/blocks.jsx | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index d27e91668c..9bf3aafb8e 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -6,6 +6,15 @@ const VM = require('scratch-vm'); const BlocksComponent = require('../components/blocks/blocks.jsx'); +const addFunctionListener = (object, property, callback) => { + const oldFn = object[property]; + object[property] = function () { + const result = oldFn.apply(this, arguments); + callback.apply(this, result); + return result; + }; +}; + class Blocks extends React.Component { constructor (props) { super(props); @@ -19,14 +28,24 @@ class Blocks extends React.Component { 'onBlockGlowOff', 'onVisualReport', 'onWorkspaceUpdate', + 'onWorkspaceMetricsChange', 'setBlocks' ]); + this.state = {workspaceMetrics: {}}; } componentDidMount () { const workspaceConfig = defaultsDeep({}, Blocks.defaultOptions, this.props.options); this.workspace = this.ScratchBlocks.inject(this.blocks, workspaceConfig); + + // @todo change this when blockly supports UI events + addFunctionListener(this.workspace, 'translate', this.onWorkspaceMetricsChange); + addFunctionListener(this.workspace, 'zoom', this.onWorkspaceMetricsChange); + this.attachVM(); } + shouldComponentUpdate () { + return false; + } componentWillUnmount () { this.detachVM(); this.workspace.dispose(); @@ -52,6 +71,19 @@ class Blocks extends React.Component { this.props.vm.off('VISUAL_REPORT', this.onVisualReport); this.props.vm.off('workspaceUpdate', this.onWorkspaceUpdate); } + onWorkspaceMetricsChange () { + const target = this.props.vm.editingTarget; + if (target && target.id) { + const workspaceMetrics = Object.assign({}, this.state.workspaceMetrics, { + [target.id]: { + scrollX: this.workspace.scrollX, + scrollY: this.workspace.scrollY, + scale: this.workspace.scale + } + }); + this.setState({workspaceMetrics}); + } + } onScriptGlowOn (data) { this.workspace.glowStack(data.id, true); } @@ -68,12 +100,25 @@ class Blocks extends React.Component { this.workspace.reportValue(data.id, data.value); } onWorkspaceUpdate (data) { + if (this.props.vm.editingTarget && !this.state.workspaceMetrics[this.props.vm.editingTarget.id]) { + this.onWorkspaceMetricsChange(); + } + this.ScratchBlocks.Events.disable(); this.workspace.clear(); + const dom = this.ScratchBlocks.Xml.textToDom(data.xml); this.ScratchBlocks.Xml.domToWorkspace(dom, this.workspace); this.ScratchBlocks.Events.enable(); this.workspace.toolbox_.refreshSelection(); + + if (this.props.vm.editingTarget && this.state.workspaceMetrics[this.props.vm.editingTarget.id]) { + const {scrollX, scrollY, scale} = this.state.workspaceMetrics[this.props.vm.editingTarget.id]; + this.workspace.scrollX = scrollX; + this.workspace.scrollY = scrollY; + this.workspace.scale = scale; + this.workspace.resize(); + } } setBlocks (blocks) { this.blocks = blocks; From 7fc93afaa1f1023eb0648fe897f634ce74b36f54 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 13 Mar 2017 11:51:18 -0400 Subject: [PATCH 0076/1971] Merge pull request #239 from paulkaplan/simple-blocks-bg-pattern Add simple blocks background pattern --- packages/scratch-gui/src/containers/blocks.jsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 9bf3aafb8e..200f0a92dc 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -168,6 +168,11 @@ Blocks.defaultOptions = { wheel: true, startScale: 0.75 }, + grid: { + spacing: 40, + length: 2, + colour: '#ddd' + }, colours: { workspace: '#F9F9F9', flyout: '#F9F9F9', From 2f462e7937d503677395b104a21ae2cba7510edc Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 23 Mar 2017 11:55:35 -0400 Subject: [PATCH 0077/1971] Merge pull request #265 from paulkaplan/tabbed-sounds Tabbed sound and costume panels --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 41888d82b5..5a2fe29f39 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -52,6 +52,7 @@ "react-modal": "1.7.2", "react-redux": "5.0.3", "react-style-proptype": "2.0.1", + "react-tabs": "0.8.2", "redux": "3.6.0", "redux-throttle": "0.1.1", "scratch-audio": "latest", From 7acf86564d4c58501cf0d61fa2cc1c927c5e9629 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Thu, 23 Mar 2017 22:13:59 -0700 Subject: [PATCH 0078/1971] Merge pull request #261 from cwillisf/use-storage Use scratch-storage --- packages/scratch-gui/package.json | 1 + packages/scratch-gui/src/containers/blocks.jsx | 5 ++--- packages/scratch-gui/src/containers/stage.jsx | 6 +----- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 6 +++++- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5a2fe29f39..a89d49487b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -58,6 +58,7 @@ "scratch-audio": "latest", "scratch-blocks": "latest", "scratch-render": "latest", + "scratch-storage": "latest", "scratch-vm": "latest", "style-loader": "0.13.2", "svg-to-image": "1.1.3", diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 200f0a92dc..b5f9798533 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -159,7 +159,7 @@ Blocks.propTypes = { dragShadowOpacity: React.PropTypes.number }) }), - vm: React.PropTypes.instanceOf(VM) + vm: React.PropTypes.instanceOf(VM).isRequired }; Blocks.defaultOptions = { @@ -188,8 +188,7 @@ Blocks.defaultOptions = { }; Blocks.defaultProps = { - options: Blocks.defaultOptions, - vm: new VM() + options: Blocks.defaultOptions }; module.exports = Blocks; diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 3f693b78ca..e5e0c06021 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -175,11 +175,7 @@ class Stage extends React.Component { } Stage.propTypes = { - vm: React.PropTypes.instanceOf(VM) -}; - -Stage.defaultProps = { - vm: new VM() + vm: React.PropTypes.instanceOf(VM).isRequired }; module.exports = Stage; diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 87c76c56ae..7ddb6493a8 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -2,6 +2,8 @@ const bindAll = require('lodash.bindall'); const React = require('react'); const VM = require('scratch-vm'); +const Storage = require('./storage'); + const {connect} = require('react-redux'); const targets = require('../reducers/targets'); @@ -89,9 +91,11 @@ const vmListenerHOC = function (WrappedComponent) { onTargetsUpdate: React.PropTypes.func, vm: React.PropTypes.instanceOf(VM).isRequired }; + const defaultVM = new VM('vm-listener-hoc'); + defaultVM.attachStorage(new Storage()); VMListener.defaultProps = { attachKeyboardEvents: true, - vm: new VM() + vm: defaultVM }; const mapStateToProps = () => ({}); const mapDispatchToProps = dispatch => ({ From 50827d89a8ddc7056bdd85d8510a361e15f5fcc8 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 27 Mar 2017 12:09:03 -0400 Subject: [PATCH 0079/1971] Merge pull request #266 from paulkaplan/fix-listener-functions Change #off to #removeListener --- .../scratch-gui/src/containers/blocks.jsx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index b5f9798533..35a0d6f932 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -56,20 +56,20 @@ class Blocks extends React.Component { .getFlyout() .getWorkspace() .addChangeListener(this.props.vm.flyoutBlockListener); - this.props.vm.on('SCRIPT_GLOW_ON', this.onScriptGlowOn); - this.props.vm.on('SCRIPT_GLOW_OFF', this.onScriptGlowOff); - this.props.vm.on('BLOCK_GLOW_ON', this.onBlockGlowOn); - this.props.vm.on('BLOCK_GLOW_OFF', this.onBlockGlowOff); - this.props.vm.on('VISUAL_REPORT', this.onVisualReport); - this.props.vm.on('workspaceUpdate', this.onWorkspaceUpdate); + this.props.vm.addListener('SCRIPT_GLOW_ON', this.onScriptGlowOn); + this.props.vm.addListener('SCRIPT_GLOW_OFF', this.onScriptGlowOff); + this.props.vm.addListener('BLOCK_GLOW_ON', this.onBlockGlowOn); + this.props.vm.addListener('BLOCK_GLOW_OFF', this.onBlockGlowOff); + this.props.vm.addListener('VISUAL_REPORT', this.onVisualReport); + this.props.vm.addListener('workspaceUpdate', this.onWorkspaceUpdate); } detachVM () { - this.props.vm.off('SCRIPT_GLOW_ON', this.onScriptGlowOn); - this.props.vm.off('SCRIPT_GLOW_OFF', this.onScriptGlowOff); - this.props.vm.off('BLOCK_GLOW_ON', this.onBlockGlowOn); - this.props.vm.off('BLOCK_GLOW_OFF', this.onBlockGlowOff); - this.props.vm.off('VISUAL_REPORT', this.onVisualReport); - this.props.vm.off('workspaceUpdate', this.onWorkspaceUpdate); + this.props.vm.removeListener('SCRIPT_GLOW_ON', this.onScriptGlowOn); + this.props.vm.removeListener('SCRIPT_GLOW_OFF', this.onScriptGlowOff); + this.props.vm.removeListener('BLOCK_GLOW_ON', this.onBlockGlowOn); + this.props.vm.removeListener('BLOCK_GLOW_OFF', this.onBlockGlowOff); + this.props.vm.removeListener('VISUAL_REPORT', this.onVisualReport); + this.props.vm.removeListener('workspaceUpdate', this.onWorkspaceUpdate); } onWorkspaceMetricsChange () { const target = this.props.vm.editingTarget; From 58dde40b53baee1c5f7e2eadc046f6f22f62dd6b Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Sat, 1 Apr 2017 19:02:44 -0400 Subject: [PATCH 0080/1971] Update dependencies (#287) * chore(package): update autoprefixer to version 6.7.7 https://greenkeeper.io/ * chore(package): update css-loader to version 0.28.0 Closes #231 https://greenkeeper.io/ * chore(package): update postcss-simple-vars to version 3.1.0 https://greenkeeper.io/ * chore(package): update react-modal to version 1.7.3 https://greenkeeper.io/ * chore(package): update react-style-proptype to version 2.0.2 https://greenkeeper.io/ # Conflicts: # package.json * chore(package): update style-loader to version 0.16.1 Closes #253 https://greenkeeper.io/ * chore(package): update webpack to version 2.3.2 Closes #264 https://greenkeeper.io/ --- packages/scratch-gui/package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a89d49487b..955551384a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -23,7 +23,7 @@ "react-dom": "15.x.x" }, "devDependencies": { - "autoprefixer": "6.7.6", + "autoprefixer": "6.7.7", "babel-core": "^6.23.1", "babel-eslint": "^7.1.1", "babel-loader": "^6.4.0", @@ -32,7 +32,7 @@ "babel-preset-react": "^6.22.0", "classnames": "2.2.5", "copy-webpack-plugin": "4.0.1", - "css-loader": "0.26.4", + "css-loader": "0.28.0", "eslint": "^3.16.1", "eslint-config-scratch": "^3.0.0", "eslint-plugin-react": "^6.10.0", @@ -46,12 +46,12 @@ "minilog": "3.1.0", "postcss-import": "9.1.0", "postcss-loader": "1.3.3", - "postcss-simple-vars": "3.0.0", + "postcss-simple-vars": "3.1.0", "react": "15.4.2", "react-dom": "15.4.2", - "react-modal": "1.7.2", + "react-modal": "1.7.3", "react-redux": "5.0.3", - "react-style-proptype": "2.0.1", + "react-style-proptype": "2.0.2", "react-tabs": "0.8.2", "redux": "3.6.0", "redux-throttle": "0.1.1", @@ -60,11 +60,11 @@ "scratch-render": "latest", "scratch-storage": "latest", "scratch-vm": "latest", - "style-loader": "0.13.2", + "style-loader": "0.16.1", "svg-to-image": "1.1.3", "svg-url-loader": "2.0.2", "travis-after-all": "^1.4.4", - "webpack": "2.2.1", + "webpack": "2.3.2", "webpack-dev-server": "^2.4.1", "xhr": "2.4.0" } From 4366affc69ce4a9abdbd2b1033ad0ecef0c04237 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 13 Apr 2017 11:11:40 -0400 Subject: [PATCH 0081/1971] Merge pull request #310 from navignaw/fix-drag-sprite Fix dragging sprite only when mousedown starts on sprite --- packages/scratch-gui/src/containers/stage.jsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index e5e0c06021..113ae6ea94 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -23,6 +23,7 @@ class Stage extends React.Component { ]); this.state = { mouseDownTimeoutId: null, + mouseDownPosition: null, isDragging: false, dragOffset: null, dragId: null @@ -73,9 +74,11 @@ class Stage extends React.Component { } onMouseMove (e) { const mousePosition = [e.clientX - this.rect.left, e.clientY - this.rect.top]; - this.cancelMouseDownTimeout(); - if (this.state.mouseDown && !this.state.isDragging) { - this.onStartDrag(mousePosition[0], mousePosition[1]); + if (this.state.mouseDownTimeoutId !== null) { + this.cancelMouseDownTimeout(); + if (this.state.mouseDown && !this.state.isDragging) { + this.onStartDrag(...this.state.mouseDownPosition); + } } if (this.state.mouseDown && this.state.isDragging) { const spritePosition = this.getScratchCoords(mousePosition[0], mousePosition[1]); @@ -96,7 +99,8 @@ class Stage extends React.Component { onMouseUp (e) { this.cancelMouseDownTimeout(); this.setState({ - mouseDown: false + mouseDown: false, + mouseDownPosition: null }); if (this.state.isDragging) { this.onStopDrag(); @@ -115,6 +119,7 @@ class Stage extends React.Component { const mousePosition = [e.clientX - this.rect.left, e.clientY - this.rect.top]; this.setState({ mouseDown: true, + mouseDownPosition: mousePosition, mouseDownTimeoutId: setTimeout( this.onStartDrag.bind(this, mousePosition[0], mousePosition[1]), 500 From 172292565b895bb772b702848181de492a6977b8 Mon Sep 17 00:00:00 2001 From: Ivan Wang Date: Tue, 18 Apr 2017 20:54:43 -0700 Subject: [PATCH 0082/1971] Set editing target when double clicking on sprite in stage (#307) * Set editing target when double clicking on sprite in stage * rename double clicker handler and add comment --- packages/scratch-gui/src/containers/stage.jsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 113ae6ea94..4357f2c436 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -13,6 +13,7 @@ class Stage extends React.Component { 'attachMouseEvents', 'cancelMouseDownTimeout', 'detachMouseEvents', + 'handleDoubleClick', 'onMouseUp', 'onMouseMove', 'onMouseDown', @@ -72,6 +73,15 @@ class Stage extends React.Component { y - (this.rect.height / 2) ]; } + handleDoubleClick (e) { + // Set editing target from cursor position, if clicking on a sprite. + const mousePosition = [e.clientX - this.rect.left, e.clientY - this.rect.top]; + const drawableId = this.renderer.pick(mousePosition[0], mousePosition[1]); + if (drawableId === null) return; + const targetId = this.props.vm.getTargetIdForDrawableId(drawableId); + if (targetId === null) return; + this.props.vm.setEditingTarget(targetId); + } onMouseMove (e) { const mousePosition = [e.clientX - this.rect.left, e.clientY - this.rect.top]; if (this.state.mouseDownTimeoutId !== null) { @@ -173,6 +183,7 @@ class Stage extends React.Component { return ( ); From 6632b03aea25e701434a7c3c191170ba627f689b Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 19 Apr 2017 09:51:30 -0700 Subject: [PATCH 0083/1971] Merge pull request #309 from cwillisf/standardize-settings Standardize settings --- packages/scratch-gui/package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 955551384a..ce13086bc4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -5,7 +5,7 @@ "main": "./src/index.js", "scripts": { "build": "npm run clean && webpack --progress --colors --bail", - "clean": "rm -rf ./build && mkdir -p build", + "clean": "rimraf ./build && mkdirp build", "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", "lint": "eslint . --ext .js,.jsx", "start": "webpack-dev-server", @@ -43,6 +43,7 @@ "lodash.isequal": "4.5.0", "lodash.omit": "4.5.0", "lodash.pick": "4.4.0", + "mkdirp": "^0.5.1", "minilog": "3.1.0", "postcss-import": "9.1.0", "postcss-loader": "1.3.3", @@ -55,6 +56,7 @@ "react-tabs": "0.8.2", "redux": "3.6.0", "redux-throttle": "0.1.1", + "rimraf": "^2.6.1", "scratch-audio": "latest", "scratch-blocks": "latest", "scratch-render": "latest", From cc5c9f54555f57a4b55a67b012c59468d2175c6d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 20 Apr 2017 19:10:37 -0400 Subject: [PATCH 0084/1971] Merge pull request #319 from rschamp/greenkeeper-2017-4-20 Update dependencies --- packages/scratch-gui/package.json | 21 ++++++----- .../scratch-gui/src/containers/blocks.jsx | 37 ++++++++++--------- packages/scratch-gui/src/containers/stage.jsx | 3 +- .../scratch-gui/src/lib/vm-listener-hoc.jsx | 13 ++++--- 4 files changed, 39 insertions(+), 35 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ce13086bc4..7e36a090a4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -19,8 +19,8 @@ "url": "git+ssh://git@github.com/LLK/scratch-gui.git" }, "peerDependencies": { - "react": "15.x.x", - "react-dom": "15.x.x" + "react": "^15", + "react-dom": "^15" }, "devDependencies": { "autoprefixer": "6.7.7", @@ -43,17 +43,18 @@ "lodash.isequal": "4.5.0", "lodash.omit": "4.5.0", "lodash.pick": "4.4.0", - "mkdirp": "^0.5.1", "minilog": "3.1.0", + "mkdirp": "^0.5.1", "postcss-import": "9.1.0", "postcss-loader": "1.3.3", "postcss-simple-vars": "3.1.0", - "react": "15.4.2", - "react-dom": "15.4.2", - "react-modal": "1.7.3", - "react-redux": "5.0.3", - "react-style-proptype": "2.0.2", - "react-tabs": "0.8.2", + "prop-types": "15.5.8", + "react": "15.5.4", + "react-dom": "15.5.4", + "react-modal": "1.7.7", + "react-redux": "5.0.4", + "react-style-proptype": "3.0.0", + "react-tabs": "0.8.3", "redux": "3.6.0", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", @@ -66,7 +67,7 @@ "svg-to-image": "1.1.3", "svg-url-loader": "2.0.2", "travis-after-all": "^1.4.4", - "webpack": "2.3.2", + "webpack": "^2.4.1", "webpack-dev-server": "^2.4.1", "xhr": "2.4.0" } diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 35a0d6f932..69ae0bf1da 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -1,5 +1,6 @@ const bindAll = require('lodash.bindall'); const defaultsDeep = require('lodash.defaultsdeep'); +const PropTypes = require('prop-types'); const React = require('react'); const VMScratchBlocks = require('../lib/blocks'); const VM = require('scratch-vm'); @@ -139,27 +140,27 @@ class Blocks extends React.Component { } Blocks.propTypes = { - options: React.PropTypes.shape({ - media: React.PropTypes.string, - zoom: React.PropTypes.shape({ - controls: React.PropTypes.boolean, - wheel: React.PropTypes.boolean, - startScale: React.PropTypes.number + options: PropTypes.shape({ + media: PropTypes.string, + zoom: PropTypes.shape({ + controls: PropTypes.boolean, + wheel: PropTypes.boolean, + startScale: PropTypes.number }), - colours: React.PropTypes.shape({ - workspace: React.PropTypes.string, - flyout: React.PropTypes.string, - toolbox: React.PropTypes.string, - toolboxSelected: React.PropTypes.string, - scrollbar: React.PropTypes.string, - scrollbarHover: React.PropTypes.string, - insertionMarker: React.PropTypes.string, - insertionMarkerOpacity: React.PropTypes.number, - fieldShadow: React.PropTypes.string, - dragShadowOpacity: React.PropTypes.number + colours: PropTypes.shape({ + workspace: PropTypes.string, + flyout: PropTypes.string, + toolbox: PropTypes.string, + toolboxSelected: PropTypes.string, + scrollbar: PropTypes.string, + scrollbarHover: PropTypes.string, + insertionMarker: PropTypes.string, + insertionMarkerOpacity: PropTypes.number, + fieldShadow: PropTypes.string, + dragShadowOpacity: PropTypes.number }) }), - vm: React.PropTypes.instanceOf(VM).isRequired + vm: PropTypes.instanceOf(VM).isRequired }; Blocks.defaultOptions = { diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 4357f2c436..b268edc41a 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -1,4 +1,5 @@ const bindAll = require('lodash.bindall'); +const PropTypes = require('prop-types'); const React = require('react'); const Renderer = require('scratch-render'); const AudioEngine = require('scratch-audio'); @@ -191,7 +192,7 @@ class Stage extends React.Component { } Stage.propTypes = { - vm: React.PropTypes.instanceOf(VM).isRequired + vm: PropTypes.instanceOf(VM).isRequired }; module.exports = Stage; diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 7ddb6493a8..8c80ae5d96 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -1,4 +1,5 @@ const bindAll = require('lodash.bindall'); +const PropTypes = require('prop-types'); const React = require('react'); const VM = require('scratch-vm'); @@ -84,12 +85,12 @@ const vmListenerHOC = function (WrappedComponent) { } } VMListener.propTypes = { - attachKeyboardEvents: React.PropTypes.bool, - onKeyDown: React.PropTypes.func, - onKeyUp: React.PropTypes.func, - onSpriteInfoReport: React.PropTypes.func, - onTargetsUpdate: React.PropTypes.func, - vm: React.PropTypes.instanceOf(VM).isRequired + attachKeyboardEvents: PropTypes.bool, + onKeyDown: PropTypes.func, + onKeyUp: PropTypes.func, + onSpriteInfoReport: PropTypes.func, + onTargetsUpdate: PropTypes.func, + vm: PropTypes.instanceOf(VM).isRequired }; const defaultVM = new VM('vm-listener-hoc'); defaultVM.attachStorage(new Storage()); From 16a4acffb2bcdef193d2e7e84d000300374940fb Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 24 Apr 2017 12:49:02 -0400 Subject: [PATCH 0085/1971] Merge pull request #322 from LLK/greenkeeper/babel-loader-7.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update babel-loader to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7e36a090a4..e063e4f548 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -26,7 +26,7 @@ "autoprefixer": "6.7.7", "babel-core": "^6.23.1", "babel-eslint": "^7.1.1", - "babel-loader": "^6.4.0", + "babel-loader": "^7.0.0", "babel-plugin-transform-object-rest-spread": "^6.22.0", "babel-preset-es2015": "^6.22.0", "babel-preset-react": "^6.22.0", From 81af0099e3e1ca4510fb3936138fe30b4ea3d690 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 26 Apr 2017 10:35:25 -0400 Subject: [PATCH 0086/1971] Merge pull request #330 from paulkaplan/prompt Add prompt for variable creation --- .../scratch-gui/src/containers/blocks.jsx | 45 +++++++++++++++---- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 69ae0bf1da..ecd816204e 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -4,7 +4,7 @@ const PropTypes = require('prop-types'); const React = require('react'); const VMScratchBlocks = require('../lib/blocks'); const VM = require('scratch-vm'); - +const Prompt = require('./prompt.jsx'); const BlocksComponent = require('../components/blocks/blocks.jsx'); const addFunctionListener = (object, property, callback) => { @@ -23,6 +23,9 @@ class Blocks extends React.Component { bindAll(this, [ 'attachVM', 'detachVM', + 'handlePromptStart', + 'handlePromptCallback', + 'handlePromptClose', 'onScriptGlowOn', 'onScriptGlowOff', 'onBlockGlowOn', @@ -32,7 +35,11 @@ class Blocks extends React.Component { 'onWorkspaceMetricsChange', 'setBlocks' ]); - this.state = {workspaceMetrics: {}}; + this.ScratchBlocks.prompt = this.handlePromptStart; + this.state = { + workspaceMetrics: {}, + prompt: null + }; } componentDidMount () { const workspaceConfig = defaultsDeep({}, Blocks.defaultOptions, this.props.options); @@ -44,8 +51,8 @@ class Blocks extends React.Component { this.attachVM(); } - shouldComponentUpdate () { - return false; + shouldComponentUpdate (nextProps, nextState) { + return this.state.prompt !== nextState.prompt; } componentWillUnmount () { this.detachVM(); @@ -72,6 +79,7 @@ class Blocks extends React.Component { this.props.vm.removeListener('VISUAL_REPORT', this.onVisualReport); this.props.vm.removeListener('workspaceUpdate', this.onWorkspaceUpdate); } + onWorkspaceMetricsChange () { const target = this.props.vm.editingTarget; if (target && target.id) { @@ -124,6 +132,16 @@ class Blocks extends React.Component { setBlocks (blocks) { this.blocks = blocks; } + handlePromptStart (message, defaultValue, callback) { + this.setState({prompt: {callback, message, defaultValue}}); + } + handlePromptCallback (data) { + this.state.prompt.callback(data); + this.handlePromptClose(); + } + handlePromptClose () { + this.setState({prompt: null}); + } render () { const { options, // eslint-disable-line no-unused-vars @@ -131,10 +149,21 @@ class Blocks extends React.Component { ...props } = this.props; return ( - +
+ + {this.state.prompt ? ( + + ) : null} +
); } } From b8bcd29c2255f69fce40495254cc035ddb62aefb Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 3 May 2017 11:30:39 -0700 Subject: [PATCH 0087/1971] Merge pull request #335 from cwillisf/minor-pin-scratch Minor-pin all Scratch 3.0 prerelease modules --- packages/scratch-gui/package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e063e4f548..be8b114831 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -58,11 +58,11 @@ "redux": "3.6.0", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "latest", - "scratch-blocks": "latest", - "scratch-render": "latest", - "scratch-storage": "latest", - "scratch-vm": "latest", + "scratch-audio": "^0.1.0-prerelease.0", + "scratch-blocks": "^0.1.0-prerelease.0", + "scratch-render": "^0.1.0-prerelease.0", + "scratch-storage": "^0.0.1-prerelease.0", + "scratch-vm": "^0.1.0-prerelease.0", "style-loader": "0.16.1", "svg-to-image": "1.1.3", "svg-url-loader": "2.0.2", From b67f13187a71d1babc7907cd5fc3a68ae0475d42 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 4 May 2017 14:55:14 -0400 Subject: [PATCH 0088/1971] Merge pull request #337 from paulkaplan/monitors-redux Add reducer, components and containers for monitors --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index be8b114831..7765b8dd74 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -51,6 +51,7 @@ "prop-types": "15.5.8", "react": "15.5.4", "react-dom": "15.5.4", + "react-draggable": "2.2.6", "react-modal": "1.7.7", "react-redux": "5.0.4", "react-style-proptype": "3.0.0", From bd541fcec2007f224c0a3d8edcdf3953bf27e915 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 11 May 2017 15:40:34 -0400 Subject: [PATCH 0089/1971] Merge pull request #339 from rschamp/feature/serialization Integrate VM serialization changes --- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 8c80ae5d96..58ea1ca716 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -3,8 +3,6 @@ const PropTypes = require('prop-types'); const React = require('react'); const VM = require('scratch-vm'); -const Storage = require('./storage'); - const {connect} = require('react-redux'); const targets = require('../reducers/targets'); @@ -92,13 +90,12 @@ const vmListenerHOC = function (WrappedComponent) { onTargetsUpdate: PropTypes.func, vm: PropTypes.instanceOf(VM).isRequired }; - const defaultVM = new VM('vm-listener-hoc'); - defaultVM.attachStorage(new Storage()); VMListener.defaultProps = { - attachKeyboardEvents: true, - vm: defaultVM + attachKeyboardEvents: true }; - const mapStateToProps = () => ({}); + const mapStateToProps = state => ({ + vm: state.vm + }); const mapDispatchToProps = dispatch => ({ onTargetsUpdate: data => { dispatch(targets.updateEditingTarget(data.editingTarget)); From a9dd9a98559e9b846d91d70dfb8d96babd3830d5 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 11 May 2017 16:17:36 -0400 Subject: [PATCH 0090/1971] Merge pull request #342 from rschamp/feature/save-load Add Save and Load buttons --- packages/scratch-gui/package.json | 2 +- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7765b8dd74..7d221cc7cc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -62,7 +62,7 @@ "scratch-audio": "^0.1.0-prerelease.0", "scratch-blocks": "^0.1.0-prerelease.0", "scratch-render": "^0.1.0-prerelease.0", - "scratch-storage": "^0.0.1-prerelease.0", + "scratch-storage": "^0.1.0", "scratch-vm": "^0.1.0-prerelease.0", "style-loader": "0.16.1", "svg-to-image": "1.1.3", diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index b34a2e1900..4397b5002e 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -2,6 +2,9 @@ const classNames = require('classnames'); const React = require('react'); const Box = require('../box/box.jsx'); +const LoadButton = require('../../containers/load-button.jsx'); +const SaveButton = require('../../containers/save-button.jsx'); + const styles = require('./menu-bar.css'); const scratchLogo = require('./scratch-logo.svg'); @@ -18,7 +21,8 @@ const MenuBar = function MenuBar () { src={scratchLogo} />
-
Animation Playtest Prototype
+ + ); }; From 3fabb4a34a870c11d58e3becad7dfdb13bfacbd8 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 12 May 2017 17:50:46 -0400 Subject: [PATCH 0091/1971] Merge pull request #361 from rschamp/performance/update_target Only listen to targets updates --- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 58ea1ca716..d2a7b0ca57 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -27,7 +27,6 @@ const vmListenerHOC = function (WrappedComponent) { // If the wrapped component uses the vm in componentDidMount, then // we need to start listening before mounting the wrapped component. this.props.vm.on('targetsUpdate', this.props.onTargetsUpdate); - this.props.vm.on('SPRITE_INFO_REPORT', this.props.onSpriteInfoReport); } componentDidMount () { if (this.props.attachKeyboardEvents) { @@ -100,9 +99,6 @@ const vmListenerHOC = function (WrappedComponent) { onTargetsUpdate: data => { dispatch(targets.updateEditingTarget(data.editingTarget)); dispatch(targets.updateTargets(data.targetList)); - }, - onSpriteInfoReport: spriteInfo => { - dispatch(targets.updateTarget(spriteInfo)); } }); return connect( From 10883a5433c69cc9b62de1ebb5b24653f331901d Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 17 May 2017 14:33:37 -0400 Subject: [PATCH 0092/1971] Merge pull request #368 from fsih/fixsquashed Fix squashed blocks in flyout when adding a sprite while on another tab --- .../scratch-gui/src/containers/blocks.jsx | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index ecd816204e..58d5cb2e1b 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -52,7 +52,21 @@ class Blocks extends React.Component { this.attachVM(); } shouldComponentUpdate (nextProps, nextState) { - return this.state.prompt !== nextState.prompt; + return this.state.prompt !== nextState.prompt || this.props.isVisible !== nextProps.isVisible; + } + componentDidUpdate (prevProps) { + if (this.props.isVisible === prevProps.isVisible) { + return; + } + + // @todo hack to resize blockly manually in case resize happened while hidden + if (this.props.isVisible) { // Scripts tab + window.dispatchEvent(new Event('resize')); + this.workspace.setVisible(true); + this.workspace.toolbox_.refreshSelection(); + } else { + this.workspace.setVisible(false); + } } componentWillUnmount () { this.detachVM(); @@ -146,6 +160,7 @@ class Blocks extends React.Component { const { options, // eslint-disable-line no-unused-vars vm, // eslint-disable-line no-unused-vars + isVisible, // eslint-disable-line no-unused-vars ...props } = this.props; return ( @@ -169,11 +184,12 @@ class Blocks extends React.Component { } Blocks.propTypes = { + isVisible: PropTypes.bool.isRequired, options: PropTypes.shape({ media: PropTypes.string, zoom: PropTypes.shape({ - controls: PropTypes.boolean, - wheel: PropTypes.boolean, + controls: PropTypes.bool, + wheel: PropTypes.bool, startScale: PropTypes.number }), colours: PropTypes.shape({ From 50f322ecfe8d1935ad0e993115a081b8dd081f3c Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 18 May 2017 17:21:45 -0400 Subject: [PATCH 0093/1971] Merge pull request #354 from fsih/monitors Listen for the monitor update from the VM --- packages/scratch-gui/src/containers/blocks.jsx | 7 ++++--- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 10 ++++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 58d5cb2e1b..487bed4807 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -74,10 +74,11 @@ class Blocks extends React.Component { } attachVM () { this.workspace.addChangeListener(this.props.vm.blockListener); - this.workspace + this.flyoutWorkspace = this.workspace .getFlyout() - .getWorkspace() - .addChangeListener(this.props.vm.flyoutBlockListener); + .getWorkspace(); + this.flyoutWorkspace.addChangeListener(this.props.vm.flyoutBlockListener); + this.flyoutWorkspace.addChangeListener(this.props.vm.monitorBlockListener); this.props.vm.addListener('SCRIPT_GLOW_ON', this.onScriptGlowOn); this.props.vm.addListener('SCRIPT_GLOW_OFF', this.onScriptGlowOff); this.props.vm.addListener('BLOCK_GLOW_ON', this.onBlockGlowOn); diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index d2a7b0ca57..e49de705e5 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -6,6 +6,7 @@ const VM = require('scratch-vm'); const {connect} = require('react-redux'); const targets = require('../reducers/targets'); +const monitors = require('../reducers/monitors'); /* * Higher Order Component to manage events emitted by the VM @@ -27,6 +28,8 @@ const vmListenerHOC = function (WrappedComponent) { // If the wrapped component uses the vm in componentDidMount, then // we need to start listening before mounting the wrapped component. this.props.vm.on('targetsUpdate', this.props.onTargetsUpdate); + this.props.vm.on('MONITORS_UPDATE', this.props.onMonitorsUpdate); + } componentDidMount () { if (this.props.attachKeyboardEvents) { @@ -73,7 +76,7 @@ const vmListenerHOC = function (WrappedComponent) { attachKeyboardEvents, onKeyDown, onKeyUp, - onSpriteInfoReport, + onMonitorsUpdate, onTargetsUpdate, /* eslint-enable no-unused-vars */ ...props @@ -85,7 +88,7 @@ const vmListenerHOC = function (WrappedComponent) { attachKeyboardEvents: PropTypes.bool, onKeyDown: PropTypes.func, onKeyUp: PropTypes.func, - onSpriteInfoReport: PropTypes.func, + onMonitorsUpdate: PropTypes.func, onTargetsUpdate: PropTypes.func, vm: PropTypes.instanceOf(VM).isRequired }; @@ -99,6 +102,9 @@ const vmListenerHOC = function (WrappedComponent) { onTargetsUpdate: data => { dispatch(targets.updateEditingTarget(data.editingTarget)); dispatch(targets.updateTargets(data.targetList)); + }, + onMonitorsUpdate: monitorList => { + dispatch(monitors.updateMonitors(monitorList)); } }); return connect( From a05b9911b240d6e5088a68284a2509b860cb3d06 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 19 May 2017 13:04:24 -0700 Subject: [PATCH 0094/1971] Merge pull request #374 from rschamp/greenkeeper Update dependencies --- packages/scratch-gui/package.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7d221cc7cc..54d7f7f2f9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -23,7 +23,7 @@ "react-dom": "^15" }, "devDependencies": { - "autoprefixer": "6.7.7", + "autoprefixer": "7.1.0", "babel-core": "^6.23.1", "babel-eslint": "^7.1.1", "babel-loader": "^7.0.0", @@ -32,11 +32,11 @@ "babel-preset-react": "^6.22.0", "classnames": "2.2.5", "copy-webpack-plugin": "4.0.1", - "css-loader": "0.28.0", + "css-loader": "0.28.1", "eslint": "^3.16.1", "eslint-config-scratch": "^3.0.0", - "eslint-plugin-react": "^6.10.0", - "gh-pages": "^0.12.0", + "eslint-plugin-react": "^7.0.1", + "gh-pages": "^1.0.0", "html-webpack-plugin": "2.28.0", "lodash.bindall": "4.4.0", "lodash.defaultsdeep": "4.6.0", @@ -45,17 +45,17 @@ "lodash.pick": "4.4.0", "minilog": "3.1.0", "mkdirp": "^0.5.1", - "postcss-import": "9.1.0", - "postcss-loader": "1.3.3", - "postcss-simple-vars": "3.1.0", - "prop-types": "15.5.8", + "postcss-import": "^10.0.0", + "postcss-loader": "^2.0.5", + "postcss-simple-vars": "^4.0.0", + "prop-types": "^15.5.10", "react": "15.5.4", "react-dom": "15.5.4", "react-draggable": "2.2.6", "react-modal": "1.7.7", - "react-redux": "5.0.4", + "react-redux": "5.0.5", "react-style-proptype": "3.0.0", - "react-tabs": "0.8.3", + "react-tabs": "1.0.0", "redux": "3.6.0", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", @@ -64,7 +64,7 @@ "scratch-render": "^0.1.0-prerelease.0", "scratch-storage": "^0.1.0", "scratch-vm": "^0.1.0-prerelease.0", - "style-loader": "0.16.1", + "style-loader": "^0.17.0", "svg-to-image": "1.1.3", "svg-url-loader": "2.0.2", "travis-after-all": "^1.4.4", From b3f55b7712b68fbb0c32510e8d94bb9e26f7409c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 25 May 2017 16:38:38 -0400 Subject: [PATCH 0095/1971] Merge pull request #373 from paulkaplan/updating-block-numbers Update shadow block values when sprite moves --- packages/scratch-gui/package.json | 1 + .../scratch-gui/src/containers/blocks.jsx | 23 ++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 54d7f7f2f9..eeabbe19fe 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -39,6 +39,7 @@ "gh-pages": "^1.0.0", "html-webpack-plugin": "2.28.0", "lodash.bindall": "4.4.0", + "lodash.debounce": "4.0.8", "lodash.defaultsdeep": "4.6.0", "lodash.isequal": "4.5.0", "lodash.omit": "4.5.0", diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 487bed4807..a2146d3f8e 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -1,4 +1,5 @@ const bindAll = require('lodash.bindall'); +const debounce = require('lodash.debounce'); const defaultsDeep = require('lodash.defaultsdeep'); const PropTypes = require('prop-types'); const React = require('react'); @@ -30,6 +31,7 @@ class Blocks extends React.Component { 'onScriptGlowOff', 'onBlockGlowOn', 'onBlockGlowOff', + 'onTargetsUpdate', 'onVisualReport', 'onWorkspaceUpdate', 'onWorkspaceMetricsChange', @@ -40,6 +42,7 @@ class Blocks extends React.Component { workspaceMetrics: {}, prompt: null }; + this.onTargetsUpdate = debounce(this.onTargetsUpdate, 100); } componentDidMount () { const workspaceConfig = defaultsDeep({}, Blocks.defaultOptions, this.props.options); @@ -85,6 +88,7 @@ class Blocks extends React.Component { this.props.vm.addListener('BLOCK_GLOW_OFF', this.onBlockGlowOff); this.props.vm.addListener('VISUAL_REPORT', this.onVisualReport); this.props.vm.addListener('workspaceUpdate', this.onWorkspaceUpdate); + this.props.vm.addListener('targetsUpdate', this.onTargetsUpdate); } detachVM () { this.props.vm.removeListener('SCRIPT_GLOW_ON', this.onScriptGlowOn); @@ -93,8 +97,25 @@ class Blocks extends React.Component { this.props.vm.removeListener('BLOCK_GLOW_OFF', this.onBlockGlowOff); this.props.vm.removeListener('VISUAL_REPORT', this.onVisualReport); this.props.vm.removeListener('workspaceUpdate', this.onWorkspaceUpdate); + this.props.vm.removeListener('targetsUpdate', this.onTargetsUpdate); + } + updateToolboxBlockValue (id, value) { + this.workspace + .getFlyout() + .getWorkspace() + .getBlockById(id) + .inputList[0] + .fieldRow[0] + .setValue(value); + } + onTargetsUpdate () { + if (this.props.vm.editingTarget) { + ['glide', 'move', 'set'].forEach(prefix => { + this.updateToolboxBlockValue(`${prefix}x`, this.props.vm.editingTarget.x.toFixed(0)); + this.updateToolboxBlockValue(`${prefix}y`, this.props.vm.editingTarget.y.toFixed(0)); + }); + } } - onWorkspaceMetricsChange () { const target = this.props.vm.editingTarget; if (target && target.id) { From 0735ec6be0c2ca4a7676485763aa51fccfda0f54 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 30 May 2017 09:35:43 -0400 Subject: [PATCH 0096/1971] Merge pull request #390 from paulkaplan/feature/variable-creation Explicitly create variables in the VM --- packages/scratch-gui/src/containers/blocks.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index a2146d3f8e..13b3975fda 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -148,7 +148,6 @@ class Blocks extends React.Component { if (this.props.vm.editingTarget && !this.state.workspaceMetrics[this.props.vm.editingTarget.id]) { this.onWorkspaceMetricsChange(); } - this.ScratchBlocks.Events.disable(); this.workspace.clear(); @@ -173,6 +172,7 @@ class Blocks extends React.Component { } handlePromptCallback (data) { this.state.prompt.callback(data); + this.props.vm.createVariable(data); this.handlePromptClose(); } handlePromptClose () { From 93d13f4e9a44c09bdb8ea1c1859784cce8b85379 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 30 May 2017 12:42:55 -0400 Subject: [PATCH 0097/1971] Merge pull request #381 from fsih/immutableMonitors Use immutable maps for the monitor state in gui --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index eeabbe19fe..3429ec678e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -38,6 +38,7 @@ "eslint-plugin-react": "^7.0.1", "gh-pages": "^1.0.0", "html-webpack-plugin": "2.28.0", + "immutable": "3.8.1", "lodash.bindall": "4.4.0", "lodash.debounce": "4.0.8", "lodash.defaultsdeep": "4.6.0", From 09ae06836be3a9d1fc8334dc7f27d5d96075a135 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 1 Jun 2017 10:04:14 -0400 Subject: [PATCH 0098/1971] Merge pull request #377 from LLK/greenkeeper/autoprefixer-7.1.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update autoprefixer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3429ec678e..4592e9d9f0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -23,7 +23,7 @@ "react-dom": "^15" }, "devDependencies": { - "autoprefixer": "7.1.0", + "autoprefixer": "7.1.1", "babel-core": "^6.23.1", "babel-eslint": "^7.1.1", "babel-loader": "^7.0.0", From 5d87f5f546c007f26f36dea0e2e81e691d104e4b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 1 Jun 2017 10:19:28 -0400 Subject: [PATCH 0099/1971] Merge pull request #378 from LLK/greenkeeper/style-loader-0.18.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update style-loader to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4592e9d9f0..e81c322134 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -66,7 +66,7 @@ "scratch-render": "^0.1.0-prerelease.0", "scratch-storage": "^0.1.0", "scratch-vm": "^0.1.0-prerelease.0", - "style-loader": "^0.17.0", + "style-loader": "^0.18.0", "svg-to-image": "1.1.3", "svg-url-loader": "2.0.2", "travis-after-all": "^1.4.4", From 16f7b926d05ead9c0d13abe3a92f793f8f4a6476 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 1 Jun 2017 10:25:37 -0400 Subject: [PATCH 0100/1971] Merge pull request #392 from LLK/greenkeeper/css-loader-0.28.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update css-loader to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e81c322134..f6f126fa68 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -32,7 +32,7 @@ "babel-preset-react": "^6.22.0", "classnames": "2.2.5", "copy-webpack-plugin": "4.0.1", - "css-loader": "0.28.1", + "css-loader": "0.28.3", "eslint": "^3.16.1", "eslint-config-scratch": "^3.0.0", "eslint-plugin-react": "^7.0.1", From 656306677cbf0f288487ddcbe0beb717f2e7aa9d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 2 Jun 2017 09:02:06 -0400 Subject: [PATCH 0101/1971] Merge pull request #405 from paulkaplan/fix-block-updates Check if blocks exists before updating values --- packages/scratch-gui/src/containers/blocks.jsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 13b3975fda..3205f63a25 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -100,13 +100,13 @@ class Blocks extends React.Component { this.props.vm.removeListener('targetsUpdate', this.onTargetsUpdate); } updateToolboxBlockValue (id, value) { - this.workspace + const block = this.workspace .getFlyout() .getWorkspace() - .getBlockById(id) - .inputList[0] - .fieldRow[0] - .setValue(value); + .getBlockById(id); + if (block) { + block.inputList[0].fieldRow[0].setValue(value); + } } onTargetsUpdate () { if (this.props.vm.editingTarget) { From a9e6551d7d7a3ec8199bb341e2bad81c2e5af109 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 2 Jun 2017 09:03:38 -0400 Subject: [PATCH 0102/1971] Merge pull request #403 from paulkaplan/hotfix/fix-resize-bug Show workspace before triggering resize --- packages/scratch-gui/src/containers/blocks.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 3205f63a25..b42560ec2d 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -64,8 +64,8 @@ class Blocks extends React.Component { // @todo hack to resize blockly manually in case resize happened while hidden if (this.props.isVisible) { // Scripts tab - window.dispatchEvent(new Event('resize')); this.workspace.setVisible(true); + window.dispatchEvent(new Event('resize')); this.workspace.toolbox_.refreshSelection(); } else { this.workspace.setVisible(false); From 2c5cd9a70b8c2fa18d8a933c6f67cbc6ae788c63 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 2 Jun 2017 10:19:02 -0400 Subject: [PATCH 0103/1971] Merge pull request #389 from cwillisf/load-jpeg-backdrops Asset load: use asset's data format, not default --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f6f126fa68..0f623cc656 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -64,7 +64,7 @@ "scratch-audio": "^0.1.0-prerelease.0", "scratch-blocks": "^0.1.0-prerelease.0", "scratch-render": "^0.1.0-prerelease.0", - "scratch-storage": "^0.1.0", + "scratch-storage": "^0.2.0", "scratch-vm": "^0.1.0-prerelease.0", "style-loader": "^0.18.0", "svg-to-image": "1.1.3", From c32e56e48fbffb824fe864a73737409a576ac603 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 12 Jun 2017 10:21:11 -0400 Subject: [PATCH 0104/1971] Merge pull request #436 from paulkaplan/disable-commenting Use `comments` flag to disable comments in scratch-blocks --- packages/scratch-gui/src/containers/blocks.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index b42560ec2d..9967412677 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -225,7 +225,8 @@ Blocks.propTypes = { insertionMarkerOpacity: PropTypes.number, fieldShadow: PropTypes.string, dragShadowOpacity: PropTypes.number - }) + }), + comments: PropTypes.bool }), vm: PropTypes.instanceOf(VM).isRequired }; @@ -252,7 +253,8 @@ Blocks.defaultOptions = { insertionMarkerOpacity: 0.2, fieldShadow: 'rgba(255, 255, 255, 0.3)', dragShadowOpacity: 0.6 - } + }, + comments: false }; Blocks.defaultProps = { From aa7606dcf5f6dc3fc18f52c66be9c7fe16718276 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 14 Jun 2017 16:24:08 -0400 Subject: [PATCH 0105/1971] Merge pull request #433 from rschamp/deploy/multi-branch Deploy all branches but master to subdirectories --- packages/scratch-gui/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0f623cc656..5493ddeca1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -9,7 +9,8 @@ "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", "lint": "eslint . --ext .js,.jsx", "start": "webpack-dev-server", - "test": "npm run lint && npm run build" + "test": "npm run lint && npm run build", + "watch": "webpack --progress --colors --watch" }, "author": "Massachusetts Institute of Technology", "license": "BSD-3-Clause", @@ -36,7 +37,7 @@ "eslint": "^3.16.1", "eslint-config-scratch": "^3.0.0", "eslint-plugin-react": "^7.0.1", - "gh-pages": "^1.0.0", + "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", "html-webpack-plugin": "2.28.0", "immutable": "3.8.1", "lodash.bindall": "4.4.0", @@ -69,7 +70,6 @@ "style-loader": "^0.18.0", "svg-to-image": "1.1.3", "svg-url-loader": "2.0.2", - "travis-after-all": "^1.4.4", "webpack": "^2.4.1", "webpack-dev-server": "^2.4.1", "xhr": "2.4.0" From be4d2d648da3900a35047316332ccae6c285807c Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 14 Jun 2017 17:03:24 -0400 Subject: [PATCH 0106/1971] Merge pull request #404 from rschamp/bugfix/npm-install Pin to `latest` for 3.0 modules --- packages/scratch-gui/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5493ddeca1..75bd48ebcb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -62,11 +62,11 @@ "redux": "3.6.0", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "^0.1.0-prerelease.0", - "scratch-blocks": "^0.1.0-prerelease.0", - "scratch-render": "^0.1.0-prerelease.0", + "scratch-audio": "latest", + "scratch-blocks": "latest", + "scratch-render": "latest", "scratch-storage": "^0.2.0", - "scratch-vm": "^0.1.0-prerelease.0", + "scratch-vm": "latest", "style-loader": "^0.18.0", "svg-to-image": "1.1.3", "svg-url-loader": "2.0.2", From b5e61569bdd96f06c8f0d915637fdaaa6d374495 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 15 Jun 2017 16:44:50 -0400 Subject: [PATCH 0107/1971] Merge pull request #459 from fsih/clearWorkspace Refresh workspace when setting it to visible --- packages/scratch-gui/src/containers/blocks.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 9967412677..3bce0f3c54 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -63,8 +63,10 @@ class Blocks extends React.Component { } // @todo hack to resize blockly manually in case resize happened while hidden + // @todo hack to reload the workspace due to gui bug #413 if (this.props.isVisible) { // Scripts tab this.workspace.setVisible(true); + this.props.vm.refreshWorkspace(); window.dispatchEvent(new Event('resize')); this.workspace.toolbox_.refreshSelection(); } else { From 7d2336388c4d02f87513eaeb30cfd6ce6330c4db Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 23 Jun 2017 13:05:26 -0400 Subject: [PATCH 0108/1971] Merge pull request #468 from LLK/revert-390-feature/variable-creation Revert "Explicitly create variables in the VM" --- packages/scratch-gui/src/containers/blocks.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 3bce0f3c54..59e868436f 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -150,6 +150,7 @@ class Blocks extends React.Component { if (this.props.vm.editingTarget && !this.state.workspaceMetrics[this.props.vm.editingTarget.id]) { this.onWorkspaceMetricsChange(); } + this.ScratchBlocks.Events.disable(); this.workspace.clear(); @@ -174,7 +175,6 @@ class Blocks extends React.Component { } handlePromptCallback (data) { this.state.prompt.callback(data); - this.props.vm.createVariable(data); this.handlePromptClose(); } handlePromptClose () { From 022b40ed26ff63ae17b5762257a0007ea70df029 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 27 Jun 2017 14:36:11 -0400 Subject: [PATCH 0109/1971] Merge pull request #469 from LLK/record-modal Record a new sound from the mic --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 75bd48ebcb..299fcd8d8a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -70,6 +70,7 @@ "style-loader": "^0.18.0", "svg-to-image": "1.1.3", "svg-url-loader": "2.0.2", + "wav-encoder": "1.1.0", "webpack": "^2.4.1", "webpack-dev-server": "^2.4.1", "xhr": "2.4.0" From 0c14d2d159d3888d4bb5463818445005b5a2958a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 30 Jun 2017 15:11:16 -0400 Subject: [PATCH 0110/1971] Merge pull request #487 from chrisgarrity/feature/add-intl-translation Localize first elements --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 299fcd8d8a..728c99a038 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -55,6 +55,7 @@ "react": "15.5.4", "react-dom": "15.5.4", "react-draggable": "2.2.6", + "react-intl": "2.3.0", "react-modal": "1.7.7", "react-redux": "5.0.5", "react-style-proptype": "3.0.0", From 15cfeefaa243c4d6b3488e02bfa0fcafd5d6c8e1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 7 Jul 2017 12:57:34 -0400 Subject: [PATCH 0111/1971] Merge pull request #490 from paulkaplan/move-to-import-from-require MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert require to import 👻 😭 --- .../src/components/menu-bar/menu-bar.jsx | 14 +++++++------- packages/scratch-gui/src/containers/blocks.jsx | 18 +++++++++--------- packages/scratch-gui/src/containers/stage.jsx | 14 +++++++------- .../scratch-gui/src/lib/vm-listener-hoc.jsx | 14 +++++++------- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 4397b5002e..c22ba3636c 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -1,12 +1,12 @@ -const classNames = require('classnames'); -const React = require('react'); +import classNames from 'classnames'; +import React from 'react'; -const Box = require('../box/box.jsx'); -const LoadButton = require('../../containers/load-button.jsx'); -const SaveButton = require('../../containers/save-button.jsx'); +import Box from '../box/box.jsx'; +import LoadButton from '../../containers/load-button.jsx'; +import SaveButton from '../../containers/save-button.jsx'; -const styles = require('./menu-bar.css'); -const scratchLogo = require('./scratch-logo.svg'); +import styles from './menu-bar.css'; +import scratchLogo from './scratch-logo.svg'; const MenuBar = function MenuBar () { return ( diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 59e868436f..bc8e3ff384 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -1,12 +1,12 @@ -const bindAll = require('lodash.bindall'); -const debounce = require('lodash.debounce'); -const defaultsDeep = require('lodash.defaultsdeep'); -const PropTypes = require('prop-types'); -const React = require('react'); -const VMScratchBlocks = require('../lib/blocks'); -const VM = require('scratch-vm'); -const Prompt = require('./prompt.jsx'); -const BlocksComponent = require('../components/blocks/blocks.jsx'); +import bindAll from 'lodash.bindall'; +import debounce from 'lodash.debounce'; +import defaultsDeep from 'lodash.defaultsdeep'; +import PropTypes from 'prop-types'; +import React from 'react'; +import VMScratchBlocks from '../lib/blocks'; +import VM from 'scratch-vm'; +import Prompt from './prompt.jsx'; +import BlocksComponent from '../components/blocks/blocks.jsx'; const addFunctionListener = (object, property, callback) => { const oldFn = object[property]; diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index b268edc41a..0c161023be 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -1,11 +1,11 @@ -const bindAll = require('lodash.bindall'); -const PropTypes = require('prop-types'); -const React = require('react'); -const Renderer = require('scratch-render'); -const AudioEngine = require('scratch-audio'); -const VM = require('scratch-vm'); +import bindAll from 'lodash.bindall'; +import PropTypes from 'prop-types'; +import React from 'react'; +import Renderer from 'scratch-render'; +import AudioEngine from 'scratch-audio'; +import VM from 'scratch-vm'; -const StageComponent = require('../components/stage/stage.jsx'); +import StageComponent from '../components/stage/stage.jsx'; class Stage extends React.Component { constructor (props) { diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index e49de705e5..8cbf05c504 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -1,12 +1,12 @@ -const bindAll = require('lodash.bindall'); -const PropTypes = require('prop-types'); -const React = require('react'); -const VM = require('scratch-vm'); +import bindAll from 'lodash.bindall'; +import PropTypes from 'prop-types'; +import React from 'react'; +import VM from 'scratch-vm'; -const {connect} = require('react-redux'); +import {connect} from 'react-redux'; -const targets = require('../reducers/targets'); -const monitors = require('../reducers/monitors'); +import targets from '../reducers/targets'; +import monitors from '../reducers/monitors'; /* * Higher Order Component to manage events emitted by the VM From 0f100bbace8193383126eea24860cb582fb9ab92 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 10 Jul 2017 10:15:53 -0400 Subject: [PATCH 0112/1971] Merge pull request #454 from LLK/greenkeeper/react-tabs-1.1.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-tabs to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 728c99a038..f615c546c1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -59,7 +59,7 @@ "react-modal": "1.7.7", "react-redux": "5.0.5", "react-style-proptype": "3.0.0", - "react-tabs": "1.0.0", + "react-tabs": "1.1.0", "redux": "3.6.0", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", From 23d5510ce94d06a6217c8d68e14f1026609665b3 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 10 Jul 2017 10:38:56 -0400 Subject: [PATCH 0113/1971] Merge pull request #506 from LLK/greenkeeper/autoprefixer-7.1.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update autoprefixer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f615c546c1..291f4f9262 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -24,7 +24,7 @@ "react-dom": "^15" }, "devDependencies": { - "autoprefixer": "7.1.1", + "autoprefixer": "7.1.2", "babel-core": "^6.23.1", "babel-eslint": "^7.1.1", "babel-loader": "^7.0.0", From 9fccb933f6ab8ceb179bcc0b9f21fc98e2caec5c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 10 Jul 2017 10:40:13 -0400 Subject: [PATCH 0114/1971] Merge pull request #475 from LLK/greenkeeper/svg-url-loader-2.1.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update svg-url-loader to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 291f4f9262..15ff2a26bd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -70,7 +70,7 @@ "scratch-vm": "latest", "style-loader": "^0.18.0", "svg-to-image": "1.1.3", - "svg-url-loader": "2.0.2", + "svg-url-loader": "2.1.0", "wav-encoder": "1.1.0", "webpack": "^2.4.1", "webpack-dev-server": "^2.4.1", From 92ddb21462629767e6aac6a8dc4964b164f61a97 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 11 Jul 2017 08:49:30 -0400 Subject: [PATCH 0115/1971] Merge pull request #456 from LLK/greenkeeper/react-15.6.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 15ff2a26bd..73e017db5d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -20,7 +20,7 @@ "url": "git+ssh://git@github.com/LLK/scratch-gui.git" }, "peerDependencies": { - "react": "^15", + "react": "^15.6.0", "react-dom": "^15" }, "devDependencies": { From 235fe1a14cf1e42bce11dc090bb557e3fae8b706 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 11 Jul 2017 10:06:04 -0400 Subject: [PATCH 0116/1971] Merge pull request #526 from LLK/greenkeeper/react-modal-2.2.1 chore(package): update react-modal to version 2.2.1 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 73e017db5d..bb85e0e2c3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -55,8 +55,8 @@ "react": "15.5.4", "react-dom": "15.5.4", "react-draggable": "2.2.6", + "react-modal": "2.2.1", "react-intl": "2.3.0", - "react-modal": "1.7.7", "react-redux": "5.0.5", "react-style-proptype": "3.0.0", "react-tabs": "1.1.0", From 25184e2e0933813ed0f0df6d16ada029a4ca0f65 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 17 Jul 2017 08:17:33 -0400 Subject: [PATCH 0117/1971] Merge pull request #457 from LLK/greenkeeper/react-dom-15.6.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-dom to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index bb85e0e2c3..de1e45edb8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -21,7 +21,7 @@ }, "peerDependencies": { "react": "^15.6.0", - "react-dom": "^15" + "react-dom": "^15.6.0" }, "devDependencies": { "autoprefixer": "7.1.2", From 6292e6931bc8f8b72ed06bbe72c72fd1f175d2f1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 17 Jul 2017 08:34:33 -0400 Subject: [PATCH 0118/1971] Merge pull request #460 from LLK/greenkeeper/redux-3.7.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update redux to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index de1e45edb8..ef161dfa76 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -60,7 +60,7 @@ "react-redux": "5.0.5", "react-style-proptype": "3.0.0", "react-tabs": "1.1.0", - "redux": "3.6.0", + "redux": "3.7.0", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "latest", From b45b6d4410272b242596b448ec9ba5220591ed5c Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Mon, 17 Jul 2017 14:27:08 -0400 Subject: [PATCH 0119/1971] Merge pull request #515 from chrisgarrity/gh507-generate-l10n Gh507 generate l10n --- packages/scratch-gui/package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ef161dfa76..607b69edbd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -7,6 +7,7 @@ "build": "npm run clean && webpack --progress --colors --bail", "clean": "rimraf ./build && mkdirp build", "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", + "i18n:src": "babel src > tmp.js && rimraf tmp.js && ./scripts/build-i18n-source.js ./translations/messages/ ./translations/", "lint": "eslint . --ext .js,.jsx", "start": "webpack-dev-server", "test": "npm run lint && npm run build", @@ -25,9 +26,11 @@ }, "devDependencies": { "autoprefixer": "7.1.2", + "babel-cli": "6.24.1", "babel-core": "^6.23.1", "babel-eslint": "^7.1.1", "babel-loader": "^7.0.0", + "babel-plugin-react-intl": "2.3.1", "babel-plugin-transform-object-rest-spread": "^6.22.0", "babel-preset-es2015": "^6.22.0", "babel-preset-react": "^6.22.0", @@ -55,8 +58,8 @@ "react": "15.5.4", "react-dom": "15.5.4", "react-draggable": "2.2.6", - "react-modal": "2.2.1", "react-intl": "2.3.0", + "react-modal": "2.2.1", "react-redux": "5.0.5", "react-style-proptype": "3.0.0", "react-tabs": "1.1.0", From ea9cdfe5d5078c18fae172eea6450f1c4e1661d9 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Tue, 18 Jul 2017 09:51:28 -0400 Subject: [PATCH 0120/1971] Merge pull request #521 from chrisgarrity/gh510-combine-translations Gh509 combine translations --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 607b69edbd..87ec36bad2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -7,6 +7,7 @@ "build": "npm run clean && webpack --progress --colors --bail", "clean": "rimraf ./build && mkdirp build", "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", + "i18n:msgs": "./scripts/generate-locale-messages.js", "i18n:src": "babel src > tmp.js && rimraf tmp.js && ./scripts/build-i18n-source.js ./translations/messages/ ./translations/", "lint": "eslint . --ext .js,.jsx", "start": "webpack-dev-server", From fb0a1451962ca0c4cae311769f03fa8d70ec4b67 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Tue, 18 Jul 2017 10:07:26 -0400 Subject: [PATCH 0121/1971] Merge pull request #547 from chrisgarrity/integrate-intl-redux Integrate intl redux --- packages/scratch-gui/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 87ec36bad2..1a5d5ef542 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -4,7 +4,7 @@ "description": "GraphicaL User Interface for creating and running Scratch 3.0 projects", "main": "./src/index.js", "scripts": { - "build": "npm run clean && webpack --progress --colors --bail", + "build": "npm run clean && npm run i18n:msgs && webpack --progress --colors --bail", "clean": "rimraf ./build && mkdirp build", "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", "i18n:msgs": "./scripts/generate-locale-messages.js", @@ -60,6 +60,7 @@ "react-dom": "15.5.4", "react-draggable": "2.2.6", "react-intl": "2.3.0", + "react-intl-redux": "0.6.0", "react-modal": "2.2.1", "react-redux": "5.0.5", "react-style-proptype": "3.0.0", From 4a0b0e6e625ca82e811730e0eb14d296edcadefc Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 19 Jul 2017 13:12:58 -0400 Subject: [PATCH 0122/1971] Merge pull request #534 from LLK/greenkeeper/react-modal-2.2.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-modal to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1a5d5ef542..7739f1afac 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -61,7 +61,7 @@ "react-draggable": "2.2.6", "react-intl": "2.3.0", "react-intl-redux": "0.6.0", - "react-modal": "2.2.1", + "react-modal": "2.2.2", "react-redux": "5.0.5", "react-style-proptype": "3.0.0", "react-tabs": "1.1.0", From ba9448705fc868b0f55e3e5155fe91ad7bbd364d Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 20 Jul 2017 09:53:16 -0400 Subject: [PATCH 0123/1971] Merge pull request #551 from chrisgarrity/feature/gh511-language-selector Add language selection widget to menubar --- packages/scratch-gui/package.json | 3 ++- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7739f1afac..bb5cd437af 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -10,7 +10,7 @@ "i18n:msgs": "./scripts/generate-locale-messages.js", "i18n:src": "babel src > tmp.js && rimraf tmp.js && ./scripts/build-i18n-source.js ./translations/messages/ ./translations/", "lint": "eslint . --ext .js,.jsx", - "start": "webpack-dev-server", + "start": "npm run i18n:msgs && webpack-dev-server", "test": "npm run lint && npm run build", "watch": "webpack --progress --colors --watch" }, @@ -32,6 +32,7 @@ "babel-eslint": "^7.1.1", "babel-loader": "^7.0.0", "babel-plugin-react-intl": "2.3.1", + "babel-plugin-syntax-dynamic-import": "6.18.0", "babel-plugin-transform-object-rest-spread": "^6.22.0", "babel-preset-es2015": "^6.22.0", "babel-preset-react": "^6.22.0", diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index c22ba3636c..35ced70496 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -4,6 +4,7 @@ import React from 'react'; import Box from '../box/box.jsx'; import LoadButton from '../../containers/load-button.jsx'; import SaveButton from '../../containers/save-button.jsx'; +import LanguageSelector from '../../containers/language-selector.jsx'; import styles from './menu-bar.css'; import scratchLogo from './scratch-logo.svg'; @@ -23,6 +24,7 @@ const MenuBar = function MenuBar () { + ); }; From 4c720d0f65e6d98a14165cf6cd8a17ab3514293a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 21 Jul 2017 08:56:58 -0400 Subject: [PATCH 0124/1971] Merge pull request #452 from bogusred/feature/jest-setup Feature/jest setup --- packages/scratch-gui/package.json | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index bb5cd437af..4d41d6b2bd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -11,7 +11,8 @@ "i18n:src": "babel src > tmp.js && rimraf tmp.js && ./scripts/build-i18n-source.js ./translations/messages/ ./translations/", "lint": "eslint . --ext .js,.jsx", "start": "npm run i18n:msgs && webpack-dev-server", - "test": "npm run lint && npm run build", + "unit-test": "jest", + "test": "npm run lint && npm run unit-test && npm run build", "watch": "webpack --progress --colors --watch" }, "author": "Massachusetts Institute of Technology", @@ -39,12 +40,14 @@ "classnames": "2.2.5", "copy-webpack-plugin": "4.0.1", "css-loader": "0.28.3", + "enzyme": "^2.8.2", "eslint": "^3.16.1", "eslint-config-scratch": "^3.0.0", "eslint-plugin-react": "^7.0.1", "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", "html-webpack-plugin": "2.28.0", "immutable": "3.8.1", + "jest": "^20.0.4", "lodash.bindall": "4.4.0", "lodash.debounce": "4.0.8", "lodash.defaultsdeep": "4.6.0", @@ -65,6 +68,8 @@ "react-modal": "2.2.2", "react-redux": "5.0.5", "react-style-proptype": "3.0.0", + "react-test-renderer": "^15.5.4", + "redux-mock-store": "^1.2.3", "react-tabs": "1.1.0", "redux": "3.7.0", "redux-throttle": "0.1.1", @@ -81,5 +86,14 @@ "webpack": "^2.4.1", "webpack-dev-server": "^2.4.1", "xhr": "2.4.0" + }, + "jest": { + "testPathIgnorePatterns": [ + "src/test.js" + ], + "moduleNameMapper": { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/test/__mocks__/fileMock.js", + "\\.(css|less)$": "/test/__mocks__/styleMock.js" + } } } From 7993df7cd603add0902aa2402a78f4410070f47a Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 21 Jul 2017 09:30:48 -0400 Subject: [PATCH 0125/1971] Merge pull request #471 from LLK/greenkeeper/html-webpack-plugin-2.29.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update html-webpack-plugin to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4d41d6b2bd..e54a3428b3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -45,7 +45,7 @@ "eslint-config-scratch": "^3.0.0", "eslint-plugin-react": "^7.0.1", "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", - "html-webpack-plugin": "2.28.0", + "html-webpack-plugin": "2.29.0", "immutable": "3.8.1", "jest": "^20.0.4", "lodash.bindall": "4.4.0", From 49c66dba0d6de002583f1f53ea4ca05e8fd9a373 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 21 Jul 2017 09:43:23 -0400 Subject: [PATCH 0126/1971] Merge pull request #552 from paulkaplan/eslint-import Add import/export rules and fix lint errors --- packages/scratch-gui/package.json | 2 ++ packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 2 +- packages/scratch-gui/src/containers/blocks.jsx | 2 +- packages/scratch-gui/src/containers/stage.jsx | 2 +- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e54a3428b3..82f449bcf6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -42,7 +42,9 @@ "css-loader": "0.28.3", "enzyme": "^2.8.2", "eslint": "^3.16.1", + "eslint-config-import": "^0.13.0", "eslint-config-scratch": "^3.0.0", + "eslint-plugin-import": "^2.7.0", "eslint-plugin-react": "^7.0.1", "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", "html-webpack-plugin": "2.29.0", diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 35ced70496..661153f8db 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -29,4 +29,4 @@ const MenuBar = function MenuBar () { ); }; -module.exports = MenuBar; +export default MenuBar; diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index bc8e3ff384..c922b68855 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -263,4 +263,4 @@ Blocks.defaultProps = { options: Blocks.defaultOptions }; -module.exports = Blocks; +export default Blocks; diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 0c161023be..f0545c088d 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -195,4 +195,4 @@ Stage.propTypes = { vm: PropTypes.instanceOf(VM).isRequired }; -module.exports = Stage; +export default Stage; diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 8cbf05c504..e0f2bcb5da 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -113,4 +113,4 @@ const vmListenerHOC = function (WrappedComponent) { )(VMListener); }; -module.exports = vmListenerHOC; +export default vmListenerHOC; From 12e2de57388f8aced8919ed475487609e9ffb361 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 21 Jul 2017 10:07:54 -0400 Subject: [PATCH 0127/1971] Merge pull request #554 from paulkaplan/hotfix-reducer-import-errors Fix reducer imports --- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index e0f2bcb5da..081817c780 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -5,8 +5,8 @@ import VM from 'scratch-vm'; import {connect} from 'react-redux'; -import targets from '../reducers/targets'; -import monitors from '../reducers/monitors'; +import {updateEditingTarget, updateTargets} from '../reducers/targets'; +import {updateMonitors} from '../reducers/monitors'; /* * Higher Order Component to manage events emitted by the VM @@ -100,11 +100,11 @@ const vmListenerHOC = function (WrappedComponent) { }); const mapDispatchToProps = dispatch => ({ onTargetsUpdate: data => { - dispatch(targets.updateEditingTarget(data.editingTarget)); - dispatch(targets.updateTargets(data.targetList)); + dispatch(updateEditingTarget(data.editingTarget)); + dispatch(updateTargets(data.targetList)); }, onMonitorsUpdate: monitorList => { - dispatch(monitors.updateMonitors(monitorList)); + dispatch(updateMonitors(monitorList)); } }); return connect( From 7fee6ad62706ee7c6d49f5cae7deaa39c0bf49c1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 26 Jul 2017 16:29:59 -0400 Subject: [PATCH 0128/1971] Merge pull request #569 from paulkaplan/shrink-default-zoom Change default block zoom to 0.675 from 0.75 --- packages/scratch-gui/src/containers/blocks.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index c922b68855..86d9108973 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -237,7 +237,7 @@ Blocks.defaultOptions = { zoom: { controls: true, wheel: true, - startScale: 0.75 + startScale: 0.675 }, grid: { spacing: 40, From 3e5b610c7281a8d219239eb48627f40cdbe38288 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 31 Jul 2017 14:26:03 -0400 Subject: [PATCH 0129/1971] Merge pull request #575 from LLK/greenkeeper/html-webpack-plugin-2.30.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update html-webpack-plugin to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 82f449bcf6..9d1cf0ef1e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -47,7 +47,7 @@ "eslint-plugin-import": "^2.7.0", "eslint-plugin-react": "^7.0.1", "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", - "html-webpack-plugin": "2.29.0", + "html-webpack-plugin": "2.30.0", "immutable": "3.8.1", "jest": "^20.0.4", "lodash.bindall": "4.4.0", From 3d90d7ffde3288e2096c7f88c9798d4621711340 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Sat, 5 Aug 2017 11:23:12 -0400 Subject: [PATCH 0130/1971] Merge pull request #583 from paulkaplan/polyfill-analyser Add web-audio polyfill for `analyserNode.getFloatTimeDomainData` --- packages/scratch-gui/package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9d1cf0ef1e..42908e12ef 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -46,6 +46,7 @@ "eslint-config-scratch": "^3.0.0", "eslint-plugin-import": "^2.7.0", "eslint-plugin-react": "^7.0.1", + "get-float-time-domain-data": "0.1.0", "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", "html-webpack-plugin": "2.30.0", "immutable": "3.8.1", @@ -70,10 +71,10 @@ "react-modal": "2.2.2", "react-redux": "5.0.5", "react-style-proptype": "3.0.0", - "react-test-renderer": "^15.5.4", - "redux-mock-store": "^1.2.3", "react-tabs": "1.1.0", + "react-test-renderer": "^15.5.4", "redux": "3.7.0", + "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "latest", From add08f6b78ba9a453f2d1e4e33eedf8a6b34b2ba Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 9 Aug 2017 10:14:45 -0400 Subject: [PATCH 0131/1971] Merge pull request #592 from LLK/greenkeeper/react-redux-5.0.6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-redux to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 42908e12ef..cba8749f3a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -69,7 +69,7 @@ "react-intl": "2.3.0", "react-intl-redux": "0.6.0", "react-modal": "2.2.2", - "react-redux": "5.0.5", + "react-redux": "5.0.6", "react-style-proptype": "3.0.0", "react-tabs": "1.1.0", "react-test-renderer": "^15.5.4", From 684117f9b3d4443e66dde525224485f853c44f96 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 10 Aug 2017 12:27:05 -0400 Subject: [PATCH 0132/1971] Merge pull request #593 from LLK/greenkeeper/wav-encoder-1.2.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update wav-encoder to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cba8749f3a..826c6cb176 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -85,7 +85,7 @@ "style-loader": "^0.18.0", "svg-to-image": "1.1.3", "svg-url-loader": "2.1.0", - "wav-encoder": "1.1.0", + "wav-encoder": "1.2.0", "webpack": "^2.4.1", "webpack-dev-server": "^2.4.1", "xhr": "2.4.0" From bec1eea0845523c5e91e2af22021ce6c3dbb136f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 10 Aug 2017 16:43:32 -0400 Subject: [PATCH 0133/1971] Merge pull request #606 from paulkaplan/fix-small-layout Allow GUI to fit into 1024px screen widths --- packages/scratch-gui/package.json | 1 + packages/scratch-gui/src/containers/stage.jsx | 14 +++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 826c6cb176..59b0e3a14a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -70,6 +70,7 @@ "react-intl-redux": "0.6.0", "react-modal": "2.2.2", "react-redux": "5.0.6", + "react-responsive": "1.3.4", "react-style-proptype": "3.0.0", "react-tabs": "1.1.0", "react-test-renderer": "^15.5.4", diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index f0545c088d..d9ab05f129 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -40,8 +40,8 @@ class Stage extends React.Component { this.audioEngine = new AudioEngine(); this.props.vm.attachAudioEngine(this.audioEngine); } - shouldComponentUpdate () { - return false; + shouldComponentUpdate (nextProps) { + return this.props.width !== nextProps.width || this.props.height !== nextProps.height; } componentWillUnmount () { this.detachMouseEvents(this.canvas); @@ -69,9 +69,10 @@ class Stage extends React.Component { this.rect = this.canvas.getBoundingClientRect(); } getScratchCoords (x, y) { + const nativeSize = this.renderer.getNativeSize(); return [ - x - (this.rect.width / 2), - y - (this.rect.height / 2) + (nativeSize[0] / this.rect.width) * (x - (this.rect.width / 2)), + (nativeSize[1] / this.rect.height) * (y - (this.rect.height / 2)) ]; } handleDoubleClick (e) { @@ -127,6 +128,7 @@ class Stage extends React.Component { } } onMouseDown (e) { + this.updateRect(); const mousePosition = [e.clientX - this.rect.left, e.clientY - this.rect.top]; this.setState({ mouseDown: true, @@ -192,7 +194,9 @@ class Stage extends React.Component { } Stage.propTypes = { - vm: PropTypes.instanceOf(VM).isRequired + height: PropTypes.number, + vm: PropTypes.instanceOf(VM).isRequired, + width: PropTypes.number }; export default Stage; From 1f6e6d9f0603ce1714decc20fd2404f03a86714c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 11 Aug 2017 08:47:42 -0400 Subject: [PATCH 0134/1971] Merge pull request #608 from LLK/greenkeeper/react-modal-2.2.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-modal to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 59b0e3a14a..e51c1bcbc1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -68,7 +68,7 @@ "react-draggable": "2.2.6", "react-intl": "2.3.0", "react-intl-redux": "0.6.0", - "react-modal": "2.2.2", + "react-modal": "2.2.3", "react-redux": "5.0.6", "react-responsive": "1.3.4", "react-style-proptype": "3.0.0", From 817cf19d1646108a4082c5bd6c4a978126cf7739 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 11 Aug 2017 10:27:49 -0400 Subject: [PATCH 0135/1971] Merge pull request #584 from paulkaplan/selenium-with-jest Add end-to-end testing using selenium-webdriver --- packages/scratch-gui/package.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e51c1bcbc1..bad718d7ee 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -11,8 +11,9 @@ "i18n:src": "babel src > tmp.js && rimraf tmp.js && ./scripts/build-i18n-source.js ./translations/messages/ ./translations/", "lint": "eslint . --ext .js,.jsx", "start": "npm run i18n:msgs && webpack-dev-server", - "unit-test": "jest", - "test": "npm run lint && npm run unit-test && npm run build", + "unit-test": "jest test/unit", + "integration-test": "npm run build && jest test/integration", + "test": "npm run lint && npm run unit-test && npm run integration-test", "watch": "webpack --progress --colors --watch" }, "author": "Massachusetts Institute of Technology", @@ -34,9 +35,11 @@ "babel-loader": "^7.0.0", "babel-plugin-react-intl": "2.3.1", "babel-plugin-syntax-dynamic-import": "6.18.0", + "babel-plugin-transform-async-to-generator": "^6.24.1", "babel-plugin-transform-object-rest-spread": "^6.22.0", "babel-preset-es2015": "^6.22.0", "babel-preset-react": "^6.22.0", + "chromedriver": "^2.31.0", "classnames": "2.2.5", "copy-webpack-plugin": "4.0.1", "css-loader": "0.28.3", @@ -83,6 +86,7 @@ "scratch-render": "latest", "scratch-storage": "^0.2.0", "scratch-vm": "latest", + "selenium-webdriver": "^3.5.0", "style-loader": "^0.18.0", "svg-to-image": "1.1.3", "svg-url-loader": "2.1.0", From 0a9b24e63b90063bee165207016735e0c4fab1f0 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 11 Aug 2017 10:36:31 -0400 Subject: [PATCH 0136/1971] Merge pull request #588 from paulkaplan/sound-editor--effects-backend Sound editor effects backend --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index bad718d7ee..9f7b08cf6f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,6 +91,7 @@ "svg-to-image": "1.1.3", "svg-url-loader": "2.1.0", "wav-encoder": "1.2.0", + "web-audio-test-api": "^0.5.2", "webpack": "^2.4.1", "webpack-dev-server": "^2.4.1", "xhr": "2.4.0" From a8b59a007710f65db6769e9119b2ed164b2ab146 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 14 Aug 2017 08:31:13 -0400 Subject: [PATCH 0137/1971] Merge pull request #610 from LLK/greenkeeper/wav-encoder-1.3.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update wav-encoder to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9f7b08cf6f..f2cdc6e7e3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -90,7 +90,7 @@ "style-loader": "^0.18.0", "svg-to-image": "1.1.3", "svg-url-loader": "2.1.0", - "wav-encoder": "1.2.0", + "wav-encoder": "1.3.0", "web-audio-test-api": "^0.5.2", "webpack": "^2.4.1", "webpack-dev-server": "^2.4.1", From d27d65373b89f93db662c3cfaccf70a4924593ce Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 14 Aug 2017 09:00:36 -0400 Subject: [PATCH 0138/1971] Merge pull request #613 from LLK/greenkeeper/react-modal-2.2.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-modal to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f2cdc6e7e3..4f5fcf2df0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -71,7 +71,7 @@ "react-draggable": "2.2.6", "react-intl": "2.3.0", "react-intl-redux": "0.6.0", - "react-modal": "2.2.3", + "react-modal": "2.2.4", "react-redux": "5.0.6", "react-responsive": "1.3.4", "react-style-proptype": "3.0.0", From 8d442c043564119eb2854f849b7ce00ae49671ae Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 14 Aug 2017 13:41:38 -0400 Subject: [PATCH 0139/1971] Merge pull request #614 from paulkaplan/deps/update-webpack Update webpack and related packages --- packages/scratch-gui/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4f5fcf2df0..38ba8fbbeb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -32,7 +32,7 @@ "babel-cli": "6.24.1", "babel-core": "^6.23.1", "babel-eslint": "^7.1.1", - "babel-loader": "^7.0.0", + "babel-loader": "^7.1.0", "babel-plugin-react-intl": "2.3.1", "babel-plugin-syntax-dynamic-import": "6.18.0", "babel-plugin-transform-async-to-generator": "^6.24.1", @@ -92,8 +92,8 @@ "svg-url-loader": "2.1.0", "wav-encoder": "1.3.0", "web-audio-test-api": "^0.5.2", - "webpack": "^2.4.1", - "webpack-dev-server": "^2.4.1", + "webpack": "^3.5.4", + "webpack-dev-server": "^2.7.0", "xhr": "2.4.0" }, "jest": { From ec49eb05a4564a0e17d7a3c4ca25c63a6782a611 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 14 Aug 2017 13:49:18 -0400 Subject: [PATCH 0140/1971] Merge pull request #605 from paulkaplan/context-menu Context menu --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 38ba8fbbeb..e80b880a03 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -67,6 +67,7 @@ "postcss-simple-vars": "^4.0.0", "prop-types": "^15.5.10", "react": "15.5.4", + "react-contextmenu": "2.6.5", "react-dom": "15.5.4", "react-draggable": "2.2.6", "react-intl": "2.3.0", From c9075c6734f2b2f765ce5dca9b8fc5f15b9206bd Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 16 Aug 2017 11:41:34 -0400 Subject: [PATCH 0141/1971] Merge pull request #620 from paulkaplan/update-eslint Update eslint packages and fix indentations --- packages/scratch-gui/package.json | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e80b880a03..fb0f8d8b06 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -31,7 +31,7 @@ "autoprefixer": "7.1.2", "babel-cli": "6.24.1", "babel-core": "^6.23.1", - "babel-eslint": "^7.1.1", + "babel-eslint": "^7.2.3", "babel-loader": "^7.1.0", "babel-plugin-react-intl": "2.3.1", "babel-plugin-syntax-dynamic-import": "6.18.0", @@ -44,11 +44,10 @@ "copy-webpack-plugin": "4.0.1", "css-loader": "0.28.3", "enzyme": "^2.8.2", - "eslint": "^3.16.1", - "eslint-config-import": "^0.13.0", - "eslint-config-scratch": "^3.0.0", + "eslint": "^4.4.1", + "eslint-config-scratch": "^4.0.0", "eslint-plugin-import": "^2.7.0", - "eslint-plugin-react": "^7.0.1", + "eslint-plugin-react": "^7.2.1", "get-float-time-domain-data": "0.1.0", "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", "html-webpack-plugin": "2.30.0", From 95892c448f3672dcbb36ecff14f0ab708a01931d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 17 Aug 2017 08:58:32 -0400 Subject: [PATCH 0142/1971] Merge pull request #628 from LLK/greenkeeper/babel-cli-6.26.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update babel-cli to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index fb0f8d8b06..ca56d3a620 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -29,7 +29,7 @@ }, "devDependencies": { "autoprefixer": "7.1.2", - "babel-cli": "6.24.1", + "babel-cli": "6.26.0", "babel-core": "^6.23.1", "babel-eslint": "^7.2.3", "babel-loader": "^7.1.0", From 50b03d87317b03020dfd473e650dc6fc4ace9522 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 17 Aug 2017 09:03:15 -0400 Subject: [PATCH 0143/1971] Merge pull request #631 from LLK/greenkeeper/css-loader-0.28.5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update css-loader to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ca56d3a620..531535e8e9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -42,7 +42,7 @@ "chromedriver": "^2.31.0", "classnames": "2.2.5", "copy-webpack-plugin": "4.0.1", - "css-loader": "0.28.3", + "css-loader": "0.28.5", "enzyme": "^2.8.2", "eslint": "^4.4.1", "eslint-config-scratch": "^4.0.0", From bafd4672e3cb2be67931bfd7910b6749e3c1fc95 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 17 Aug 2017 11:51:21 -0400 Subject: [PATCH 0144/1971] Merge pull request #624 from paulkaplan/update-react Update all react deps to most recent version --- packages/scratch-gui/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 531535e8e9..d615d01ba6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -24,8 +24,8 @@ "url": "git+ssh://git@github.com/LLK/scratch-gui.git" }, "peerDependencies": { - "react": "^15.6.0", - "react-dom": "^15.6.0" + "react": "^15.6.1", + "react-dom": "^15.6.1" }, "devDependencies": { "autoprefixer": "7.1.2", @@ -65,9 +65,9 @@ "postcss-loader": "^2.0.5", "postcss-simple-vars": "^4.0.0", "prop-types": "^15.5.10", - "react": "15.5.4", + "react": "15.6.1", "react-contextmenu": "2.6.5", - "react-dom": "15.5.4", + "react-dom": "15.6.1", "react-draggable": "2.2.6", "react-intl": "2.3.0", "react-intl-redux": "0.6.0", From 9198f4784dddbb471bcfef673c565170bf2bb7e7 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 17 Aug 2017 13:58:35 -0400 Subject: [PATCH 0145/1971] Merge pull request #627 from paulkaplan/touch-events Enable touch events for the stage and audio trimmers --- packages/scratch-gui/src/containers/stage.jsx | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index d9ab05f129..d4e3de5d3e 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -4,6 +4,7 @@ import React from 'react'; import Renderer from 'scratch-render'; import AudioEngine from 'scratch-audio'; import VM from 'scratch-vm'; +import {getEventXY} from '../lib/touch-utils'; import StageComponent from '../components/stage/stage.jsx'; @@ -50,12 +51,18 @@ class Stage extends React.Component { attachMouseEvents (canvas) { document.addEventListener('mousemove', this.onMouseMove); document.addEventListener('mouseup', this.onMouseUp); + document.addEventListener('touchmove', this.onMouseMove); + document.addEventListener('touchend', this.onMouseUp); canvas.addEventListener('mousedown', this.onMouseDown); + canvas.addEventListener('touchstart', this.onMouseDown); } detachMouseEvents (canvas) { document.removeEventListener('mousemove', this.onMouseMove); document.removeEventListener('mouseup', this.onMouseUp); + document.removeEventListener('touchmove', this.onMouseMove); + document.removeEventListener('touchend', this.onMouseUp); canvas.removeEventListener('mousedown', this.onMouseDown); + canvas.removeEventListener('touchstart', this.onMouseDown); } attachRectEvents () { window.addEventListener('resize', this.updateRect); @@ -76,8 +83,9 @@ class Stage extends React.Component { ]; } handleDoubleClick (e) { + const {x, y} = getEventXY(e); // Set editing target from cursor position, if clicking on a sprite. - const mousePosition = [e.clientX - this.rect.left, e.clientY - this.rect.top]; + const mousePosition = [x - this.rect.left, y - this.rect.top]; const drawableId = this.renderer.pick(mousePosition[0], mousePosition[1]); if (drawableId === null) return; const targetId = this.props.vm.getTargetIdForDrawableId(drawableId); @@ -85,7 +93,8 @@ class Stage extends React.Component { this.props.vm.setEditingTarget(targetId); } onMouseMove (e) { - const mousePosition = [e.clientX - this.rect.left, e.clientY - this.rect.top]; + const {x, y} = getEventXY(e); + const mousePosition = [x - this.rect.left, y - this.rect.top]; if (this.state.mouseDownTimeoutId !== null) { this.cancelMouseDownTimeout(); if (this.state.mouseDown && !this.state.isDragging) { @@ -109,6 +118,7 @@ class Stage extends React.Component { this.props.vm.postIOData('mouse', coordinates); } onMouseUp (e) { + const {x, y} = getEventXY(e); this.cancelMouseDownTimeout(); this.setState({ mouseDown: false, @@ -119,8 +129,8 @@ class Stage extends React.Component { } else { const data = { isDown: false, - x: e.clientX - this.rect.left, - y: e.clientY - this.rect.top, + x: x - this.rect.left, + y: y - this.rect.top, canvasWidth: this.rect.width, canvasHeight: this.rect.height }; @@ -129,7 +139,8 @@ class Stage extends React.Component { } onMouseDown (e) { this.updateRect(); - const mousePosition = [e.clientX - this.rect.left, e.clientY - this.rect.top]; + const {x, y} = getEventXY(e); + const mousePosition = [x - this.rect.left, y - this.rect.top]; this.setState({ mouseDown: true, mouseDownPosition: mousePosition, @@ -146,7 +157,9 @@ class Stage extends React.Component { canvasHeight: this.rect.height }; this.props.vm.postIOData('mouse', data); - e.preventDefault(); + if (e.preventDefault) { + e.preventDefault(); + } } cancelMouseDownTimeout () { if (this.state.mouseDownTimeoutId !== null) { From 4e6d80e1b982a0a00e443d9cb42f4111e41b04d5 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 17 Aug 2017 15:16:17 -0400 Subject: [PATCH 0146/1971] Merge pull request #625 from rschamp/library-examples Add GUI-as-library examples --- packages/scratch-gui/src/containers/blocks.jsx | 3 ++- packages/scratch-gui/src/containers/stage.jsx | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 86d9108973..63a7627658 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -208,7 +208,7 @@ class Blocks extends React.Component { } Blocks.propTypes = { - isVisible: PropTypes.bool.isRequired, + isVisible: PropTypes.bool, options: PropTypes.shape({ media: PropTypes.string, zoom: PropTypes.shape({ @@ -260,6 +260,7 @@ Blocks.defaultOptions = { }; Blocks.defaultProps = { + isVisible: true, options: Blocks.defaultOptions }; diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index d4e3de5d3e..1b5b2ec9cc 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -2,7 +2,6 @@ import bindAll from 'lodash.bindall'; import PropTypes from 'prop-types'; import React from 'react'; import Renderer from 'scratch-render'; -import AudioEngine from 'scratch-audio'; import VM from 'scratch-vm'; import {getEventXY} from '../lib/touch-utils'; @@ -38,8 +37,6 @@ class Stage extends React.Component { this.updateRect(); this.renderer = new Renderer(this.canvas); this.props.vm.attachRenderer(this.renderer); - this.audioEngine = new AudioEngine(); - this.props.vm.attachAudioEngine(this.audioEngine); } shouldComponentUpdate (nextProps) { return this.props.width !== nextProps.width || this.props.height !== nextProps.height; From ce0337a1971aea361e7322e05f7513b763fae9a8 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 18 Aug 2017 15:36:06 -0400 Subject: [PATCH 0147/1971] Merge pull request #618 from paulkaplan/safari-audio Safari audio --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d615d01ba6..d293d00de0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -87,6 +87,7 @@ "scratch-storage": "^0.2.0", "scratch-vm": "latest", "selenium-webdriver": "^3.5.0", + "startaudiocontext": "1.2.1", "style-loader": "^0.18.0", "svg-to-image": "1.1.3", "svg-url-loader": "2.1.0", From 7a866fe96f0d3631c1d055d4f64d378fe4a6caf9 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Mon, 21 Aug 2017 12:15:42 -0400 Subject: [PATCH 0148/1971] Merge pull request #636 from chrisgarrity/bug/gh630-script Script update for windows - fixes #630 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d293d00de0..ce1f5108b4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -7,7 +7,7 @@ "build": "npm run clean && npm run i18n:msgs && webpack --progress --colors --bail", "clean": "rimraf ./build && mkdirp build", "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", - "i18n:msgs": "./scripts/generate-locale-messages.js", + "i18n:msgs": "node ./scripts/generate-locale-messages.js", "i18n:src": "babel src > tmp.js && rimraf tmp.js && ./scripts/build-i18n-source.js ./translations/messages/ ./translations/", "lint": "eslint . --ext .js,.jsx", "start": "npm run i18n:msgs && webpack-dev-server", From ecbb2446f7062afb800923f9cc9a45ce90be4caa Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 24 Aug 2017 17:20:22 -0400 Subject: [PATCH 0149/1971] Merge pull request #645 from LLK/greenkeeper/react-draggable-3.0.2 chore(package): update react-draggable to version 3.0.2 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ce1f5108b4..19b2bfc040 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -68,7 +68,7 @@ "react": "15.6.1", "react-contextmenu": "2.6.5", "react-dom": "15.6.1", - "react-draggable": "2.2.6", + "react-draggable": "3.0.2", "react-intl": "2.3.0", "react-intl-redux": "0.6.0", "react-modal": "2.2.4", From 7d7c6ca0af75daa0acb836e70242f8e5e63c0a84 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 28 Aug 2017 17:21:12 -0400 Subject: [PATCH 0150/1971] Merge pull request #648 from LLK/greenkeeper/autoprefixer-7.1.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update autoprefixer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 19b2bfc040..6a91be254f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -28,7 +28,7 @@ "react-dom": "^15.6.1" }, "devDependencies": { - "autoprefixer": "7.1.2", + "autoprefixer": "7.1.3", "babel-cli": "6.26.0", "babel-core": "^6.23.1", "babel-eslint": "^7.2.3", From e507b4c1ee2a335336cff051c4e8c0fd0ce4d574 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 31 Aug 2017 10:04:36 -0400 Subject: [PATCH 0151/1971] Merge pull request #660 from LLK/greenkeeper/css-loader-0.28.7 chore(package): update css-loader to version 0.28.7 --- packages/scratch-gui/package.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6a91be254f..5038c0ba86 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -28,21 +28,21 @@ "react-dom": "^15.6.1" }, "devDependencies": { - "autoprefixer": "7.1.3", - "babel-cli": "6.26.0", + "autoprefixer": "^7.1.3", + "babel-cli": "^6.26.0", "babel-core": "^6.23.1", "babel-eslint": "^7.2.3", "babel-loader": "^7.1.0", - "babel-plugin-react-intl": "2.3.1", - "babel-plugin-syntax-dynamic-import": "6.18.0", + "babel-plugin-react-intl": "^2.3.1", + "babel-plugin-syntax-dynamic-import": "^6.18.0", "babel-plugin-transform-async-to-generator": "^6.24.1", "babel-plugin-transform-object-rest-spread": "^6.22.0", "babel-preset-es2015": "^6.22.0", "babel-preset-react": "^6.22.0", "chromedriver": "^2.31.0", "classnames": "2.2.5", - "copy-webpack-plugin": "4.0.1", - "css-loader": "0.28.5", + "copy-webpack-plugin": "^4.0.1", + "css-loader": "^0.28.7", "enzyme": "^2.8.2", "eslint": "^4.4.1", "eslint-config-scratch": "^4.0.0", @@ -50,7 +50,7 @@ "eslint-plugin-react": "^7.2.1", "get-float-time-domain-data": "0.1.0", "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", - "html-webpack-plugin": "2.30.0", + "html-webpack-plugin": "^2.30.0", "immutable": "3.8.1", "jest": "^20.0.4", "lodash.bindall": "4.4.0", @@ -90,7 +90,7 @@ "startaudiocontext": "1.2.1", "style-loader": "^0.18.0", "svg-to-image": "1.1.3", - "svg-url-loader": "2.1.0", + "svg-url-loader": "^2.1.0", "wav-encoder": "1.3.0", "web-audio-test-api": "^0.5.2", "webpack": "^3.5.4", From 11fe66325d744a74ac53295493d2870b85b5132a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 6 Sep 2017 16:28:29 -0400 Subject: [PATCH 0152/1971] Merge pull request #668 from LLK/greenkeeper/react-modal-2.3.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-modal to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5038c0ba86..c170d2e1cd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -71,7 +71,7 @@ "react-draggable": "3.0.2", "react-intl": "2.3.0", "react-intl-redux": "0.6.0", - "react-modal": "2.2.4", + "react-modal": "2.3.1", "react-redux": "5.0.6", "react-responsive": "1.3.4", "react-style-proptype": "3.0.0", From 8a99b67db0f775872ed184e51d193ee91c75439e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 6 Sep 2017 16:29:26 -0400 Subject: [PATCH 0153/1971] Merge pull request #670 from LLK/greenkeeper/react-tabs-2.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-tabs to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c170d2e1cd..7cec954f66 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -75,7 +75,7 @@ "react-redux": "5.0.6", "react-responsive": "1.3.4", "react-style-proptype": "3.0.0", - "react-tabs": "1.1.0", + "react-tabs": "2.0.0", "react-test-renderer": "^15.5.4", "redux": "3.7.0", "redux-mock-store": "^1.2.3", From b6366e94377a7954c452afb5ddea05d5a9756f18 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 6 Sep 2017 16:30:04 -0400 Subject: [PATCH 0154/1971] Merge pull request #671 from LLK/greenkeeper/react-intl-2.4.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-intl to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7cec954f66..ae13053c00 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -69,7 +69,7 @@ "react-contextmenu": "2.6.5", "react-dom": "15.6.1", "react-draggable": "3.0.2", - "react-intl": "2.3.0", + "react-intl": "2.4.0", "react-intl-redux": "0.6.0", "react-modal": "2.3.1", "react-redux": "5.0.6", From 3d334b231fbebe88586700e603ce5cedd9a70e3f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 6 Sep 2017 16:30:55 -0400 Subject: [PATCH 0155/1971] Merge pull request #666 from LLK/greenkeeper/jest-21.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update jest to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ae13053c00..5b79ddc959 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -52,7 +52,7 @@ "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", "html-webpack-plugin": "^2.30.0", "immutable": "3.8.1", - "jest": "^20.0.4", + "jest": "^21.0.0", "lodash.bindall": "4.4.0", "lodash.debounce": "4.0.8", "lodash.defaultsdeep": "4.6.0", From ef3801d5d01ea2f8db32a08e42253a06db7b036a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 7 Sep 2017 10:38:00 -0400 Subject: [PATCH 0156/1971] Merge pull request #661 from LLK/greenkeeper/react-draggable-3.0.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-draggable to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5b79ddc959..eba4aacc8f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -68,7 +68,7 @@ "react": "15.6.1", "react-contextmenu": "2.6.5", "react-dom": "15.6.1", - "react-draggable": "3.0.2", + "react-draggable": "3.0.3", "react-intl": "2.4.0", "react-intl-redux": "0.6.0", "react-modal": "2.3.1", From 923af8bdc32445b85359c49399628b085c3e6bb6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 18 Sep 2017 12:46:32 -0400 Subject: [PATCH 0157/1971] Merge pull request #688 from paulkaplan/fix-travis Fix travis problems --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index eba4aacc8f..32d692ed3c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -12,7 +12,7 @@ "lint": "eslint . --ext .js,.jsx", "start": "npm run i18n:msgs && webpack-dev-server", "unit-test": "jest test/unit", - "integration-test": "npm run build && jest test/integration", + "integration-test": "npm run build && jest --runInBand test/integration", "test": "npm run lint && npm run unit-test && npm run integration-test", "watch": "webpack --progress --colors --watch" }, From c1736340c4911aa8fbec81407bcd4a6914d0ffec Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Mon, 18 Sep 2017 11:03:33 -0700 Subject: [PATCH 0158/1971] Merge pull request #655 from cwillisf/feature/extensions Feature/extensions --- .../scratch-gui/src/containers/blocks.jsx | 59 ++++++++++++++++++- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 63a7627658..fbd4ecb6d6 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -1,6 +1,7 @@ import bindAll from 'lodash.bindall'; import debounce from 'lodash.debounce'; import defaultsDeep from 'lodash.defaultsdeep'; +import makeToolboxXML from '../lib/make-toolbox-xml'; import PropTypes from 'prop-types'; import React from 'react'; import VMScratchBlocks from '../lib/blocks'; @@ -8,6 +9,9 @@ import VM from 'scratch-vm'; import Prompt from './prompt.jsx'; import BlocksComponent from '../components/blocks/blocks.jsx'; +import {connect} from 'react-redux'; +import {updateToolbox} from '../reducers/toolbox'; + const addFunctionListener = (object, property, callback) => { const oldFn = object[property]; object[property] = function () { @@ -31,6 +35,7 @@ class Blocks extends React.Component { 'onScriptGlowOff', 'onBlockGlowOn', 'onBlockGlowOff', + 'handleExtensionAdded', 'onTargetsUpdate', 'onVisualReport', 'onWorkspaceUpdate', @@ -55,9 +60,20 @@ class Blocks extends React.Component { this.attachVM(); } shouldComponentUpdate (nextProps, nextState) { - return this.state.prompt !== nextState.prompt || this.props.isVisible !== nextProps.isVisible; + return ( + this.state.prompt !== nextState.prompt || + this.props.isVisible !== nextProps.isVisible || + this.props.toolboxXML !== nextProps.toolboxXML + ); } componentDidUpdate (prevProps) { + if (prevProps.toolboxXML !== this.props.toolboxXML) { + const selectedCategoryName = this.workspace.toolbox_.getSelectedItem().name_; + this.workspace.updateToolbox(this.props.toolboxXML); + // Blockly throws if we don't select a category after updating the toolbox. + /** @TODO Find a way to avoid the exception without accessing private properties. */ + this.setToolboxSelectedItemByName(selectedCategoryName); + } if (this.props.isVisible === prevProps.isVisible) { return; } @@ -77,6 +93,20 @@ class Blocks extends React.Component { this.detachVM(); this.workspace.dispose(); } + /** + * Select a particular category in the toolbox by specifying the category name. + * This is a workaround for a bug: @see {@link componentDidUpdate} above. + * @TODO Remove this or reimplement using only public APIs. + * @param {string} name - the name of the category to select. + */ + setToolboxSelectedItemByName (name) { + const categories = this.workspace.toolbox_.categoryMenu_.categories_; + for (let i = 0; i < categories.length; i++) { + if (categories[i].name_ === name) { + this.workspace.toolbox_.setSelectedItem(categories[i]); + } + } + } attachVM () { this.workspace.addChangeListener(this.props.vm.blockListener); this.flyoutWorkspace = this.workspace @@ -91,6 +121,7 @@ class Blocks extends React.Component { this.props.vm.addListener('VISUAL_REPORT', this.onVisualReport); this.props.vm.addListener('workspaceUpdate', this.onWorkspaceUpdate); this.props.vm.addListener('targetsUpdate', this.onTargetsUpdate); + this.props.vm.addListener('EXTENSION_ADDED', this.handleExtensionAdded); } detachVM () { this.props.vm.removeListener('SCRIPT_GLOW_ON', this.onScriptGlowOn); @@ -100,6 +131,7 @@ class Blocks extends React.Component { this.props.vm.removeListener('VISUAL_REPORT', this.onVisualReport); this.props.vm.removeListener('workspaceUpdate', this.onWorkspaceUpdate); this.props.vm.removeListener('targetsUpdate', this.onTargetsUpdate); + this.props.vm.removeListener('EXTENSION_ADDED', this.handleExtensionAdded); } updateToolboxBlockValue (id, value) { const block = this.workspace @@ -167,6 +199,12 @@ class Blocks extends React.Component { this.workspace.resize(); } } + handleExtensionAdded (blocksInfo) { + this.ScratchBlocks.defineBlocksWithJsonArray(blocksInfo.map(blockInfo => blockInfo.json)); + const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(); + const toolboxXML = makeToolboxXML(dynamicBlocksXML); + this.props.onExtensionAdded(toolboxXML); + } setBlocks (blocks) { this.blocks = blocks; } @@ -185,6 +223,8 @@ class Blocks extends React.Component { options, // eslint-disable-line no-unused-vars vm, // eslint-disable-line no-unused-vars isVisible, // eslint-disable-line no-unused-vars + onExtensionAdded, // eslint-disable-line no-unused-vars + toolboxXML, // eslint-disable-line no-unused-vars ...props } = this.props; return ( @@ -209,6 +249,7 @@ class Blocks extends React.Component { Blocks.propTypes = { isVisible: PropTypes.bool, + onExtensionAdded: PropTypes.func, options: PropTypes.shape({ media: PropTypes.string, zoom: PropTypes.shape({ @@ -230,6 +271,7 @@ Blocks.propTypes = { }), comments: PropTypes.bool }), + toolboxXML: PropTypes.string, vm: PropTypes.instanceOf(VM).isRequired }; @@ -264,4 +306,17 @@ Blocks.defaultProps = { options: Blocks.defaultOptions }; -export default Blocks; +const mapStateToProps = state => ({ + toolboxXML: state.toolbox.toolboxXML +}); + +const mapDispatchToProps = dispatch => ({ + onExtensionAdded: toolboxXML => { + dispatch(updateToolbox(toolboxXML)); + } +}); + +export default connect( + mapStateToProps, + mapDispatchToProps +)(Blocks); From 3fc8d8b0d191016fac2fa1612ba18d16bc9d2e31 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 19 Sep 2017 09:46:56 -0400 Subject: [PATCH 0159/1971] Merge pull request #678 from LLK/greenkeeper/react-contextmenu-2.7.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-contextmenu to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 32d692ed3c..298217f51a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -66,7 +66,7 @@ "postcss-simple-vars": "^4.0.0", "prop-types": "^15.5.10", "react": "15.6.1", - "react-contextmenu": "2.6.5", + "react-contextmenu": "2.7.0", "react-dom": "15.6.1", "react-draggable": "3.0.3", "react-intl": "2.4.0", From cd3840f4c1fd2b76d382b1ec5b8faf90935194cf Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 20 Sep 2017 14:37:27 -0400 Subject: [PATCH 0160/1971] Merge pull request #693 from paulkaplan/update-webpack-3-6-0 Update webpack to 3.6.0 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 298217f51a..dd5a73033b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -93,7 +93,7 @@ "svg-url-loader": "^2.1.0", "wav-encoder": "1.3.0", "web-audio-test-api": "^0.5.2", - "webpack": "^3.5.4", + "webpack": "^3.6.0", "webpack-dev-server": "^2.7.0", "xhr": "2.4.0" }, From 0d3693dbc9bbc1b4f32f7d56b452b07c10eee8b6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 20 Sep 2017 14:41:40 -0400 Subject: [PATCH 0161/1971] Merge pull request #694 from paulkaplan/update-eslint-4-7-1 Update to eslint 4.7.1 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index dd5a73033b..4d4dc452a8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -44,7 +44,7 @@ "copy-webpack-plugin": "^4.0.1", "css-loader": "^0.28.7", "enzyme": "^2.8.2", - "eslint": "^4.4.1", + "eslint": "^4.7.1", "eslint-config-scratch": "^4.0.0", "eslint-plugin-import": "^2.7.0", "eslint-plugin-react": "^7.2.1", From db7578a0f301a491874c307c7e15566d25bd69d9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 21 Sep 2017 09:40:56 -0400 Subject: [PATCH 0162/1971] Merge pull request #697 from LLK/greenkeeper/webpack-dev-server-2.8.2 chore(package): update webpack-dev-server to version 2.8.2 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4d4dc452a8..e5619adadc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "wav-encoder": "1.3.0", "web-audio-test-api": "^0.5.2", "webpack": "^3.6.0", - "webpack-dev-server": "^2.7.0", + "webpack-dev-server": "^2.8.2", "xhr": "2.4.0" }, "jest": { From 492b0a701b9b68503f2e8e594377e7be52378055 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 21 Sep 2017 09:44:23 -0400 Subject: [PATCH 0163/1971] Merge pull request #698 from paulkaplan/update-chromedriver Update chromedriver --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e5619adadc..188c8e4b31 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -39,7 +39,7 @@ "babel-plugin-transform-object-rest-spread": "^6.22.0", "babel-preset-es2015": "^6.22.0", "babel-preset-react": "^6.22.0", - "chromedriver": "^2.31.0", + "chromedriver": "^2.32.3", "classnames": "2.2.5", "copy-webpack-plugin": "^4.0.1", "css-loader": "^0.28.7", From ab80b98e455340318f0f6fea97fd15f13f961dd5 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 25 Sep 2017 14:07:31 -0400 Subject: [PATCH 0164/1971] Merge pull request #692 from paulkaplan/eyedropper Eyedropper color picking tool --- .../scratch-gui/src/containers/blocks.jsx | 18 +++-- packages/scratch-gui/src/containers/stage.jsx | 76 ++++++++++++++++++- 2 files changed, 85 insertions(+), 9 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index fbd4ecb6d6..b157184835 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -11,6 +11,7 @@ import BlocksComponent from '../components/blocks/blocks.jsx'; import {connect} from 'react-redux'; import {updateToolbox} from '../reducers/toolbox'; +import {activateColorPicker} from '../reducers/color-picker'; const addFunctionListener = (object, property, callback) => { const oldFn = object[property]; @@ -50,6 +51,8 @@ class Blocks extends React.Component { this.onTargetsUpdate = debounce(this.onTargetsUpdate, 100); } componentDidMount () { + this.ScratchBlocks.FieldColourSlider.activateEyedropper_ = this.props.onActivateColorPicker; + const workspaceConfig = defaultsDeep({}, Blocks.defaultOptions, this.props.options); this.workspace = this.ScratchBlocks.inject(this.blocks, workspaceConfig); @@ -219,14 +222,17 @@ class Blocks extends React.Component { this.setState({prompt: null}); } render () { + /* eslint-disable no-unused-vars */ const { - options, // eslint-disable-line no-unused-vars - vm, // eslint-disable-line no-unused-vars - isVisible, // eslint-disable-line no-unused-vars - onExtensionAdded, // eslint-disable-line no-unused-vars - toolboxXML, // eslint-disable-line no-unused-vars + options, + vm, + isVisible, + onActivateColorPicker, + onExtensionAdded, + toolboxXML, ...props } = this.props; + /* eslint-enable no-unused-vars */ return (
({ }); const mapDispatchToProps = dispatch => ({ + onActivateColorPicker: callback => dispatch(activateColorPicker(callback)), onExtensionAdded: toolboxXML => { dispatch(updateToolbox(toolboxXML)); } diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 1b5b2ec9cc..6d3d58211c 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -3,10 +3,19 @@ import PropTypes from 'prop-types'; import React from 'react'; import Renderer from 'scratch-render'; import VM from 'scratch-vm'; +import {connect} from 'react-redux'; + import {getEventXY} from '../lib/touch-utils'; import StageComponent from '../components/stage/stage.jsx'; +import { + activateColorPicker, + deactivateColorPicker +} from '../reducers/color-picker'; + +const colorPickerRadius = 20; + class Stage extends React.Component { constructor (props) { super(props); @@ -28,7 +37,8 @@ class Stage extends React.Component { mouseDownPosition: null, isDragging: false, dragOffset: null, - dragId: null + dragId: null, + colorInfo: null }; } componentDidMount () { @@ -38,12 +48,31 @@ class Stage extends React.Component { this.renderer = new Renderer(this.canvas); this.props.vm.attachRenderer(this.renderer); } - shouldComponentUpdate (nextProps) { - return this.props.width !== nextProps.width || this.props.height !== nextProps.height; + shouldComponentUpdate (nextProps, nextState) { + return this.props.width !== nextProps.width || + this.props.height !== nextProps.height || + this.props.isColorPicking !== nextProps.isColorPicking || + this.state.colorInfo !== nextState.colorInfo; + } + componentDidUpdate (prevProps) { + if (this.props.isColorPicking && !prevProps.isColorPicking) { + this.startColorPickingLoop(); + } else if (!this.props.isColorPicking && prevProps.isColorPicking) { + this.stopColorPickingLoop(); + } } componentWillUnmount () { this.detachMouseEvents(this.canvas); this.detachRectEvents(); + this.stopColorPickingLoop(); + } + startColorPickingLoop () { + this.intervalId = setInterval(() => { + this.setState({colorInfo: this.getColorInfo(this.pickX, this.pickY)}); + }, 30); + } + stopColorPickingLoop () { + clearInterval(this.intervalId); } attachMouseEvents (canvas) { document.addEventListener('mousemove', this.onMouseMove); @@ -79,6 +108,13 @@ class Stage extends React.Component { (nativeSize[1] / this.rect.height) * (y - (this.rect.height / 2)) ]; } + getColorInfo (x, y) { + return { + x: x, + y: y, + ...this.renderer.extractColor(x, y, colorPickerRadius) + }; + } handleDoubleClick (e) { const {x, y} = getEventXY(e); // Set editing target from cursor position, if clicking on a sprite. @@ -92,6 +128,11 @@ class Stage extends React.Component { onMouseMove (e) { const {x, y} = getEventXY(e); const mousePosition = [x - this.rect.left, y - this.rect.top]; + + // Set the pickX/Y for the color picker loop to pick up + this.pickX = mousePosition[0]; + this.pickY = mousePosition[1]; + if (this.state.mouseDownTimeoutId !== null) { this.cancelMouseDownTimeout(); if (this.state.mouseDown && !this.state.isDragging) { @@ -157,6 +198,16 @@ class Stage extends React.Component { if (e.preventDefault) { e.preventDefault(); } + if (this.props.isColorPicking) { + const {r, g, b} = this.state.colorInfo.color; + const componentToString = c => { + const hex = c.toString(16); + return hex.length === 1 ? `0${hex}` : hex; + }; + const colorString = `#${componentToString(r)}${componentToString(g)}${componentToString(b)}`; + this.props.onDeactivateColorPicker(colorString); + this.setState({colorInfo: null}); + } } cancelMouseDownTimeout () { if (this.state.mouseDownTimeoutId !== null) { @@ -191,11 +242,13 @@ class Stage extends React.Component { render () { const { vm, // eslint-disable-line no-unused-vars + onActivateColorPicker, // eslint-disable-line no-unused-vars ...props } = this.props; return ( @@ -205,8 +258,23 @@ class Stage extends React.Component { Stage.propTypes = { height: PropTypes.number, + isColorPicking: PropTypes.bool, + onActivateColorPicker: PropTypes.func, + onDeactivateColorPicker: PropTypes.func, vm: PropTypes.instanceOf(VM).isRequired, width: PropTypes.number }; -export default Stage; +const mapStateToProps = state => ({ + isColorPicking: state.colorPicker.active +}); + +const mapDispatchToProps = dispatch => ({ + onActivateColorPicker: () => dispatch(activateColorPicker()), + onDeactivateColorPicker: color => dispatch(deactivateColorPicker(color)) +}); + +export default connect( + mapStateToProps, + mapDispatchToProps +)(Stage); From a412fad7f686dfa9fa38f0c114858f5da3e128c3 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 2 Oct 2017 18:16:19 -0400 Subject: [PATCH 0165/1971] Merge pull request #726 from ericrosenbaum/bugfix/remove-integration-test Remove integration test --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 188c8e4b31..0d1dcce73e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -13,7 +13,7 @@ "start": "npm run i18n:msgs && webpack-dev-server", "unit-test": "jest test/unit", "integration-test": "npm run build && jest --runInBand test/integration", - "test": "npm run lint && npm run unit-test && npm run integration-test", + "test": "npm run lint && npm run unit-test", "watch": "webpack --progress --colors --watch" }, "author": "Massachusetts Institute of Technology", From b6e43a552d5c67856c7d7d07656301a0ce8961d2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 4 Oct 2017 14:00:26 -0400 Subject: [PATCH 0166/1971] Merge pull request #737 from paulkaplan/fix-develop-deploy Add npm run build to the test to cover for removed integration tests --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0d1dcce73e..3bd394669e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -12,8 +12,8 @@ "lint": "eslint . --ext .js,.jsx", "start": "npm run i18n:msgs && webpack-dev-server", "unit-test": "jest test/unit", - "integration-test": "npm run build && jest --runInBand test/integration", - "test": "npm run lint && npm run unit-test", + "integration-test": "jest --runInBand test/integration", + "test": "npm run lint && npm run unit-test && npm run build", "watch": "webpack --progress --colors --watch" }, "author": "Massachusetts Institute of Technology", From 103678cc7b95aa50eec1718b6fdab655ad830ff0 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 4 Oct 2017 14:02:16 -0400 Subject: [PATCH 0167/1971] Merge pull request #733 from ericrosenbaum/bugfix/sync-toolbox Sync toolbox --- packages/scratch-gui/src/containers/blocks.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index b157184835..7a2a4d908f 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -56,6 +56,9 @@ class Blocks extends React.Component { const workspaceConfig = defaultsDeep({}, Blocks.defaultOptions, this.props.options); this.workspace = this.ScratchBlocks.inject(this.blocks, workspaceConfig); + // Load the toolbox from the GUI (otherwise we get the scratch-blocks default toolbox) + this.workspace.updateToolbox(this.props.toolboxXML); + // @todo change this when blockly supports UI events addFunctionListener(this.workspace, 'translate', this.onWorkspaceMetricsChange); addFunctionListener(this.workspace, 'zoom', this.onWorkspaceMetricsChange); From 06f182e97dfb0076a71fb18a771b421b0878eba0 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 5 Oct 2017 15:54:30 -0400 Subject: [PATCH 0168/1971] Merge pull request #742 from LLK/greenkeeper/eslint-config-scratch-5.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update eslint-config-scratch to the latest version 🚀 --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3bd394669e..9c05851275 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -31,7 +31,7 @@ "autoprefixer": "^7.1.3", "babel-cli": "^6.26.0", "babel-core": "^6.23.1", - "babel-eslint": "^7.2.3", + "babel-eslint": "^8.0.1", "babel-loader": "^7.1.0", "babel-plugin-react-intl": "^2.3.1", "babel-plugin-syntax-dynamic-import": "^6.18.0", @@ -45,7 +45,7 @@ "css-loader": "^0.28.7", "enzyme": "^2.8.2", "eslint": "^4.7.1", - "eslint-config-scratch": "^4.0.0", + "eslint-config-scratch": "^5.0.0", "eslint-plugin-import": "^2.7.0", "eslint-plugin-react": "^7.2.1", "get-float-time-domain-data": "0.1.0", From 5dd3b310af7dec55d317d51edab56ac4829e5af7 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 5 Oct 2017 15:55:45 -0400 Subject: [PATCH 0169/1971] Merge pull request #740 from LLK/greenkeeper/react-tabs-2.1.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-tabs to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9c05851275..c16938f83b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -75,7 +75,7 @@ "react-redux": "5.0.6", "react-responsive": "1.3.4", "react-style-proptype": "3.0.0", - "react-tabs": "2.0.0", + "react-tabs": "2.1.0", "react-test-renderer": "^15.5.4", "redux": "3.7.0", "redux-mock-store": "^1.2.3", From fb1193d0a90f27a3f2ba0ba86ebaa05af15eabbf Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 6 Oct 2017 10:45:02 -0400 Subject: [PATCH 0170/1971] Merge pull request #744 from paulkaplan/fix-selenium-tests Fix selenium tests --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c16938f83b..708548e41e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -13,7 +13,7 @@ "start": "npm run i18n:msgs && webpack-dev-server", "unit-test": "jest test/unit", "integration-test": "jest --runInBand test/integration", - "test": "npm run lint && npm run unit-test && npm run build", + "test": "npm run lint && npm run unit-test && npm run build && npm run integration-test", "watch": "webpack --progress --colors --watch" }, "author": "Massachusetts Institute of Technology", From 48a7b337486d53254dc1f221200ccdc78b127a42 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 6 Oct 2017 10:50:23 -0400 Subject: [PATCH 0171/1971] Merge pull request #717 from bartljak/issue-#701 Fixed issue causing right click to drag sprite. --- packages/scratch-gui/src/containers/stage.jsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 6d3d58211c..1de93d23d3 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -179,14 +179,16 @@ class Stage extends React.Component { this.updateRect(); const {x, y} = getEventXY(e); const mousePosition = [x - this.rect.left, y - this.rect.top]; - this.setState({ - mouseDown: true, - mouseDownPosition: mousePosition, - mouseDownTimeoutId: setTimeout( - this.onStartDrag.bind(this, mousePosition[0], mousePosition[1]), - 500 - ) - }); + if (e.button === 0) { + this.setState({ + mouseDown: true, + mouseDownPosition: mousePosition, + mouseDownTimeoutId: setTimeout( + this.onStartDrag.bind(this, mousePosition[0], mousePosition[1]), + 500 + ) + }); + } const data = { isDown: true, x: mousePosition[0], From 9d4c83ca4c856fa7478e4eec76488ba01e7fd780 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 11 Oct 2017 11:18:28 -0400 Subject: [PATCH 0172/1971] Merge pull request #753 from paulkaplan/fix-travis-again Fix travis again by pinning chromedriver and selenium versions --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 708548e41e..56cc7105e4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -39,7 +39,7 @@ "babel-plugin-transform-object-rest-spread": "^6.22.0", "babel-preset-es2015": "^6.22.0", "babel-preset-react": "^6.22.0", - "chromedriver": "^2.32.3", + "chromedriver": "2.32.3", "classnames": "2.2.5", "copy-webpack-plugin": "^4.0.1", "css-loader": "^0.28.7", @@ -86,7 +86,7 @@ "scratch-render": "latest", "scratch-storage": "^0.2.0", "scratch-vm": "latest", - "selenium-webdriver": "^3.5.0", + "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.18.0", "svg-to-image": "1.1.3", From b89360841d8e571f7d2d5544d1a17ad945c87b6f Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 11 Oct 2017 11:10:01 -0700 Subject: [PATCH 0173/1971] Merge pull request #691 from cwillisf/fix-tests-on-windows Fix tests on Windows --- packages/scratch-gui/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 56cc7105e4..8399f51fce 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -9,11 +9,11 @@ "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", "i18n:msgs": "node ./scripts/generate-locale-messages.js", "i18n:src": "babel src > tmp.js && rimraf tmp.js && ./scripts/build-i18n-source.js ./translations/messages/ ./translations/", - "lint": "eslint . --ext .js,.jsx", "start": "npm run i18n:msgs && webpack-dev-server", - "unit-test": "jest test/unit", - "integration-test": "jest --runInBand test/integration", - "test": "npm run lint && npm run unit-test && npm run build && npm run integration-test", + "test": "npm run test:lint && npm run build && npm run test:unit && npm run test:integration", + "test:integration": "jest --runInBand test[\\\\/]integration", + "test:lint": "eslint . --ext .js,.jsx", + "test:unit": "jest test[\\\\/]unit", "watch": "webpack --progress --colors --watch" }, "author": "Massachusetts Institute of Technology", From d0aa6a9176c38fb6654fb07b378f01a7bcae1d6b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 13 Oct 2017 14:15:05 -0400 Subject: [PATCH 0174/1971] Merge pull request #774 from paulkaplan/react-16 Update to react 16! --- packages/scratch-gui/package.json | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8399f51fce..344cac1262 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -10,7 +10,7 @@ "i18n:msgs": "node ./scripts/generate-locale-messages.js", "i18n:src": "babel src > tmp.js && rimraf tmp.js && ./scripts/build-i18n-source.js ./translations/messages/ ./translations/", "start": "npm run i18n:msgs && webpack-dev-server", - "test": "npm run test:lint && npm run build && npm run test:unit && npm run test:integration", + "test": "npm run test:lint && npm run test:unit && NODE_ENV=production npm run build && npm run test:integration", "test:integration": "jest --runInBand test[\\\\/]integration", "test:lint": "eslint . --ext .js,.jsx", "test:unit": "jest test[\\\\/]unit", @@ -24,8 +24,8 @@ "url": "git+ssh://git@github.com/LLK/scratch-gui.git" }, "peerDependencies": { - "react": "^15.6.1", - "react-dom": "^15.6.1" + "react": "^16.0.0", + "react-dom": "^16.0.0" }, "devDependencies": { "autoprefixer": "^7.1.3", @@ -43,7 +43,8 @@ "classnames": "2.2.5", "copy-webpack-plugin": "^4.0.1", "css-loader": "^0.28.7", - "enzyme": "^2.8.2", + "enzyme": "^3.1.0", + "enzyme-adapter-react-16": "1.0.1", "eslint": "^4.7.1", "eslint-config-scratch": "^5.0.0", "eslint-plugin-import": "^2.7.0", @@ -65,18 +66,19 @@ "postcss-loader": "^2.0.5", "postcss-simple-vars": "^4.0.0", "prop-types": "^15.5.10", - "react": "15.6.1", - "react-contextmenu": "2.7.0", - "react-dom": "15.6.1", + "raf": "^3.4.0", + "react": "16.0.0", + "react-contextmenu": "2.8.0", + "react-dom": "16.0.0", "react-draggable": "3.0.3", "react-intl": "2.4.0", "react-intl-redux": "0.6.0", - "react-modal": "2.3.1", + "react-modal": "3.0.0", "react-redux": "5.0.6", - "react-responsive": "1.3.4", + "react-responsive": "2.0.0", "react-style-proptype": "3.0.0", "react-tabs": "2.1.0", - "react-test-renderer": "^15.5.4", + "react-test-renderer": "16.0.0", "redux": "3.7.0", "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", @@ -98,6 +100,10 @@ "xhr": "2.4.0" }, "jest": { + "setupFiles": [ + "raf/polyfill", + "/test/helpers/enzyme-setup.js" + ], "testPathIgnorePatterns": [ "src/test.js" ], From ec7dae42b541c7be48c0cd22b490ebb20f89554c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 13 Oct 2017 15:00:06 -0400 Subject: [PATCH 0175/1971] Merge pull request #730 from LLK/greenkeeper/style-loader-0.19.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update style-loader to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 344cac1262..a7220ee00b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -90,7 +90,7 @@ "scratch-vm": "latest", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", - "style-loader": "^0.18.0", + "style-loader": "^0.19.0", "svg-to-image": "1.1.3", "svg-url-loader": "^2.1.0", "wav-encoder": "1.3.0", From 7414da34ff85da1c2a02f3dde95d35cbb0502866 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 13 Oct 2017 15:02:36 -0400 Subject: [PATCH 0176/1971] Merge pull request #685 from LLK/greenkeeper/postcss-import-11.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update postcss-import to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a7220ee00b..0bba00aea1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -62,7 +62,7 @@ "lodash.pick": "4.4.0", "minilog": "3.1.0", "mkdirp": "^0.5.1", - "postcss-import": "^10.0.0", + "postcss-import": "^11.0.0", "postcss-loader": "^2.0.5", "postcss-simple-vars": "^4.0.0", "prop-types": "^15.5.10", From fb491a7011ac8512d405f695fe6b1e5bbdf630f5 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Sat, 14 Oct 2017 19:45:48 -0400 Subject: [PATCH 0177/1971] Merge pull request #760 from fsih/guiPaintIntegration Add paint editor --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0bba00aea1..ab13e243e4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -84,6 +84,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "latest", + "scratch-paint": "latest", "scratch-blocks": "latest", "scratch-render": "latest", "scratch-storage": "^0.2.0", From ed81670439f2bd4f56eb18f3afaf19429b0d522d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 16 Oct 2017 10:51:05 -0400 Subject: [PATCH 0178/1971] Merge pull request #776 from LLK/greenkeeper/react-modal-3.0.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-modal to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ab13e243e4..cb81262909 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -73,7 +73,7 @@ "react-draggable": "3.0.3", "react-intl": "2.4.0", "react-intl-redux": "0.6.0", - "react-modal": "3.0.0", + "react-modal": "3.0.2", "react-redux": "5.0.6", "react-responsive": "2.0.0", "react-style-proptype": "3.0.0", From 6b23ae7a21bb62597e59dc62b646405859e33efc Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Mon, 16 Oct 2017 15:31:13 -0400 Subject: [PATCH 0179/1971] Merge pull request #775 from chrisgarrity/feature/use-l10n Import translations from scratch-l10n package --- packages/scratch-gui/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cb81262909..61b359d08e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -4,12 +4,11 @@ "description": "GraphicaL User Interface for creating and running Scratch 3.0 projects", "main": "./src/index.js", "scripts": { - "build": "npm run clean && npm run i18n:msgs && webpack --progress --colors --bail", + "build": "npm run clean && webpack --progress --colors --bail", "clean": "rimraf ./build && mkdirp build", "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", - "i18n:msgs": "node ./scripts/generate-locale-messages.js", "i18n:src": "babel src > tmp.js && rimraf tmp.js && ./scripts/build-i18n-source.js ./translations/messages/ ./translations/", - "start": "npm run i18n:msgs && webpack-dev-server", + "start": "webpack-dev-server", "test": "npm run test:lint && npm run test:unit && NODE_ENV=production npm run build && npm run test:integration", "test:integration": "jest --runInBand test[\\\\/]integration", "test:lint": "eslint . --ext .js,.jsx", @@ -86,6 +85,7 @@ "scratch-audio": "latest", "scratch-paint": "latest", "scratch-blocks": "latest", + "scratch-l10n": "1.0.20171013211708", "scratch-render": "latest", "scratch-storage": "^0.2.0", "scratch-vm": "latest", From 54e9921f440ad0b8ad0965564b17a44e00de0746 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 17 Oct 2017 13:15:15 -0400 Subject: [PATCH 0180/1971] Merge pull request #787 from LLK/greenkeeper/enzyme-adapter-react-16-1.0.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update enzyme-adapter-react-16 to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 61b359d08e..e9acf96a82 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -43,7 +43,7 @@ "copy-webpack-plugin": "^4.0.1", "css-loader": "^0.28.7", "enzyme": "^3.1.0", - "enzyme-adapter-react-16": "1.0.1", + "enzyme-adapter-react-16": "1.0.2", "eslint": "^4.7.1", "eslint-config-scratch": "^5.0.0", "eslint-plugin-import": "^2.7.0", From 442868ed00fc4b25a8756c7d7909cdafa7e807b9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 18 Oct 2017 09:14:09 -0400 Subject: [PATCH 0181/1971] Merge pull request #789 from LLK/greenkeeper/react-responsive-3.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-responsive to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e9acf96a82..5d0e0c2217 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -74,7 +74,7 @@ "react-intl-redux": "0.6.0", "react-modal": "3.0.2", "react-redux": "5.0.6", - "react-responsive": "2.0.0", + "react-responsive": "3.0.0", "react-style-proptype": "3.0.0", "react-tabs": "2.1.0", "react-test-renderer": "16.0.0", From 2e4a249ef0d3fdfcc271d102d022d71f0b9a5d99 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 18 Oct 2017 10:48:33 -0400 Subject: [PATCH 0182/1971] Merge pull request #790 from paulkaplan/update-chromedriver-2.33.1 Update to chromedriver 2.33.1 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5d0e0c2217..3660d18cad 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -38,7 +38,7 @@ "babel-plugin-transform-object-rest-spread": "^6.22.0", "babel-preset-es2015": "^6.22.0", "babel-preset-react": "^6.22.0", - "chromedriver": "2.32.3", + "chromedriver": "2.33.1", "classnames": "2.2.5", "copy-webpack-plugin": "^4.0.1", "css-loader": "^0.28.7", From 141f73cc0718d09db2dac3381cd6379dc0505c41 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 18 Oct 2017 16:10:34 -0400 Subject: [PATCH 0183/1971] Merge pull request #779 from paulkaplan/fix-multi-refresh Remove manual refresh for scratch-blocks workspace --- packages/scratch-gui/src/containers/blocks.jsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 7a2a4d908f..c8ffb1a792 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -85,12 +85,10 @@ class Blocks extends React.Component { } // @todo hack to resize blockly manually in case resize happened while hidden - // @todo hack to reload the workspace due to gui bug #413 if (this.props.isVisible) { // Scripts tab this.workspace.setVisible(true); this.props.vm.refreshWorkspace(); window.dispatchEvent(new Event('resize')); - this.workspace.toolbox_.refreshSelection(); } else { this.workspace.setVisible(false); } @@ -195,7 +193,6 @@ class Blocks extends React.Component { const dom = this.ScratchBlocks.Xml.textToDom(data.xml); this.ScratchBlocks.Xml.domToWorkspace(dom, this.workspace); this.ScratchBlocks.Events.enable(); - this.workspace.toolbox_.refreshSelection(); if (this.props.vm.editingTarget && this.state.workspaceMetrics[this.props.vm.editingTarget.id]) { const {scrollX, scrollY, scale} = this.state.workspaceMetrics[this.props.vm.editingTarget.id]; From af8443ee5642273149b3a8e48c2431938f53b0cb Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 19 Oct 2017 15:54:11 -0400 Subject: [PATCH 0184/1971] Merge pull request #793 from ericrosenbaum/feature/add-pen-extension New extension library modal styles (with pen extension) --- .../scratch-gui/src/containers/blocks.jsx | 21 +++---------------- 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index c8ffb1a792..01aea5af3b 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -76,14 +76,11 @@ class Blocks extends React.Component { if (prevProps.toolboxXML !== this.props.toolboxXML) { const selectedCategoryName = this.workspace.toolbox_.getSelectedItem().name_; this.workspace.updateToolbox(this.props.toolboxXML); - // Blockly throws if we don't select a category after updating the toolbox. - /** @TODO Find a way to avoid the exception without accessing private properties. */ - this.setToolboxSelectedItemByName(selectedCategoryName); + this.workspace.toolbox_.setSelectedCategoryByName(selectedCategoryName); } if (this.props.isVisible === prevProps.isVisible) { return; } - // @todo hack to resize blockly manually in case resize happened while hidden if (this.props.isVisible) { // Scripts tab this.workspace.setVisible(true); @@ -97,20 +94,6 @@ class Blocks extends React.Component { this.detachVM(); this.workspace.dispose(); } - /** - * Select a particular category in the toolbox by specifying the category name. - * This is a workaround for a bug: @see {@link componentDidUpdate} above. - * @TODO Remove this or reimplement using only public APIs. - * @param {string} name - the name of the category to select. - */ - setToolboxSelectedItemByName (name) { - const categories = this.workspace.toolbox_.categoryMenu_.categories_; - for (let i = 0; i < categories.length; i++) { - if (categories[i].name_ === name) { - this.workspace.toolbox_.setSelectedItem(categories[i]); - } - } - } attachVM () { this.workspace.addChangeListener(this.props.vm.blockListener); this.flyoutWorkspace = this.workspace @@ -207,6 +190,8 @@ class Blocks extends React.Component { const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(); const toolboxXML = makeToolboxXML(dynamicBlocksXML); this.props.onExtensionAdded(toolboxXML); + const categoryName = blocksInfo[0].json.category; + this.workspace.toolbox_.setSelectedCategoryByName(categoryName); } setBlocks (blocks) { this.blocks = blocks; From 3d8642ff2c2de4e6c6e27bd46f6e2932023cf33a Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 19 Oct 2017 15:56:15 -0400 Subject: [PATCH 0185/1971] Merge pull request #788 from chrisgarrity/feature/add-paint-l10n importing l10n that includes paint strings --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3660d18cad..690963a1d3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -85,7 +85,7 @@ "scratch-audio": "latest", "scratch-paint": "latest", "scratch-blocks": "latest", - "scratch-l10n": "1.0.20171013211708", + "scratch-l10n": "^2.0.0", "scratch-render": "latest", "scratch-storage": "^0.2.0", "scratch-vm": "latest", From 2173c0c5dd9e39852357b23fe52b435e77cbdadf Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 23 Oct 2017 10:45:08 -0400 Subject: [PATCH 0186/1971] Merge pull request #803 from paulkaplan/revert-multi-refresh Revert broken performance improvement and add tests --- packages/scratch-gui/src/containers/blocks.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 01aea5af3b..345d21e26f 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -82,10 +82,12 @@ class Blocks extends React.Component { return; } // @todo hack to resize blockly manually in case resize happened while hidden + // @todo hack to reload the workspace due to gui bug #413 if (this.props.isVisible) { // Scripts tab this.workspace.setVisible(true); this.props.vm.refreshWorkspace(); window.dispatchEvent(new Event('resize')); + this.workspace.toolbox_.refreshSelection(); } else { this.workspace.setVisible(false); } @@ -176,6 +178,7 @@ class Blocks extends React.Component { const dom = this.ScratchBlocks.Xml.textToDom(data.xml); this.ScratchBlocks.Xml.domToWorkspace(dom, this.workspace); this.ScratchBlocks.Events.enable(); + this.workspace.toolbox_.refreshSelection(); if (this.props.vm.editingTarget && this.state.workspaceMetrics[this.props.vm.editingTarget.id]) { const {scrollX, scrollY, scale} = this.state.workspaceMetrics[this.props.vm.editingTarget.id]; From e03051aae9f9980e4a776b2d0369e51f92f919b6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 25 Oct 2017 15:44:55 -0400 Subject: [PATCH 0187/1971] Merge pull request #807 from paulkaplan/sudo-stop-multi-refresh Stop refreshing the toolbox by using new batch update API --- packages/scratch-gui/src/containers/blocks.jsx | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 345d21e26f..d9c8c5221a 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -87,7 +87,6 @@ class Blocks extends React.Component { this.workspace.setVisible(true); this.props.vm.refreshWorkspace(); window.dispatchEvent(new Event('resize')); - this.workspace.toolbox_.refreshSelection(); } else { this.workspace.setVisible(false); } @@ -172,13 +171,11 @@ class Blocks extends React.Component { this.onWorkspaceMetricsChange(); } - this.ScratchBlocks.Events.disable(); - this.workspace.clear(); - + // Remove and reattach the workspace listener (but allow flyout events) + this.workspace.removeChangeListener(this.props.vm.blockListener); const dom = this.ScratchBlocks.Xml.textToDom(data.xml); - this.ScratchBlocks.Xml.domToWorkspace(dom, this.workspace); - this.ScratchBlocks.Events.enable(); - this.workspace.toolbox_.refreshSelection(); + this.ScratchBlocks.Xml.clearWorkspaceAndLoadFromXml(dom, this.workspace); + this.workspace.addChangeListener(this.props.vm.blockListener); if (this.props.vm.editingTarget && this.state.workspaceMetrics[this.props.vm.editingTarget.id]) { const {scrollX, scrollY, scale} = this.state.workspaceMetrics[this.props.vm.editingTarget.id]; From c4ea11d7e9694861164a05a7988d9ccd09eed38c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 1 Nov 2017 15:50:25 -0400 Subject: [PATCH 0188/1971] Merge pull request #822 from MakeblockTeam/develop Update toolbox before ScratchBlocks inject and Give IDs to some shadow blocks --- packages/scratch-gui/src/containers/blocks.jsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index d9c8c5221a..58c0841ba3 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -53,12 +53,13 @@ class Blocks extends React.Component { componentDidMount () { this.ScratchBlocks.FieldColourSlider.activateEyedropper_ = this.props.onActivateColorPicker; - const workspaceConfig = defaultsDeep({}, Blocks.defaultOptions, this.props.options); + const workspaceConfig = defaultsDeep({}, + Blocks.defaultOptions, + this.props.options, + {toolbox: this.props.toolboxXML} + ); this.workspace = this.ScratchBlocks.inject(this.blocks, workspaceConfig); - // Load the toolbox from the GUI (otherwise we get the scratch-blocks default toolbox) - this.workspace.updateToolbox(this.props.toolboxXML); - // @todo change this when blockly supports UI events addFunctionListener(this.workspace, 'translate', this.onWorkspaceMetricsChange); addFunctionListener(this.workspace, 'zoom', this.onWorkspaceMetricsChange); From fa867a3763946dafbed527ef8d81fb9fbc3e818d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 2 Nov 2017 10:18:54 -0400 Subject: [PATCH 0189/1971] Merge pull request #835 from LLK/greenkeeper/enzyme-adapter-react-16-1.0.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update enzyme-adapter-react-16 to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 690963a1d3..19fc023879 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -43,7 +43,7 @@ "copy-webpack-plugin": "^4.0.1", "css-loader": "^0.28.7", "enzyme": "^3.1.0", - "enzyme-adapter-react-16": "1.0.2", + "enzyme-adapter-react-16": "1.0.3", "eslint": "^4.7.1", "eslint-config-scratch": "^5.0.0", "eslint-plugin-import": "^2.7.0", From 48542765b7438f901b412bd10f972e6c4953cc3f Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 2 Nov 2017 14:06:30 -0400 Subject: [PATCH 0190/1971] Merge pull request #834 from ericrosenbaum/bugfix/extension-dedupe Prevent loading duplicate extensions --- .../scratch-gui/src/containers/blocks.jsx | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 58c0841ba3..59dd1a5398 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -8,10 +8,12 @@ import VMScratchBlocks from '../lib/blocks'; import VM from 'scratch-vm'; import Prompt from './prompt.jsx'; import BlocksComponent from '../components/blocks/blocks.jsx'; +import ExtensionLibrary from './extension-library.jsx'; import {connect} from 'react-redux'; import {updateToolbox} from '../reducers/toolbox'; import {activateColorPicker} from '../reducers/color-picker'; +import {closeExtensionLibrary} from '../reducers/modals'; const addFunctionListener = (object, property, callback) => { const oldFn = object[property]; @@ -29,6 +31,7 @@ class Blocks extends React.Component { bindAll(this, [ 'attachVM', 'detachVM', + 'handleCategorySelected', 'handlePromptStart', 'handlePromptCallback', 'handlePromptClose', @@ -70,7 +73,8 @@ class Blocks extends React.Component { return ( this.state.prompt !== nextState.prompt || this.props.isVisible !== nextProps.isVisible || - this.props.toolboxXML !== nextProps.toolboxXML + this.props.toolboxXML !== nextProps.toolboxXML || + this.props.extensionLibraryVisible !== nextProps.extensionLibraryVisible ); } componentDidUpdate (prevProps) { @@ -192,6 +196,9 @@ class Blocks extends React.Component { const toolboxXML = makeToolboxXML(dynamicBlocksXML); this.props.onExtensionAdded(toolboxXML); const categoryName = blocksInfo[0].json.category; + this.handleCategorySelected(categoryName); + } + handleCategorySelected (categoryName) { this.workspace.toolbox_.setSelectedCategoryByName(categoryName); } setBlocks (blocks) { @@ -210,11 +217,13 @@ class Blocks extends React.Component { render () { /* eslint-disable no-unused-vars */ const { + extensionLibraryVisible, options, vm, isVisible, onActivateColorPicker, onExtensionAdded, + onRequestCloseExtensionLibrary, toolboxXML, ...props } = this.props; @@ -234,15 +243,24 @@ class Blocks extends React.Component { onOk={this.handlePromptCallback} /> ) : null} + {extensionLibraryVisible ? ( + + ) : null}
); } } Blocks.propTypes = { + extensionLibraryVisible: PropTypes.bool, isVisible: PropTypes.bool, onActivateColorPicker: PropTypes.func, onExtensionAdded: PropTypes.func, + onRequestCloseExtensionLibrary: PropTypes.func, options: PropTypes.shape({ media: PropTypes.string, zoom: PropTypes.shape({ @@ -300,6 +318,7 @@ Blocks.defaultProps = { }; const mapStateToProps = state => ({ + extensionLibraryVisible: state.modals.extensionLibrary, toolboxXML: state.toolbox.toolboxXML }); @@ -307,6 +326,9 @@ const mapDispatchToProps = dispatch => ({ onActivateColorPicker: callback => dispatch(activateColorPicker(callback)), onExtensionAdded: toolboxXML => { dispatch(updateToolbox(toolboxXML)); + }, + onRequestCloseExtensionLibrary: () => { + dispatch(closeExtensionLibrary()); } }); From 48f68ba44596494429758f6009c547ab5df9898d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 2 Nov 2017 17:11:42 -0400 Subject: [PATCH 0191/1971] Merge pull request #846 from LLK/greenkeeper/scratch-storage-0.3.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-storage to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 19fc023879..900812b578 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -87,7 +87,7 @@ "scratch-blocks": "latest", "scratch-l10n": "^2.0.0", "scratch-render": "latest", - "scratch-storage": "^0.2.0", + "scratch-storage": "^0.3.0", "scratch-vm": "latest", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", From 2061a586c388c4b3d55499b11156c369d4b07910 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 3 Nov 2017 09:47:54 -0400 Subject: [PATCH 0192/1971] Merge pull request #837 from rschamp/offline-cache Allow loading the default project while offline --- packages/scratch-gui/package.json | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 900812b578..13a4b8c1f0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -38,6 +38,7 @@ "babel-plugin-transform-object-rest-spread": "^6.22.0", "babel-preset-es2015": "^6.22.0", "babel-preset-react": "^6.22.0", + "buffer-loader": "0.0.1", "chromedriver": "2.33.1", "classnames": "2.2.5", "copy-webpack-plugin": "^4.0.1", @@ -48,6 +49,7 @@ "eslint-config-scratch": "^5.0.0", "eslint-plugin-import": "^2.7.0", "eslint-plugin-react": "^7.2.1", + "file-loader": "1.1.5", "get-float-time-domain-data": "0.1.0", "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", "html-webpack-plugin": "^2.30.0", @@ -66,6 +68,7 @@ "postcss-simple-vars": "^4.0.0", "prop-types": "^15.5.10", "raf": "^3.4.0", + "raw-loader": "0.5.1", "react": "16.0.0", "react-contextmenu": "2.8.0", "react-dom": "16.0.0", @@ -83,9 +86,9 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "latest", - "scratch-paint": "latest", "scratch-blocks": "latest", "scratch-l10n": "^2.0.0", + "scratch-paint": "latest", "scratch-render": "latest", "scratch-storage": "^0.3.0", "scratch-vm": "latest", @@ -93,7 +96,7 @@ "startaudiocontext": "1.2.1", "style-loader": "^0.19.0", "svg-to-image": "1.1.3", - "svg-url-loader": "^2.1.0", + "text-encoding": "0.6.4", "wav-encoder": "1.3.0", "web-audio-test-api": "^0.5.2", "webpack": "^3.6.0", From 18eb108193e39a568a6b44910eb0655be13ddd27 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 3 Nov 2017 16:15:54 -0400 Subject: [PATCH 0193/1971] Merge pull request #859 from ericrosenbaum/bugfix/no-scroll-on-autoload-extension Scroll to extension category only when loading from library --- packages/scratch-gui/src/containers/blocks.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 59dd1a5398..471dc6cd08 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -195,8 +195,6 @@ class Blocks extends React.Component { const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(); const toolboxXML = makeToolboxXML(dynamicBlocksXML); this.props.onExtensionAdded(toolboxXML); - const categoryName = blocksInfo[0].json.category; - this.handleCategorySelected(categoryName); } handleCategorySelected (categoryName) { this.workspace.toolbox_.setSelectedCategoryByName(categoryName); From fa99d96c5ee6c8cd195d8548c1310990708e9a2e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 7 Nov 2017 09:51:53 -0500 Subject: [PATCH 0194/1971] Merge pull request #800 from LLK/greenkeeper/react-style-proptype-3.1.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-style-proptype to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 13a4b8c1f0..9a7fe4f1d1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -78,7 +78,7 @@ "react-modal": "3.0.2", "react-redux": "5.0.6", "react-responsive": "3.0.0", - "react-style-proptype": "3.0.0", + "react-style-proptype": "3.1.0", "react-tabs": "2.1.0", "react-test-renderer": "16.0.0", "redux": "3.7.0", From a26b646688ea7de69cbcf1a62570de818686eab7 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 7 Nov 2017 09:53:35 -0500 Subject: [PATCH 0195/1971] Merge pull request #862 from LLK/greenkeeper/enzyme-adapter-react-16-1.0.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update enzyme-adapter-react-16 to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9a7fe4f1d1..9a1119fbff 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -44,7 +44,7 @@ "copy-webpack-plugin": "^4.0.1", "css-loader": "^0.28.7", "enzyme": "^3.1.0", - "enzyme-adapter-react-16": "1.0.3", + "enzyme-adapter-react-16": "1.0.4", "eslint": "^4.7.1", "eslint-config-scratch": "^5.0.0", "eslint-plugin-import": "^2.7.0", From 1400c2d32c4fa6103678abcff58042bcaeac2550 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 9 Nov 2017 16:59:06 -0500 Subject: [PATCH 0196/1971] Merge pull request #868 from chrisgarrity/l10n-update Use scratch-ll10n for extracting strings --- packages/scratch-gui/package.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9a1119fbff..09d176b74c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -7,7 +7,7 @@ "build": "npm run clean && webpack --progress --colors --bail", "clean": "rimraf ./build && mkdirp build", "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", - "i18n:src": "babel src > tmp.js && rimraf tmp.js && ./scripts/build-i18n-source.js ./translations/messages/ ./translations/", + "i18n:src": "babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/ ./translations/", "start": "webpack-dev-server", "test": "npm run test:lint && npm run test:unit && NODE_ENV=production npm run build && npm run test:integration", "test:integration": "jest --runInBand test[\\\\/]integration", @@ -28,11 +28,9 @@ }, "devDependencies": { "autoprefixer": "^7.1.3", - "babel-cli": "^6.26.0", "babel-core": "^6.23.1", "babel-eslint": "^8.0.1", "babel-loader": "^7.1.0", - "babel-plugin-react-intl": "^2.3.1", "babel-plugin-syntax-dynamic-import": "^6.18.0", "babel-plugin-transform-async-to-generator": "^6.24.1", "babel-plugin-transform-object-rest-spread": "^6.22.0", From caddc0e462782c23aaec456a6e9f5d0517e05d14 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 13 Nov 2017 08:57:45 -0500 Subject: [PATCH 0197/1971] Merge pull request #871 from paulkaplan/ask-answer-events Wire up ask/answer components to the stage --- packages/scratch-gui/src/containers/stage.jsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 1de93d23d3..fcff811bff 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -24,12 +24,14 @@ class Stage extends React.Component { 'cancelMouseDownTimeout', 'detachMouseEvents', 'handleDoubleClick', + 'handleQuestionAnswered', 'onMouseUp', 'onMouseMove', 'onMouseDown', 'onStartDrag', 'onStopDrag', 'updateRect', + 'questionListener', 'setCanvas' ]); this.state = { @@ -38,7 +40,8 @@ class Stage extends React.Component { isDragging: false, dragOffset: null, dragId: null, - colorInfo: null + colorInfo: null, + question: null }; } componentDidMount () { @@ -47,12 +50,14 @@ class Stage extends React.Component { this.updateRect(); this.renderer = new Renderer(this.canvas); this.props.vm.attachRenderer(this.renderer); + this.props.vm.runtime.addListener('QUESTION', this.questionListener); } shouldComponentUpdate (nextProps, nextState) { return this.props.width !== nextProps.width || this.props.height !== nextProps.height || this.props.isColorPicking !== nextProps.isColorPicking || - this.state.colorInfo !== nextState.colorInfo; + this.state.colorInfo !== nextState.colorInfo || + this.state.question !== nextState.question; } componentDidUpdate (prevProps) { if (this.props.isColorPicking && !prevProps.isColorPicking) { @@ -66,6 +71,14 @@ class Stage extends React.Component { this.detachRectEvents(); this.stopColorPickingLoop(); } + questionListener (question) { + this.setState({question: question}); + } + handleQuestionAnswered (answer) { + this.setState({question: null}, () => { + this.props.vm.runtime.emit('ANSWER', answer); + }); + } startColorPickingLoop () { this.intervalId = setInterval(() => { this.setState({colorInfo: this.getColorInfo(this.pickX, this.pickY)}); @@ -251,7 +264,9 @@ class Stage extends React.Component { ); From cbd18fb93dd6199a1a851166d06ffd0a6493120e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 13 Nov 2017 10:10:53 -0500 Subject: [PATCH 0198/1971] Merge pull request #853 from quachtina96/scratchlogo Add alternate text for Scratch logo. --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 661153f8db..448daa29da 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -18,6 +18,7 @@ const MenuBar = function MenuBar () { >
Scratch From c5a98d740e1bee29ee1b86b7020a4c51816b2dbf Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 13 Nov 2017 10:41:35 -0500 Subject: [PATCH 0199/1971] Merge pull request #889 from paulkaplan/update-react-16-1-0 Update all react 16.1.0 related dependencies together --- packages/scratch-gui/package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 09d176b74c..8a2e71f916 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -67,18 +67,18 @@ "prop-types": "^15.5.10", "raf": "^3.4.0", "raw-loader": "0.5.1", - "react": "16.0.0", + "react": "16.1.0", "react-contextmenu": "2.8.0", - "react-dom": "16.0.0", + "react-dom": "16.1.0", "react-draggable": "3.0.3", "react-intl": "2.4.0", - "react-intl-redux": "0.6.0", - "react-modal": "3.0.2", + "react-intl-redux": "0.7.0", + "react-modal": "3.1.2", "react-redux": "5.0.6", "react-responsive": "3.0.0", "react-style-proptype": "3.1.0", "react-tabs": "2.1.0", - "react-test-renderer": "16.0.0", + "react-test-renderer": "16.1.0", "redux": "3.7.0", "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", From 74fe984622e2b9f2a280eca281f7c108dd519de1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 20 Nov 2017 09:38:02 -0500 Subject: [PATCH 0200/1971] Merge pull request #917 from LLK/greenkeeper/eslint-plugin-react-7.5.1 chore(package): update eslint-plugin-react to version 7.5.1 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8a2e71f916..545a8d0082 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -46,7 +46,7 @@ "eslint": "^4.7.1", "eslint-config-scratch": "^5.0.0", "eslint-plugin-import": "^2.7.0", - "eslint-plugin-react": "^7.2.1", + "eslint-plugin-react": "^7.5.1", "file-loader": "1.1.5", "get-float-time-domain-data": "0.1.0", "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", From db5b36b27eaae6f17425677ca6f330a20033a8ec Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 20 Nov 2017 11:39:48 -0500 Subject: [PATCH 0201/1971] Merge pull request #899 from fsih/workspaceTargetUpdate Refresh workspace with sprite-specific blocks when sprite changes --- .../scratch-gui/src/containers/blocks.jsx | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 471dc6cd08..f3e46dfaf2 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -172,6 +172,11 @@ class Blocks extends React.Component { this.workspace.reportValue(data.id, data.value); } onWorkspaceUpdate (data) { + // When we change sprites, update the toolbox to have the new sprite's blocks + if (this.props.vm.editingTarget) { + this.props.updateToolboxState(makeToolboxXML(this.props.vm.editingTarget.id)); + } + if (this.props.vm.editingTarget && !this.state.workspaceMetrics[this.props.vm.editingTarget.id]) { this.onWorkspaceMetricsChange(); } @@ -179,6 +184,8 @@ class Blocks extends React.Component { // Remove and reattach the workspace listener (but allow flyout events) this.workspace.removeChangeListener(this.props.vm.blockListener); const dom = this.ScratchBlocks.Xml.textToDom(data.xml); + // @todo This line rerenders toolbox, and the change in the toolbox XML also rerenders the toolbox. + // We should only rerender the toolbox once. See https://github.com/LLK/scratch-gui/issues/901 this.ScratchBlocks.Xml.clearWorkspaceAndLoadFromXml(dom, this.workspace); this.workspace.addChangeListener(this.props.vm.blockListener); @@ -193,8 +200,8 @@ class Blocks extends React.Component { handleExtensionAdded (blocksInfo) { this.ScratchBlocks.defineBlocksWithJsonArray(blocksInfo.map(blockInfo => blockInfo.json)); const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(); - const toolboxXML = makeToolboxXML(dynamicBlocksXML); - this.props.onExtensionAdded(toolboxXML); + const toolboxXML = makeToolboxXML(this.props.vm.editingTarget.id, dynamicBlocksXML); + this.props.updateToolboxState(toolboxXML); } handleCategorySelected (categoryName) { this.workspace.toolbox_.setSelectedCategoryByName(categoryName); @@ -220,7 +227,7 @@ class Blocks extends React.Component { vm, isVisible, onActivateColorPicker, - onExtensionAdded, + updateToolboxState, onRequestCloseExtensionLibrary, toolboxXML, ...props @@ -257,7 +264,6 @@ Blocks.propTypes = { extensionLibraryVisible: PropTypes.bool, isVisible: PropTypes.bool, onActivateColorPicker: PropTypes.func, - onExtensionAdded: PropTypes.func, onRequestCloseExtensionLibrary: PropTypes.func, options: PropTypes.shape({ media: PropTypes.string, @@ -281,6 +287,7 @@ Blocks.propTypes = { comments: PropTypes.bool }), toolboxXML: PropTypes.string, + updateToolboxState: PropTypes.func, vm: PropTypes.instanceOf(VM).isRequired }; @@ -322,11 +329,11 @@ const mapStateToProps = state => ({ const mapDispatchToProps = dispatch => ({ onActivateColorPicker: callback => dispatch(activateColorPicker(callback)), - onExtensionAdded: toolboxXML => { - dispatch(updateToolbox(toolboxXML)); - }, onRequestCloseExtensionLibrary: () => { dispatch(closeExtensionLibrary()); + }, + updateToolboxState: toolboxXML => { + dispatch(updateToolbox(toolboxXML)); } }); From 28045053fb15d18df57b87e26c167b693d280374 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 20 Nov 2017 15:32:47 -0500 Subject: [PATCH 0202/1971] Merge pull request #912 from LLK/greenkeeper/react-tabs-2.1.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-tabs to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 545a8d0082..b7961a3190 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -77,7 +77,7 @@ "react-redux": "5.0.6", "react-responsive": "3.0.0", "react-style-proptype": "3.1.0", - "react-tabs": "2.1.0", + "react-tabs": "2.1.1", "react-test-renderer": "16.1.0", "redux": "3.7.0", "redux-mock-store": "^1.2.3", From 346b8cfe352aac653fb1c6ce26f22b17846b10ee Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 20 Nov 2017 15:33:16 -0500 Subject: [PATCH 0203/1971] Merge pull request #897 from LLK/greenkeeper/enzyme-adapter-react-16-1.1.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update enzyme-adapter-react-16 to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b7961a3190..2bce6db471 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -42,7 +42,7 @@ "copy-webpack-plugin": "^4.0.1", "css-loader": "^0.28.7", "enzyme": "^3.1.0", - "enzyme-adapter-react-16": "1.0.4", + "enzyme-adapter-react-16": "1.1.0", "eslint": "^4.7.1", "eslint-config-scratch": "^5.0.0", "eslint-plugin-import": "^2.7.0", From 4173085470baab43619e7c009fa92bdadd9750ce Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 20 Nov 2017 15:33:48 -0500 Subject: [PATCH 0204/1971] Merge pull request #893 from LLK/greenkeeper/react-dom-16.1.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-dom to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2bce6db471..8086cdfeec 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -69,7 +69,7 @@ "raw-loader": "0.5.1", "react": "16.1.0", "react-contextmenu": "2.8.0", - "react-dom": "16.1.0", + "react-dom": "16.1.1", "react-draggable": "3.0.3", "react-intl": "2.4.0", "react-intl-redux": "0.7.0", From b9be6c6218560192a2a36ba0c4048d10569aeae6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 20 Nov 2017 15:34:16 -0500 Subject: [PATCH 0205/1971] Merge pull request #892 from LLK/greenkeeper/react-16.1.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8086cdfeec..a23959a463 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -67,7 +67,7 @@ "prop-types": "^15.5.10", "raf": "^3.4.0", "raw-loader": "0.5.1", - "react": "16.1.0", + "react": "16.1.1", "react-contextmenu": "2.8.0", "react-dom": "16.1.1", "react-draggable": "3.0.3", From 8f0334eac4bdc2d7d962b35d1998b580883cd482 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 20 Nov 2017 16:02:10 -0500 Subject: [PATCH 0206/1971] Merge pull request #891 from LLK/greenkeeper/react-test-renderer-16.1.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-test-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a23959a463..3ed372dc93 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -78,7 +78,7 @@ "react-responsive": "3.0.0", "react-style-proptype": "3.1.0", "react-tabs": "2.1.1", - "react-test-renderer": "16.1.0", + "react-test-renderer": "16.1.1", "redux": "3.7.0", "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", From 8164f20ec94b3d45d4d480c30fe080155baac655 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 20 Nov 2017 16:02:35 -0500 Subject: [PATCH 0207/1971] Merge pull request #898 from LLK/greenkeeper/react-responsive-4.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-responsive to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3ed372dc93..ced7e709f2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -75,7 +75,7 @@ "react-intl-redux": "0.7.0", "react-modal": "3.1.2", "react-redux": "5.0.6", - "react-responsive": "3.0.0", + "react-responsive": "4.0.0", "react-style-proptype": "3.1.0", "react-tabs": "2.1.1", "react-test-renderer": "16.1.1", From 1ad246e2d24ce69613671216aa1239b7a0ba0e1d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 21 Nov 2017 10:58:54 -0500 Subject: [PATCH 0208/1971] Merge pull request #931 from paulkaplan/target-specific-palette Target specific palette --- packages/scratch-gui/src/containers/blocks.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index f3e46dfaf2..47d12661cf 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -174,7 +174,8 @@ class Blocks extends React.Component { onWorkspaceUpdate (data) { // When we change sprites, update the toolbox to have the new sprite's blocks if (this.props.vm.editingTarget) { - this.props.updateToolboxState(makeToolboxXML(this.props.vm.editingTarget.id)); + const target = this.props.vm.editingTarget; + this.props.updateToolboxState(makeToolboxXML(target.isStage, target.id)); } if (this.props.vm.editingTarget && !this.state.workspaceMetrics[this.props.vm.editingTarget.id]) { @@ -200,7 +201,8 @@ class Blocks extends React.Component { handleExtensionAdded (blocksInfo) { this.ScratchBlocks.defineBlocksWithJsonArray(blocksInfo.map(blockInfo => blockInfo.json)); const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(); - const toolboxXML = makeToolboxXML(this.props.vm.editingTarget.id, dynamicBlocksXML); + const target = this.props.vm.editingTarget; + const toolboxXML = makeToolboxXML(target.isStage, target.id, dynamicBlocksXML); this.props.updateToolboxState(toolboxXML); } handleCategorySelected (categoryName) { From 2f6e962a1c7b6faab702b1204a3918e6d13cd3e8 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 23 Nov 2017 11:17:12 -0500 Subject: [PATCH 0209/1971] Merge pull request #943 from LLK/greenkeeper/copy-webpack-plugin-4.2.3 chore(package): update copy-webpack-plugin to version 4.2.3 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ced7e709f2..a834440414 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -39,7 +39,7 @@ "buffer-loader": "0.0.1", "chromedriver": "2.33.1", "classnames": "2.2.5", - "copy-webpack-plugin": "^4.0.1", + "copy-webpack-plugin": "^4.2.3", "css-loader": "^0.28.7", "enzyme": "^3.1.0", "enzyme-adapter-react-16": "1.1.0", From 0db25c8a03d6d8e91cd74d8b55aadcbb3a2ae7c2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 27 Nov 2017 09:01:17 -0500 Subject: [PATCH 0210/1971] Merge pull request #938 from paulkaplan/fix-toolbox-extension-loading Fix extensions disappearing when switching sprites --- packages/scratch-gui/src/containers/blocks.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 47d12661cf..c1a7a2f60e 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -175,7 +175,9 @@ class Blocks extends React.Component { // When we change sprites, update the toolbox to have the new sprite's blocks if (this.props.vm.editingTarget) { const target = this.props.vm.editingTarget; - this.props.updateToolboxState(makeToolboxXML(target.isStage, target.id)); + const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(); + const toolboxXML = makeToolboxXML(target.isStage, target.id, dynamicBlocksXML); + this.props.updateToolboxState(toolboxXML); } if (this.props.vm.editingTarget && !this.state.workspaceMetrics[this.props.vm.editingTarget.id]) { From d25d58d84ea2b0d7bcd349ad781f9d554e0f75a5 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Mon, 27 Nov 2017 10:52:28 -0500 Subject: [PATCH 0211/1971] Merge pull request #950 from chrisgarrity/revert-react-intl-redux Revert breaking change version update --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a834440414..da25aa851f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -72,7 +72,7 @@ "react-dom": "16.1.1", "react-draggable": "3.0.3", "react-intl": "2.4.0", - "react-intl-redux": "0.7.0", + "react-intl-redux": "0.6.0", "react-modal": "3.1.2", "react-redux": "5.0.6", "react-responsive": "4.0.0", From bc3f79e3739c7079a6e5ad2f322d8c0bb138a37e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 5 Dec 2017 15:37:30 -0500 Subject: [PATCH 0212/1971] Merge pull request #992 from paulkaplan/custom-procedures First iteration of procedures modal with block workspace --- .../scratch-gui/src/containers/blocks.jsx | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index c1a7a2f60e..895c1af58c 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -9,11 +9,13 @@ import VM from 'scratch-vm'; import Prompt from './prompt.jsx'; import BlocksComponent from '../components/blocks/blocks.jsx'; import ExtensionLibrary from './extension-library.jsx'; +import CustomProcedures from './custom-procedures.jsx'; import {connect} from 'react-redux'; import {updateToolbox} from '../reducers/toolbox'; import {activateColorPicker} from '../reducers/color-picker'; import {closeExtensionLibrary} from '../reducers/modals'; +import {activateCustomProcedures, deactivateCustomProcedures} from '../reducers/custom-procedures'; const addFunctionListener = (object, property, callback) => { const oldFn = object[property]; @@ -35,6 +37,7 @@ class Blocks extends React.Component { 'handlePromptStart', 'handlePromptCallback', 'handlePromptClose', + 'handleCustomProceduresClose', 'onScriptGlowOn', 'onScriptGlowOff', 'onBlockGlowOn', @@ -55,6 +58,7 @@ class Blocks extends React.Component { } componentDidMount () { this.ScratchBlocks.FieldColourSlider.activateEyedropper_ = this.props.onActivateColorPicker; + this.ScratchBlocks.Procedures.externalProcedureDefCallback = this.props.onActivateCustomProcedures; const workspaceConfig = defaultsDeep({}, Blocks.defaultOptions, @@ -74,7 +78,8 @@ class Blocks extends React.Component { this.state.prompt !== nextState.prompt || this.props.isVisible !== nextProps.isVisible || this.props.toolboxXML !== nextProps.toolboxXML || - this.props.extensionLibraryVisible !== nextProps.extensionLibraryVisible + this.props.extensionLibraryVisible !== nextProps.extensionLibraryVisible || + this.props.customProceduresVisible !== nextProps.customProceduresVisible ); } componentDidUpdate (prevProps) { @@ -223,16 +228,23 @@ class Blocks extends React.Component { handlePromptClose () { this.setState({prompt: null}); } + handleCustomProceduresClose (data) { + this.props.onRequestCloseCustomProcedures(data); + this.workspace.refreshToolboxSelection_(); + } render () { /* eslint-disable no-unused-vars */ const { + customProceduresVisible, extensionLibraryVisible, options, vm, isVisible, onActivateColorPicker, updateToolboxState, + onActivateCustomProcedures, onRequestCloseExtensionLibrary, + onRequestCloseCustomProcedures, toolboxXML, ...props } = this.props; @@ -259,15 +271,26 @@ class Blocks extends React.Component { onRequestClose={onRequestCloseExtensionLibrary} /> ) : null} + {customProceduresVisible ? ( + + ) : null}
); } } Blocks.propTypes = { + customProceduresVisible: PropTypes.bool, extensionLibraryVisible: PropTypes.bool, isVisible: PropTypes.bool, onActivateColorPicker: PropTypes.func, + onActivateCustomProcedures: PropTypes.func, + onRequestCloseCustomProcedures: PropTypes.func, onRequestCloseExtensionLibrary: PropTypes.func, options: PropTypes.shape({ media: PropTypes.string, @@ -328,14 +351,19 @@ Blocks.defaultProps = { const mapStateToProps = state => ({ extensionLibraryVisible: state.modals.extensionLibrary, - toolboxXML: state.toolbox.toolboxXML + toolboxXML: state.toolbox.toolboxXML, + customProceduresVisible: state.customProcedures.active }); const mapDispatchToProps = dispatch => ({ onActivateColorPicker: callback => dispatch(activateColorPicker(callback)), + onActivateCustomProcedures: (data, callback) => dispatch(activateCustomProcedures(data, callback)), onRequestCloseExtensionLibrary: () => { dispatch(closeExtensionLibrary()); }, + onRequestCloseCustomProcedures: data => { + dispatch(deactivateCustomProcedures(data)); + }, updateToolboxState: toolboxXML => { dispatch(updateToolbox(toolboxXML)); } From 2b8cbab10c5e8bb6a68e2253b2929ca3e8c7bb33 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 6 Dec 2017 10:52:59 -0500 Subject: [PATCH 0213/1971] Merge pull request #1003 from paulkaplan/deps-update 12/6/17 greenkeeper updates to react 16.2 related things --- packages/scratch-gui/package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index da25aa851f..9661a9bfb7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -67,18 +67,18 @@ "prop-types": "^15.5.10", "raf": "^3.4.0", "raw-loader": "0.5.1", - "react": "16.1.1", - "react-contextmenu": "2.8.0", - "react-dom": "16.1.1", - "react-draggable": "3.0.3", + "react": "16.2.0", + "react-contextmenu": "2.9.1", + "react-dom": "16.2.0", + "react-draggable": "3.0.4", "react-intl": "2.4.0", "react-intl-redux": "0.6.0", - "react-modal": "3.1.2", + "react-modal": "3.1.7", "react-redux": "5.0.6", - "react-responsive": "4.0.0", + "react-responsive": "4.0.3", "react-style-proptype": "3.1.0", "react-tabs": "2.1.1", - "react-test-renderer": "16.1.1", + "react-test-renderer": "16.2.0", "redux": "3.7.0", "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", From a04e744330f981968f2ad2f4c56cf306bf4871f8 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 8 Dec 2017 10:25:19 -0500 Subject: [PATCH 0214/1971] Merge pull request #1012 from LLK/greenkeeper/webpack-dev-server-2.9.7 chore(package): update webpack-dev-server to version 2.9.7 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9661a9bfb7..10196c0186 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "wav-encoder": "1.3.0", "web-audio-test-api": "^0.5.2", "webpack": "^3.6.0", - "webpack-dev-server": "^2.8.2", + "webpack-dev-server": "^2.9.7", "xhr": "2.4.0" }, "jest": { From e3608dd481bcc04897a843c7901027e19f6f97e6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 13 Dec 2017 16:41:34 -0500 Subject: [PATCH 0215/1971] Merge pull request #1029 from LLK/greenkeeper/react-modal-3.1.8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-modal to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 10196c0186..53740d574f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -73,7 +73,7 @@ "react-draggable": "3.0.4", "react-intl": "2.4.0", "react-intl-redux": "0.6.0", - "react-modal": "3.1.7", + "react-modal": "3.1.8", "react-redux": "5.0.6", "react-responsive": "4.0.3", "react-style-proptype": "3.1.0", From 7db3f321d44e0a7cb5705efabb1a6c9bcdd26b64 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 14 Dec 2017 09:38:17 -0500 Subject: [PATCH 0216/1971] Merge pull request #667 from josiahneuberger/add-full-screen-render-view Add full screen render view --- packages/scratch-gui/src/containers/stage.jsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index fcff811bff..72ed404d41 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -57,6 +57,7 @@ class Stage extends React.Component { this.props.height !== nextProps.height || this.props.isColorPicking !== nextProps.isColorPicking || this.state.colorInfo !== nextState.colorInfo || + this.props.isZoomed !== nextProps.isZoomed || this.state.question !== nextState.question; } componentDidUpdate (prevProps) { @@ -65,6 +66,8 @@ class Stage extends React.Component { } else if (!this.props.isColorPicking && prevProps.isColorPicking) { this.stopColorPickingLoop(); } + this.updateRect(); + this.renderer.resize(this.rect.width, this.rect.height); } componentWillUnmount () { this.detachMouseEvents(this.canvas); @@ -276,6 +279,7 @@ class Stage extends React.Component { Stage.propTypes = { height: PropTypes.number, isColorPicking: PropTypes.bool, + isZoomed: PropTypes.bool, onActivateColorPicker: PropTypes.func, onDeactivateColorPicker: PropTypes.func, vm: PropTypes.instanceOf(VM).isRequired, @@ -283,7 +287,8 @@ Stage.propTypes = { }; const mapStateToProps = state => ({ - isColorPicking: state.colorPicker.active + isColorPicking: state.colorPicker.active, + isZoomed: state.isZoomed }); const mapDispatchToProps = dispatch => ({ From 563d2cbcb3f81092c8492a2371ba1c639db997d6 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 14 Dec 2017 09:48:30 -0500 Subject: [PATCH 0217/1971] Merge pull request #1035 from LLK/greenkeeper/copy-webpack-plugin-4.3.0 chore(package): update copy-webpack-plugin to version 4.3.0 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 53740d574f..2dba94108e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -39,7 +39,7 @@ "buffer-loader": "0.0.1", "chromedriver": "2.33.1", "classnames": "2.2.5", - "copy-webpack-plugin": "^4.2.3", + "copy-webpack-plugin": "^4.3.0", "css-loader": "^0.28.7", "enzyme": "^3.1.0", "enzyme-adapter-react-16": "1.1.0", From 5b17c3083263a98f2683956f97d7929f59ea01fb Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 14 Dec 2017 12:07:22 -0500 Subject: [PATCH 0218/1971] Merge pull request #1034 from LLK/greenkeeper/xhr-2.4.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update xhr to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2dba94108e..d3fff0c957 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -99,7 +99,7 @@ "web-audio-test-api": "^0.5.2", "webpack": "^3.6.0", "webpack-dev-server": "^2.9.7", - "xhr": "2.4.0" + "xhr": "2.4.1" }, "jest": { "setupFiles": [ From 38263e3ae6429f568d7bbdd18a1e82f0ede708e9 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 14 Dec 2017 15:53:18 -0500 Subject: [PATCH 0219/1971] Localize extension blocks (#968) Localizes the pen extension: - loads all pen locale information - may want to consider dynamically loading locales later. --- packages/scratch-gui/package.json | 2 +- .../scratch-gui/src/containers/blocks.jsx | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d3fff0c957..34d4d64c4a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -85,7 +85,7 @@ "rimraf": "^2.6.1", "scratch-audio": "latest", "scratch-blocks": "latest", - "scratch-l10n": "^2.0.0", + "scratch-l10n": "latest", "scratch-paint": "latest", "scratch-render": "latest", "scratch-storage": "^0.3.0", diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 895c1af58c..647f1f8ba4 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -43,6 +43,7 @@ class Blocks extends React.Component { 'onBlockGlowOn', 'onBlockGlowOff', 'handleExtensionAdded', + 'handleBlocksInfoUpdate', 'onTargetsUpdate', 'onVisualReport', 'onWorkspaceUpdate', @@ -72,6 +73,7 @@ class Blocks extends React.Component { addFunctionListener(this.workspace, 'zoom', this.onWorkspaceMetricsChange); this.attachVM(); + this.props.vm.setLocale(this.props.locale, this.props.messages); } shouldComponentUpdate (nextProps, nextState) { return ( @@ -79,10 +81,15 @@ class Blocks extends React.Component { this.props.isVisible !== nextProps.isVisible || this.props.toolboxXML !== nextProps.toolboxXML || this.props.extensionLibraryVisible !== nextProps.extensionLibraryVisible || - this.props.customProceduresVisible !== nextProps.customProceduresVisible + this.props.customProceduresVisible !== nextProps.customProceduresVisible || + this.props.locale !== nextProps.locale ); } componentDidUpdate (prevProps) { + if (prevProps.locale !== this.props.locale) { + this.props.vm.setLocale(this.props.locale, this.props.messages); + } + if (prevProps.toolboxXML !== this.props.toolboxXML) { const selectedCategoryName = this.workspace.toolbox_.getSelectedItem().name_; this.workspace.updateToolbox(this.props.toolboxXML); @@ -120,6 +127,7 @@ class Blocks extends React.Component { this.props.vm.addListener('workspaceUpdate', this.onWorkspaceUpdate); this.props.vm.addListener('targetsUpdate', this.onTargetsUpdate); this.props.vm.addListener('EXTENSION_ADDED', this.handleExtensionAdded); + this.props.vm.addListener('BLOCKSINFO_UPDATE', this.handleBlocksInfoUpdate); } detachVM () { this.props.vm.removeListener('SCRIPT_GLOW_ON', this.onScriptGlowOn); @@ -130,6 +138,7 @@ class Blocks extends React.Component { this.props.vm.removeListener('workspaceUpdate', this.onWorkspaceUpdate); this.props.vm.removeListener('targetsUpdate', this.onTargetsUpdate); this.props.vm.removeListener('EXTENSION_ADDED', this.handleExtensionAdded); + this.props.vm.removeListener('BLOCKSINFO_UPDATE', this.handleBlocksInfoUpdate); } updateToolboxBlockValue (id, value) { const block = this.workspace @@ -212,6 +221,10 @@ class Blocks extends React.Component { const toolboxXML = makeToolboxXML(target.isStage, target.id, dynamicBlocksXML); this.props.updateToolboxState(toolboxXML); } + handleBlocksInfoUpdate (blocksInfo) { + // @todo Later we should replace this to avoid all the warnings from redefining blocks. + this.handleExtensionAdded(blocksInfo); + } handleCategorySelected (categoryName) { this.workspace.toolbox_.setSelectedCategoryByName(categoryName); } @@ -288,6 +301,8 @@ Blocks.propTypes = { customProceduresVisible: PropTypes.bool, extensionLibraryVisible: PropTypes.bool, isVisible: PropTypes.bool, + locale: PropTypes.string, + messages: PropTypes.objectOf(PropTypes.string), onActivateColorPicker: PropTypes.func, onActivateCustomProcedures: PropTypes.func, onRequestCloseCustomProcedures: PropTypes.func, @@ -351,6 +366,8 @@ Blocks.defaultProps = { const mapStateToProps = state => ({ extensionLibraryVisible: state.modals.extensionLibrary, + locale: state.intl.locale, + messages: state.intl.messages, toolboxXML: state.toolbox.toolboxXML, customProceduresVisible: state.customProcedures.active }); From 878e4f374e00e20e3bf7c80b73ffcf5a4a93458c Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 18 Dec 2017 09:59:47 -0500 Subject: [PATCH 0220/1971] Merge pull request #1057 from LLK/greenkeeper/file-loader-1.1.6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update file-loader to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 34d4d64c4a..db83082f69 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -47,7 +47,7 @@ "eslint-config-scratch": "^5.0.0", "eslint-plugin-import": "^2.7.0", "eslint-plugin-react": "^7.5.1", - "file-loader": "1.1.5", + "file-loader": "1.1.6", "get-float-time-domain-data": "0.1.0", "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", "html-webpack-plugin": "^2.30.0", From 8b1c34d11ae2cfc0deb8d6905c4645a54bd42dbb Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 18 Dec 2017 14:49:32 -0500 Subject: [PATCH 0221/1971] Merge pull request #1062 from LLK/greenkeeper/chromedriver-2.34.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update chromedriver to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index db83082f69..bdd4f6b5af 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -37,7 +37,7 @@ "babel-preset-es2015": "^6.22.0", "babel-preset-react": "^6.22.0", "buffer-loader": "0.0.1", - "chromedriver": "2.33.1", + "chromedriver": "2.34.0", "classnames": "2.2.5", "copy-webpack-plugin": "^4.3.0", "css-loader": "^0.28.7", From bf83e9eb4356dfd52e80058dfbac5ca1f0a1684d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 19 Dec 2017 09:58:29 -0500 Subject: [PATCH 0222/1971] Merge pull request #1068 from paulkaplan/lock-deps Lock scratch-* dependencies to their current versions --- packages/scratch-gui/package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index bdd4f6b5af..7e6fdd4bad 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -83,13 +83,13 @@ "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "latest", - "scratch-blocks": "latest", - "scratch-l10n": "latest", - "scratch-paint": "latest", - "scratch-render": "latest", - "scratch-storage": "^0.3.0", - "scratch-vm": "latest", + "scratch-audio": "0.1.0-prerelease.1511362862", + "scratch-blocks": "0.1.0-prerelease.1513627896", + "scratch-l10n": "2.0.20171211145754", + "scratch-paint": "0.1.0-prerelease.20171215160852", + "scratch-render": "0.1.0-prerelease.1513022270", + "scratch-storage": "0.3.0", + "scratch-vm": "0.1.0-prerelease.1513627112-prerelease.1513627124", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.19.0", From 2211535e78689f50b3f87e1df6c1e27bafcde4ab Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 19 Dec 2017 11:15:30 -0500 Subject: [PATCH 0223/1971] Merge pull request #1043 from paulkaplan/fix-touch-handling Fix touch actions on the stage --- packages/scratch-gui/src/containers/stage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 72ed404d41..9f76aa7ab1 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -195,7 +195,7 @@ class Stage extends React.Component { this.updateRect(); const {x, y} = getEventXY(e); const mousePosition = [x - this.rect.left, y - this.rect.top]; - if (e.button === 0) { + if (e.button === 0 || e instanceof TouchEvent) { this.setState({ mouseDown: true, mouseDownPosition: mousePosition, From b03e4b5d8e9400129b585424be71fcb0c496e5a5 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 19 Dec 2017 13:29:39 -0500 Subject: [PATCH 0224/1971] Merge pull request #1071 from paulkaplan/update-paint Update paint dependency to get hotfix for eyedropper --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7e6fdd4bad..5fb0bcc1cb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -86,7 +86,7 @@ "scratch-audio": "0.1.0-prerelease.1511362862", "scratch-blocks": "0.1.0-prerelease.1513627896", "scratch-l10n": "2.0.20171211145754", - "scratch-paint": "0.1.0-prerelease.20171215160852", + "scratch-paint": "0.1.0-prerelease.20171219170526", "scratch-render": "0.1.0-prerelease.1513022270", "scratch-storage": "0.3.0", "scratch-vm": "0.1.0-prerelease.1513627112-prerelease.1513627124", From d64be732c97ee825bd26cd0663d40166ac4b35a1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 19 Dec 2017 16:52:11 -0500 Subject: [PATCH 0225/1971] Merge pull request #1079 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1513719016 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.151… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5fb0bcc1cb..3f32343ec0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -84,7 +84,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1511362862", - "scratch-blocks": "0.1.0-prerelease.1513627896", + "scratch-blocks": "0.1.0-prerelease.1513719016", "scratch-l10n": "2.0.20171211145754", "scratch-paint": "0.1.0-prerelease.20171219170526", "scratch-render": "0.1.0-prerelease.1513022270", From beed0605c1265579ed3402e93a9e7e3bff450e41 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 20 Dec 2017 10:39:52 -0500 Subject: [PATCH 0226/1971] Merge pull request #1083 from thisandagain/bugfix/1082 Disable the 'collapse' option in the blocks workspace --- packages/scratch-gui/src/containers/blocks.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 647f1f8ba4..808bb215df 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -326,7 +326,8 @@ Blocks.propTypes = { fieldShadow: PropTypes.string, dragShadowOpacity: PropTypes.number }), - comments: PropTypes.bool + comments: PropTypes.bool, + collapse: PropTypes.bool }), toolboxXML: PropTypes.string, updateToolboxState: PropTypes.func, @@ -356,7 +357,8 @@ Blocks.defaultOptions = { fieldShadow: 'rgba(255, 255, 255, 0.3)', dragShadowOpacity: 0.6 }, - comments: false + comments: false, + collapse: false }; Blocks.defaultProps = { From f25a71ce0db2e45b54199d35c0336d6d0b53d094 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 20 Dec 2017 11:22:05 -0500 Subject: [PATCH 0227/1971] Merge pull request #1078 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1513719220-prerelease.1513719234 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.1.0-prerelease.1513719… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3f32343ec0..aff018a91e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "scratch-paint": "0.1.0-prerelease.20171219170526", "scratch-render": "0.1.0-prerelease.1513022270", "scratch-storage": "0.3.0", - "scratch-vm": "0.1.0-prerelease.1513627112-prerelease.1513627124", + "scratch-vm": "0.1.0-prerelease.1513719220-prerelease.1513719234", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.19.0", From 6d0c75cacb6c4895660fab673b36b1962f8007e4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 20 Dec 2017 11:23:19 -0500 Subject: [PATCH 0228/1971] Merge pull request #1075 from LLK/greenkeeper/react-modal-3.1.9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-modal to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index aff018a91e..c778b7c839 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -73,7 +73,7 @@ "react-draggable": "3.0.4", "react-intl": "2.4.0", "react-intl-redux": "0.6.0", - "react-modal": "3.1.8", + "react-modal": "3.1.9", "react-redux": "5.0.6", "react-responsive": "4.0.3", "react-style-proptype": "3.1.0", From 7ce35bbd3f745a5d2ce67d19df1344bd835ea7a9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 20 Dec 2017 12:18:54 -0500 Subject: [PATCH 0229/1971] Merge pull request #1085 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1513789849-prerelease.1513789862 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c778b7c839..fd1416f8e2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "scratch-paint": "0.1.0-prerelease.20171219170526", "scratch-render": "0.1.0-prerelease.1513022270", "scratch-storage": "0.3.0", - "scratch-vm": "0.1.0-prerelease.1513719220-prerelease.1513719234", + "scratch-vm": "0.1.0-prerelease.1513789849-prerelease.1513789862", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.19.0", From 558e5c1099287e7b27993892b46387a9513be4b0 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 20 Dec 2017 13:45:35 -0500 Subject: [PATCH 0230/1971] Merge pull request #1086 from paulkaplan/update-paint-editor Update to most recent paint editor release --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index fd1416f8e2..778cc2eeab 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -86,7 +86,7 @@ "scratch-audio": "0.1.0-prerelease.1511362862", "scratch-blocks": "0.1.0-prerelease.1513719016", "scratch-l10n": "2.0.20171211145754", - "scratch-paint": "0.1.0-prerelease.20171219170526", + "scratch-paint": "0.1.0-prerelease.20171220165922", "scratch-render": "0.1.0-prerelease.1513022270", "scratch-storage": "0.3.0", "scratch-vm": "0.1.0-prerelease.1513789849-prerelease.1513789862", From 3ba6ef01ed51de936534476c58d6e893644be7b9 Mon Sep 17 00:00:00 2001 From: Matthew Taylor Date: Wed, 20 Dec 2017 14:49:27 -0500 Subject: [PATCH 0231/1971] Partially Implement GH-830: Add coming soon tooltips (#1069) * Implement GH-830: Add coming soon tooltips This implements coming soon tooltips for: * the small stage size * save, load and language It also creates the component that can be copied over into the paint editor for use there as well. Lastly, this does a small refactor on the full screen implementation to include multiple small screen sizes too. --- packages/scratch-gui/package.json | 1 + packages/scratch-gui/src/containers/stage.jsx | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 778cc2eeab..50369a1f70 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -79,6 +79,7 @@ "react-style-proptype": "3.1.0", "react-tabs": "2.1.1", "react-test-renderer": "16.2.0", + "react-tooltip": "3.4.0", "redux": "3.7.0", "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 9f76aa7ab1..cb2a38963e 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -57,7 +57,7 @@ class Stage extends React.Component { this.props.height !== nextProps.height || this.props.isColorPicking !== nextProps.isColorPicking || this.state.colorInfo !== nextState.colorInfo || - this.props.isZoomed !== nextProps.isZoomed || + this.props.isFullScreen !== nextProps.isFullScreen || this.state.question !== nextState.question; } componentDidUpdate (prevProps) { @@ -279,7 +279,7 @@ class Stage extends React.Component { Stage.propTypes = { height: PropTypes.number, isColorPicking: PropTypes.bool, - isZoomed: PropTypes.bool, + isFullScreen: PropTypes.bool.isRequired, onActivateColorPicker: PropTypes.func, onDeactivateColorPicker: PropTypes.func, vm: PropTypes.instanceOf(VM).isRequired, @@ -288,7 +288,7 @@ Stage.propTypes = { const mapStateToProps = state => ({ isColorPicking: state.colorPicker.active, - isZoomed: state.isZoomed + isFullScreen: state.stageSize.isFullScreen }); const mapDispatchToProps = dispatch => ({ From 7ff3b48e34b9b0fac0d5f105c584f0a556d28016 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 20 Dec 2017 15:47:21 -0500 Subject: [PATCH 0232/1971] Merge pull request #1088 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1513799362-prerelease.1513799374 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 50369a1f70..f5c94432c5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -90,7 +90,7 @@ "scratch-paint": "0.1.0-prerelease.20171220165922", "scratch-render": "0.1.0-prerelease.1513022270", "scratch-storage": "0.3.0", - "scratch-vm": "0.1.0-prerelease.1513789849-prerelease.1513789862", + "scratch-vm": "0.1.0-prerelease.1513799362-prerelease.1513799374", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.19.0", From 352e723324242411425abb34d3781ee533c77241 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 20 Dec 2017 16:27:43 -0500 Subject: [PATCH 0233/1971] Merge pull request #1090 from LLK/greenkeeper/scratch-audio-0.1.0-prerelease.1513803406 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-audio to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f5c94432c5..77e66906ed 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -84,7 +84,7 @@ "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "0.1.0-prerelease.1511362862", + "scratch-audio": "0.1.0-prerelease.1513803406", "scratch-blocks": "0.1.0-prerelease.1513719016", "scratch-l10n": "2.0.20171211145754", "scratch-paint": "0.1.0-prerelease.20171220165922", From 38115aabb369969e43aba0f5ff952a5ead0c9c89 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 20 Dec 2017 19:23:03 -0500 Subject: [PATCH 0234/1971] Merge pull request #1095 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.1513807417 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 77e66906ed..f10b6df7c9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -88,7 +88,7 @@ "scratch-blocks": "0.1.0-prerelease.1513719016", "scratch-l10n": "2.0.20171211145754", "scratch-paint": "0.1.0-prerelease.20171220165922", - "scratch-render": "0.1.0-prerelease.1513022270", + "scratch-render": "0.1.0-prerelease.1513807417", "scratch-storage": "0.3.0", "scratch-vm": "0.1.0-prerelease.1513799362-prerelease.1513799374", "selenium-webdriver": "3.5.0", From 5b7ba17d1d9a97fc465b7bcecef3d39eb57a6ab3 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 20 Dec 2017 19:27:37 -0500 Subject: [PATCH 0235/1971] Merge pull request #1102 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1513813608 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f10b6df7c9..a2ce5cc5ce 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -85,7 +85,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1513803406", - "scratch-blocks": "0.1.0-prerelease.1513719016", + "scratch-blocks": "0.1.0-prerelease.1513813608", "scratch-l10n": "2.0.20171211145754", "scratch-paint": "0.1.0-prerelease.20171220165922", "scratch-render": "0.1.0-prerelease.1513807417", From 62734f415b6134ff0ca4fb3deec1f69593bb361f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 21 Dec 2017 11:49:08 -0500 Subject: [PATCH 0236/1971] Merge pull request #1106 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1513872749 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a2ce5cc5ce..e6458829d9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -85,7 +85,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1513803406", - "scratch-blocks": "0.1.0-prerelease.1513813608", + "scratch-blocks": "0.1.0-prerelease.1513872749", "scratch-l10n": "2.0.20171211145754", "scratch-paint": "0.1.0-prerelease.20171220165922", "scratch-render": "0.1.0-prerelease.1513807417", From 971354cc9765b50c2e27f92ae58b4a0f57c4eeb7 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 21 Dec 2017 15:30:56 -0500 Subject: [PATCH 0237/1971] Preview modals (#1087) - implements welcome to the preview modal, and unsupported browser modal. - will need assets updated in Jan. --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e6458829d9..0e03879a3a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -61,6 +61,7 @@ "lodash.pick": "4.4.0", "minilog": "3.1.0", "mkdirp": "^0.5.1", + "platform": "^1.3.4", "postcss-import": "^11.0.0", "postcss-loader": "^2.0.5", "postcss-simple-vars": "^4.0.0", From 0c1866acd6af1d901fbdc9e462faa432412f7dc2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 21 Dec 2017 16:23:57 -0500 Subject: [PATCH 0238/1971] Merge pull request #1113 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1513889734-prerelease.1513889749 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0e03879a3a..4e226ad87e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-paint": "0.1.0-prerelease.20171220165922", "scratch-render": "0.1.0-prerelease.1513807417", "scratch-storage": "0.3.0", - "scratch-vm": "0.1.0-prerelease.1513799362-prerelease.1513799374", + "scratch-vm": "0.1.0-prerelease.1513889734-prerelease.1513889749", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.19.0", From fdbba8118afa137955b340ecbc512e75407c1053 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 22 Dec 2017 10:46:14 -0500 Subject: [PATCH 0239/1971] Merge pull request #1117 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1513908877 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4e226ad87e..581a274595 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -86,7 +86,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1513803406", - "scratch-blocks": "0.1.0-prerelease.1513872749", + "scratch-blocks": "0.1.0-prerelease.1513908877", "scratch-l10n": "2.0.20171211145754", "scratch-paint": "0.1.0-prerelease.20171220165922", "scratch-render": "0.1.0-prerelease.1513807417", From cf59db1fed6819bc4daa7af1b44306ae0788140d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 27 Dec 2017 11:12:23 -0500 Subject: [PATCH 0240/1971] Merge pull request #1122 from sjhuang26/issue-1048-negative-zeros Modify rounding logic in flyout blocks --- packages/scratch-gui/src/containers/blocks.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 808bb215df..20ec2615c4 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -152,8 +152,8 @@ class Blocks extends React.Component { onTargetsUpdate () { if (this.props.vm.editingTarget) { ['glide', 'move', 'set'].forEach(prefix => { - this.updateToolboxBlockValue(`${prefix}x`, this.props.vm.editingTarget.x.toFixed(0)); - this.updateToolboxBlockValue(`${prefix}y`, this.props.vm.editingTarget.y.toFixed(0)); + this.updateToolboxBlockValue(`${prefix}x`, Math.round(this.props.vm.editingTarget.x).toString()); + this.updateToolboxBlockValue(`${prefix}y`, Math.round(this.props.vm.editingTarget.y).toString()); }); } } From 9506081cc5c86358c7c55a29243a4bdba8e9f921 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 27 Dec 2017 11:13:23 -0500 Subject: [PATCH 0241/1971] Merge pull request #1118 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1513958196 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 581a274595..77adf0660e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -86,7 +86,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1513803406", - "scratch-blocks": "0.1.0-prerelease.1513908877", + "scratch-blocks": "0.1.0-prerelease.1513958196", "scratch-l10n": "2.0.20171211145754", "scratch-paint": "0.1.0-prerelease.20171220165922", "scratch-render": "0.1.0-prerelease.1513807417", From 64d3abe11aedba75e0b1c9533e2163962263b127 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 27 Dec 2017 11:16:57 -0500 Subject: [PATCH 0242/1971] Merge pull request #1119 from kchadha/msg-var-modal-title Custom Titles for Variable Modals --- packages/scratch-gui/src/containers/blocks.jsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 20ec2615c4..a016391e63 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -231,8 +231,10 @@ class Blocks extends React.Component { setBlocks (blocks) { this.blocks = blocks; } - handlePromptStart (message, defaultValue, callback) { - this.setState({prompt: {callback, message, defaultValue}}); + handlePromptStart (message, defaultValue, callback, optTitle) { + const p = {prompt: {callback, message, defaultValue}}; + p.prompt.title = optTitle ? optTitle : 'New Variable'; + this.setState(p); } handlePromptCallback (data) { this.state.prompt.callback(data); @@ -272,7 +274,7 @@ class Blocks extends React.Component { From b6b129e115f086a43cb488abc237d525350393f0 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 27 Dec 2017 12:52:21 -0500 Subject: [PATCH 0243/1971] Merge pull request #1133 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1514392312 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 77adf0660e..8815817505 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -86,7 +86,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1513803406", - "scratch-blocks": "0.1.0-prerelease.1513958196", + "scratch-blocks": "0.1.0-prerelease.1514392312", "scratch-l10n": "2.0.20171211145754", "scratch-paint": "0.1.0-prerelease.20171220165922", "scratch-render": "0.1.0-prerelease.1513807417", From 03f367d7f9525453776c7d4a01369162788bb1ae Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 27 Dec 2017 12:53:20 -0500 Subject: [PATCH 0244/1971] Merge pull request #1134 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1514392948-prerelease.1514392961 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8815817505..2fd1a24cbe 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-paint": "0.1.0-prerelease.20171220165922", "scratch-render": "0.1.0-prerelease.1513807417", "scratch-storage": "0.3.0", - "scratch-vm": "0.1.0-prerelease.1513889734-prerelease.1513889749", + "scratch-vm": "0.1.0-prerelease.1514392948-prerelease.1514392961", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.19.0", From d03cda0de06e7365cc33f1ab3d649bcb8e510acf Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 27 Dec 2017 13:54:18 -0500 Subject: [PATCH 0245/1971] Merge pull request #1135 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1514398332 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2fd1a24cbe..436068885e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -86,7 +86,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1513803406", - "scratch-blocks": "0.1.0-prerelease.1514392312", + "scratch-blocks": "0.1.0-prerelease.1514398332", "scratch-l10n": "2.0.20171211145754", "scratch-paint": "0.1.0-prerelease.20171220165922", "scratch-render": "0.1.0-prerelease.1513807417", From 5d6968e12f201a2b4f23a326ff555a07118e5796 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 27 Dec 2017 14:37:07 -0500 Subject: [PATCH 0246/1971] Merge pull request #1136 from paulkaplan/update-vm Update to most recent VM for broadcast updates --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 436068885e..f451537c02 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-paint": "0.1.0-prerelease.20171220165922", "scratch-render": "0.1.0-prerelease.1513807417", "scratch-storage": "0.3.0", - "scratch-vm": "0.1.0-prerelease.1514392948-prerelease.1514392961", + "scratch-vm": "0.1.0-prerelease.1514394990-prerelease.1514395002", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.19.0", From 3db78e8aacab7af6aa746ce4e3b208a1483e5d0b Mon Sep 17 00:00:00 2001 From: Matthew Taylor Date: Wed, 27 Dec 2017 16:09:56 -0500 Subject: [PATCH 0247/1971] GH-829: Add feedback form to menu bar (#1120) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add feedback form This implements #829 * small style updates to the stage buttons from @carljbowman * remove `stageSize` for now We eventually need this, but not yet, and it’s causing lint failure. --- .../src/components/menu-bar/menu-bar.jsx | 61 ++++++++++++++++--- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 448daa29da..538455f336 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -1,21 +1,29 @@ import classNames from 'classnames'; +import {connect} from 'react-redux'; +import {FormattedMessage} from 'react-intl'; +import PropTypes from 'prop-types'; import React from 'react'; import Box from '../box/box.jsx'; +import Button from '../button/button.jsx'; import LoadButton from '../../containers/load-button.jsx'; import SaveButton from '../../containers/save-button.jsx'; import LanguageSelector from '../../containers/language-selector.jsx'; +import {openFeedbackForm} from '../../reducers/modals'; + import styles from './menu-bar.css'; + +import feedbackIcon from './icon--feedback.svg'; import scratchLogo from './scratch-logo.svg'; -const MenuBar = function MenuBar () { - return ( - +const MenuBar = props => ( + +
Scratch - - ); +
+
+ +
+ +); + +MenuBar.propTypes = { + onGiveFeedback: PropTypes.func.isRequired }; -export default MenuBar; +const mapStateToProps = () => ({}); + +const mapDispatchToProps = dispatch => ({ + onGiveFeedback: () => { + dispatch(openFeedbackForm()); + } +}); + +export default connect( + mapStateToProps, + mapDispatchToProps +)(MenuBar); From 7d063b49aef8f47334a39801ae4cbb2e2f7e1d16 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 29 Dec 2017 09:44:49 -0500 Subject: [PATCH 0248/1971] Merge pull request #1141 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1514491382 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f451537c02..403638e902 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -86,7 +86,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1513803406", - "scratch-blocks": "0.1.0-prerelease.1514398332", + "scratch-blocks": "0.1.0-prerelease.1514491382", "scratch-l10n": "2.0.20171211145754", "scratch-paint": "0.1.0-prerelease.20171220165922", "scratch-render": "0.1.0-prerelease.1513807417", From 5bd2aba36c812ce010a87a7053c815399935af06 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 29 Dec 2017 09:45:05 -0500 Subject: [PATCH 0249/1971] Merge pull request #1144 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1514558457-prerelease.1514558470 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 403638e902..5958d98512 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-paint": "0.1.0-prerelease.20171220165922", "scratch-render": "0.1.0-prerelease.1513807417", "scratch-storage": "0.3.0", - "scratch-vm": "0.1.0-prerelease.1514394990-prerelease.1514395002", + "scratch-vm": "0.1.0-prerelease.1514558457-prerelease.1514558470", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.19.0", From 630c0535c27cc1b8b90738b5b9a6dc89ea6312fa Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 29 Dec 2017 09:47:10 -0500 Subject: [PATCH 0250/1971] Merge pull request #1145 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1514558744 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5958d98512..8680b4dcbc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -86,7 +86,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1513803406", - "scratch-blocks": "0.1.0-prerelease.1514491382", + "scratch-blocks": "0.1.0-prerelease.1514558744", "scratch-l10n": "2.0.20171211145754", "scratch-paint": "0.1.0-prerelease.20171220165922", "scratch-render": "0.1.0-prerelease.1513807417", From cccc0beaca3b4dda93cd0f9e6ecc0dbd7a44277a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 29 Dec 2017 09:53:35 -0500 Subject: [PATCH 0251/1971] Merge pull request #1146 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1514558750-prerelease.1514558766 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8680b4dcbc..a9dd3560fd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-paint": "0.1.0-prerelease.20171220165922", "scratch-render": "0.1.0-prerelease.1513807417", "scratch-storage": "0.3.0", - "scratch-vm": "0.1.0-prerelease.1514558457-prerelease.1514558470", + "scratch-vm": "0.1.0-prerelease.1514558750-prerelease.1514558766", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.19.0", From 1ee9f41f1142c652932430ae84a7b0962e50bd8c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 29 Dec 2017 11:30:34 -0500 Subject: [PATCH 0252/1971] Merge pull request #1147 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1514561281-prerelease.1514561294 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a9dd3560fd..04c620cf81 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-paint": "0.1.0-prerelease.20171220165922", "scratch-render": "0.1.0-prerelease.1513807417", "scratch-storage": "0.3.0", - "scratch-vm": "0.1.0-prerelease.1514558750-prerelease.1514558766", + "scratch-vm": "0.1.0-prerelease.1514561281-prerelease.1514561294", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.19.0", From 5229604f2e3b41d79538b5f124c6e1a9169e4fc1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 3 Jan 2018 09:49:55 -0500 Subject: [PATCH 0253/1971] Merge pull request #1155 from paulkaplan/fix-eslint-plugin-import-update Update to 2.8.0 manually because of greenkeeper test error. --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 04c620cf81..546652306e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -45,7 +45,7 @@ "enzyme-adapter-react-16": "1.1.0", "eslint": "^4.7.1", "eslint-config-scratch": "^5.0.0", - "eslint-plugin-import": "^2.7.0", + "eslint-plugin-import": "^2.8.0", "eslint-plugin-react": "^7.5.1", "file-loader": "1.1.6", "get-float-time-domain-data": "0.1.0", From 880ea92027685665e59f618e4a54bd80bff1d557 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 3 Jan 2018 11:12:04 -0500 Subject: [PATCH 0254/1971] Merge pull request #1154 from LLK/greenkeeper/chromedriver-2.34.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update chromedriver to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 546652306e..cc284ad212 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -37,7 +37,7 @@ "babel-preset-es2015": "^6.22.0", "babel-preset-react": "^6.22.0", "buffer-loader": "0.0.1", - "chromedriver": "2.34.0", + "chromedriver": "2.34.1", "classnames": "2.2.5", "copy-webpack-plugin": "^4.3.0", "css-loader": "^0.28.7", From 9b0e095efdd8aec2ec07794070f036e1c29c6a3a Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 4 Jan 2018 10:05:23 -0500 Subject: [PATCH 0255/1971] Merge pull request #1167 from paulkaplan/update-scratch-paint Update scratch paint version number manually --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cc284ad212..0b7520599e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -88,7 +88,7 @@ "scratch-audio": "0.1.0-prerelease.1513803406", "scratch-blocks": "0.1.0-prerelease.1514558744", "scratch-l10n": "2.0.20171211145754", - "scratch-paint": "0.1.0-prerelease.20171220165922", + "scratch-paint": "0.1.0-prerelease.20180103203117", "scratch-render": "0.1.0-prerelease.1513807417", "scratch-storage": "0.3.0", "scratch-vm": "0.1.0-prerelease.1514561281-prerelease.1514561294", From 0c6dddc7615b6e62bb174d1bbac9d6cc5686ece8 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 5 Jan 2018 11:37:06 -0500 Subject: [PATCH 0256/1971] Merge pull request #1169 from paulkaplan/costume-backdrop-blocks Use new looks reporters with menus for costume name and number. --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0b7520599e..e32471ee93 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -86,12 +86,12 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1513803406", - "scratch-blocks": "0.1.0-prerelease.1514558744", + "scratch-blocks": "0.1.0-prerelease.1515121920", "scratch-l10n": "2.0.20171211145754", "scratch-paint": "0.1.0-prerelease.20180103203117", "scratch-render": "0.1.0-prerelease.1513807417", "scratch-storage": "0.3.0", - "scratch-vm": "0.1.0-prerelease.1514561281-prerelease.1514561294", + "scratch-vm": "0.1.0-prerelease.1515163816-prerelease.1515163829", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.19.0", From 892b1d87af8a06f05dbe7b3ee136fbbd200b513d Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Mon, 8 Jan 2018 11:58:01 -0500 Subject: [PATCH 0257/1971] Merge pull request #1201 from LLK/greenkeeper/scratch-l10n-2.0.20180108132626 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e32471ee93..e6bb5df976 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -87,7 +87,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1513803406", "scratch-blocks": "0.1.0-prerelease.1515121920", - "scratch-l10n": "2.0.20171211145754", + "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.1.0-prerelease.20180103203117", "scratch-render": "0.1.0-prerelease.1513807417", "scratch-storage": "0.3.0", From 9a2afa7571a710840c41761abdfc432fc848c127 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Mon, 8 Jan 2018 15:04:08 -0500 Subject: [PATCH 0258/1971] WebGL unavailable modal (#1116) Add Error Boundary (has componentDidCatch method) to handle WebGL errors and display an informative message. --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e6bb5df976..0879116d2c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -61,7 +61,7 @@ "lodash.pick": "4.4.0", "minilog": "3.1.0", "mkdirp": "^0.5.1", - "platform": "^1.3.4", + "platform": "1.3.4", "postcss-import": "^11.0.0", "postcss-loader": "^2.0.5", "postcss-simple-vars": "^4.0.0", From 2903a146258a619ebb608b5feae26f8a5a658b7b Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 10 Jan 2018 10:13:59 -0500 Subject: [PATCH 0259/1971] Support IE just enough to show unsupported modal (#1213) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added object.assign polyfill * Fix IE load failure Don’t call `(window.AudioContext || window.webkitAudioContext)()` if platform is IE Catch any other errors, and display Browser unsupported in error-boundary. If for some reason there are no other errors (unlikely), preview-modal will still check for IE and show the unsupported modal. Polyfilling Object.assign is enough to get through load and show the modal for IE. We can always switch to `babel-polyfill` if there are others needed later, but for now this is smaller. --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0879116d2c..9ec0ca75fd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -43,6 +43,7 @@ "css-loader": "^0.28.7", "enzyme": "^3.1.0", "enzyme-adapter-react-16": "1.1.0", + "es6-object-assign": "1.1.0", "eslint": "^4.7.1", "eslint-config-scratch": "^5.0.0", "eslint-plugin-import": "^2.8.0", From 0e3ad1fb5407aabb8cfb7e480f16cfaca385527c Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 10 Jan 2018 13:46:22 -0500 Subject: [PATCH 0260/1971] Merge pull request #1214 from fsih/monitorDropdownStrings Clean up monitor dropdown strings --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9ec0ca75fd..a7e3842040 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -87,7 +87,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1513803406", - "scratch-blocks": "0.1.0-prerelease.1515121920", + "scratch-blocks": "0.1.0-prerelease.1515601869", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.1.0-prerelease.20180103203117", "scratch-render": "0.1.0-prerelease.1513807417", From 6a819a903e16366cbf29c66171143d545047b20c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 10 Jan 2018 16:26:59 -0500 Subject: [PATCH 0261/1971] Merge pull request #1222 from paulkaplan/fix-edge Get GUI working on Edge --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a7e3842040..ddf77b120c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -90,7 +90,7 @@ "scratch-blocks": "0.1.0-prerelease.1515601869", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.1.0-prerelease.20180103203117", - "scratch-render": "0.1.0-prerelease.1513807417", + "scratch-render": "0.1.0-prerelease.1515614083", "scratch-storage": "0.3.0", "scratch-vm": "0.1.0-prerelease.1515163816-prerelease.1515163829", "selenium-webdriver": "3.5.0", From 818d2736e1986f28a2b5a39c47eda7fa2c39543b Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 10 Jan 2018 17:13:56 -0500 Subject: [PATCH 0262/1971] Merge pull request #1217 from fsih/inputWidth Set default input width --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ddf77b120c..5b4f686c5d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "scratch-audio": "0.1.0-prerelease.1513803406", "scratch-blocks": "0.1.0-prerelease.1515601869", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.1.0-prerelease.20180103203117", + "scratch-paint": "0.1.0-prerelease.20180110204944", "scratch-render": "0.1.0-prerelease.1515614083", "scratch-storage": "0.3.0", "scratch-vm": "0.1.0-prerelease.1515163816-prerelease.1515163829", From 8356d373d899ec8b4bfebfe0689941fdfe7bd464 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 11 Jan 2018 14:11:00 -0500 Subject: [PATCH 0263/1971] Merge pull request #1232 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1515691021-prerelease.1515691035 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5b4f686c5d..ae042e8083 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "scratch-paint": "0.1.0-prerelease.20180110204944", "scratch-render": "0.1.0-prerelease.1515614083", "scratch-storage": "0.3.0", - "scratch-vm": "0.1.0-prerelease.1515163816-prerelease.1515163829", + "scratch-vm": "0.1.0-prerelease.1515691021-prerelease.1515691035", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.19.0", From f56f20ec8571106a7218aab4d5c51c543ef0a4a8 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 11 Jan 2018 21:19:25 -0500 Subject: [PATCH 0264/1971] Merge pull request #1236 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1515707006 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ae042e8083..79ed3290c3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -87,7 +87,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1513803406", - "scratch-blocks": "0.1.0-prerelease.1515601869", + "scratch-blocks": "0.1.0-prerelease.1515707006", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.1.0-prerelease.20180110204944", "scratch-render": "0.1.0-prerelease.1515614083", From 15e4067cad3ff846fffc8165a6c13961a442d314 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 12 Jan 2018 09:54:40 -0500 Subject: [PATCH 0265/1971] Merge pull request #1226 from LLK/greenkeeper/scratch-audio-0.1.0-prerelease.1515596313 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-audio to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 79ed3290c3..f060a20713 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -86,7 +86,7 @@ "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "0.1.0-prerelease.1513803406", + "scratch-audio": "0.1.0-prerelease.1515596313", "scratch-blocks": "0.1.0-prerelease.1515707006", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.1.0-prerelease.20180110204944", From 7ecd4e48670776c3ae5c3d91590422965a2a0667 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 12 Jan 2018 10:34:01 -0500 Subject: [PATCH 0266/1971] Merge pull request #1238 from paulkaplan/use-renderer-webgl-check Use new renderer methods to check for WebGL. --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f060a20713..1f945c19ac 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -90,7 +90,7 @@ "scratch-blocks": "0.1.0-prerelease.1515707006", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.1.0-prerelease.20180110204944", - "scratch-render": "0.1.0-prerelease.1515614083", + "scratch-render": "0.1.0-prerelease.1515699707", "scratch-storage": "0.3.0", "scratch-vm": "0.1.0-prerelease.1515691021-prerelease.1515691035", "selenium-webdriver": "3.5.0", From ba7bad012624896c39efee88e9595c419f233e63 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 12 Jan 2018 11:03:52 -0500 Subject: [PATCH 0267/1971] Merge pull request #1229 from paulkaplan/fix-gum Fix Firefox sound recording --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1f945c19ac..2f6d4d3782 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -50,6 +50,7 @@ "eslint-plugin-react": "^7.5.1", "file-loader": "1.1.6", "get-float-time-domain-data": "0.1.0", + "get-user-media-promise": "1.1.1", "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", "html-webpack-plugin": "^2.30.0", "immutable": "3.8.1", From a15e18c4be430d39ffc5d1330abefc430eb6e1fe Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 12 Jan 2018 15:04:30 -0500 Subject: [PATCH 0268/1971] Merge pull request #1240 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1515771598-prerelease.1515771612 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2f6d4d3782..e584f40c74 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -93,7 +93,7 @@ "scratch-paint": "0.1.0-prerelease.20180110204944", "scratch-render": "0.1.0-prerelease.1515699707", "scratch-storage": "0.3.0", - "scratch-vm": "0.1.0-prerelease.1515691021-prerelease.1515691035", + "scratch-vm": "0.1.0-prerelease.1515771598-prerelease.1515771612", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.19.0", From 6e813a26058f67abf12ba866c064ae770b7dac48 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 12 Jan 2018 15:58:47 -0500 Subject: [PATCH 0269/1971] Merge pull request #1223 from ericrosenbaum/bugfix/droppability-audit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove some blocks’ menu shadows to make them non-droppable --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e584f40c74..e04c1f2eb2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -88,7 +88,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1515596313", - "scratch-blocks": "0.1.0-prerelease.1515707006", + "scratch-blocks": "0.1.0-prerelease.1515788457", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.1.0-prerelease.20180110204944", "scratch-render": "0.1.0-prerelease.1515699707", From be00d7990832b8e99edb7ab67e53e155f8693e1f Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Fri, 12 Jan 2018 16:13:50 -0500 Subject: [PATCH 0270/1971] Merge pull request #1243 from thisandagain/feature/1036 Add Google Analytics --- packages/scratch-gui/package.json | 1 + packages/scratch-gui/src/containers/blocks.jsx | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e04c1f2eb2..6dc10c5514 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -74,6 +74,7 @@ "react-contextmenu": "2.9.1", "react-dom": "16.2.0", "react-draggable": "3.0.4", + "react-ga": "2.4.1", "react-intl": "2.4.0", "react-intl-redux": "0.6.0", "react-modal": "3.1.9", diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index a016391e63..e546845e16 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -6,6 +6,8 @@ import PropTypes from 'prop-types'; import React from 'react'; import VMScratchBlocks from '../lib/blocks'; import VM from 'scratch-vm'; + +import analytics from '../lib/analytics'; import Prompt from './prompt.jsx'; import BlocksComponent from '../components/blocks/blocks.jsx'; import ExtensionLibrary from './extension-library.jsx'; @@ -74,6 +76,8 @@ class Blocks extends React.Component { this.attachVM(); this.props.vm.setLocale(this.props.locale, this.props.messages); + + analytics.pageview('/editors/blocks'); } shouldComponentUpdate (nextProps, nextState) { return ( From 8d3510ba9fcbe435b9093b2a66e2f8ee0cf01fdc Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 17 Jan 2018 09:24:27 -0500 Subject: [PATCH 0271/1971] Merge pull request #1272 from rschamp/scratch-audio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-audio to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6dc10c5514..97900e12f1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -88,7 +88,7 @@ "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "0.1.0-prerelease.1515596313", + "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1515788457", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.1.0-prerelease.20180110204944", From 7e7a785294c1b059caf718627a664093e38f88c4 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 17 Jan 2018 10:20:06 -0500 Subject: [PATCH 0272/1971] Merge pull request #1274 from paulkaplan/update-scratch-deps Manually update the scratch-* deps, greenkeeper seems behind. --- packages/scratch-gui/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 97900e12f1..a4d53c081f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,10 +91,10 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1515788457", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.1.0-prerelease.20180110204944", - "scratch-render": "0.1.0-prerelease.1515699707", + "scratch-paint": "0.1.0-prerelease.20180112204058", + "scratch-render": "0.1.0-prerelease.1516202017", "scratch-storage": "0.3.0", - "scratch-vm": "0.1.0-prerelease.1515771598-prerelease.1515771612", + "scratch-vm": "0.1.0-prerelease.1516201958-prerelease.1516201973", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.19.0", From 2a5d4586bd4a07eb005816f3a900fc950fbf5382 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 17 Jan 2018 11:12:46 -0500 Subject: [PATCH 0273/1971] Merge pull request #1285 from rschamp/update-vm Update scratch-vm to the latest version --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a4d53c081f..7ed7946aa9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.1.0-prerelease.20180112204058", "scratch-render": "0.1.0-prerelease.1516202017", "scratch-storage": "0.3.0", - "scratch-vm": "0.1.0-prerelease.1516201958-prerelease.1516201973", + "scratch-vm": "0.1.0-prerelease.1516205298-prerelease.1516205311", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.19.0", From 54da29635d43e6585851b12cdc68ee7ab9e73366 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 17 Jan 2018 13:46:42 -0500 Subject: [PATCH 0274/1971] Merge pull request #1289 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1516210259-prerelease.1516210276 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7ed7946aa9..e6ef75b48f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.1.0-prerelease.20180112204058", "scratch-render": "0.1.0-prerelease.1516202017", "scratch-storage": "0.3.0", - "scratch-vm": "0.1.0-prerelease.1516205298-prerelease.1516205311", + "scratch-vm": "0.1.0-prerelease.1516210259-prerelease.1516210276", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.19.0", From 30886934a48d30a235c6bbba19068e499ced74bd Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 18 Jan 2018 11:13:18 -0500 Subject: [PATCH 0275/1971] Merge pull request #1295 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1516215511-prerelease.1516215524 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e6ef75b48f..4a4e989cec 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.1.0-prerelease.20180112204058", "scratch-render": "0.1.0-prerelease.1516202017", "scratch-storage": "0.3.0", - "scratch-vm": "0.1.0-prerelease.1516210259-prerelease.1516210276", + "scratch-vm": "0.1.0-prerelease.1516215511-prerelease.1516215524", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.19.0", From 302a50595707a958aaacac42b1039f6712606bbc Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 22 Jan 2018 11:34:31 -0500 Subject: [PATCH 0276/1971] Merge pull request #1319 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1516636897 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4a4e989cec..be62441dc0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1516198804", - "scratch-blocks": "0.1.0-prerelease.1515788457", + "scratch-blocks": "0.1.0-prerelease.1516636897", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.1.0-prerelease.20180112204058", "scratch-render": "0.1.0-prerelease.1516202017", From 435b3572a0f2826b228cf49f4d0a19402bf57456 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Tue, 23 Jan 2018 22:10:47 -0800 Subject: [PATCH 0277/1971] Merge pull request #1333 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.1516772701 chore(package): update scratch-render to version 0.1.0-prerelease.1516772701 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index be62441dc0..86342090ab 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "scratch-blocks": "0.1.0-prerelease.1516636897", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.1.0-prerelease.20180112204058", - "scratch-render": "0.1.0-prerelease.1516202017", + "scratch-render": "0.1.0-prerelease.1516772701", "scratch-storage": "0.3.0", "scratch-vm": "0.1.0-prerelease.1516215511-prerelease.1516215524", "selenium-webdriver": "3.5.0", From bd5aa90ae2506eed977ca6d29b3a93980ff58743 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 26 Jan 2018 11:53:59 -0500 Subject: [PATCH 0278/1971] Merge pull request #1344 from paulkaplan/fix-selenium-chrome-64 Fix integration tests for the recent chrome stable update --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 86342090ab..6606649a7b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -37,7 +37,7 @@ "babel-preset-es2015": "^6.22.0", "babel-preset-react": "^6.22.0", "buffer-loader": "0.0.1", - "chromedriver": "2.34.1", + "chromedriver": "2.35.0", "classnames": "2.2.5", "copy-webpack-plugin": "^4.3.0", "css-loader": "^0.28.7", From e80048182348c75c4415885b626b343637ea3147 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 26 Jan 2018 12:10:06 -0500 Subject: [PATCH 0279/1971] Merge pull request #1345 from paulkaplan/update-more-deps Update all the recent scratch-* and other updates. --- packages/scratch-gui/package.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6606649a7b..6f5c1c97f4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -71,17 +71,17 @@ "raf": "^3.4.0", "raw-loader": "0.5.1", "react": "16.2.0", - "react-contextmenu": "2.9.1", + "react-contextmenu": "2.9.2", "react-dom": "16.2.0", - "react-draggable": "3.0.4", + "react-draggable": "3.0.5", "react-ga": "2.4.1", "react-intl": "2.4.0", "react-intl-redux": "0.6.0", - "react-modal": "3.1.9", + "react-modal": "3.1.11", "react-redux": "5.0.6", "react-responsive": "4.0.3", "react-style-proptype": "3.1.0", - "react-tabs": "2.1.1", + "react-tabs": "2.2.1", "react-test-renderer": "16.2.0", "react-tooltip": "3.4.0", "redux": "3.7.0", @@ -89,12 +89,12 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1516198804", - "scratch-blocks": "0.1.0-prerelease.1516636897", + "scratch-blocks": "0.1.0-prerelease.1516895260", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.1.0-prerelease.20180112204058", - "scratch-render": "0.1.0-prerelease.1516772701", - "scratch-storage": "0.3.0", - "scratch-vm": "0.1.0-prerelease.1516215511-prerelease.1516215524", + "scratch-paint": "0.1.0-prerelease.20180124235741", + "scratch-render": "0.1.0-prerelease.1516837442", + "scratch-storage": "0.4.0", + "scratch-vm": "0.1.0-prerelease.1516804276-prerelease.1516804295", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.19.0", From 24109a227f7513e5035022eb1da301e10021f215 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 26 Jan 2018 12:10:40 -0500 Subject: [PATCH 0280/1971] Merge pull request #1324 from LLK/greenkeeper/platform-1.3.5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update platform to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6f5c1c97f4..de25064c3c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -63,7 +63,7 @@ "lodash.pick": "4.4.0", "minilog": "3.1.0", "mkdirp": "^0.5.1", - "platform": "1.3.4", + "platform": "1.3.5", "postcss-import": "^11.0.0", "postcss-loader": "^2.0.5", "postcss-simple-vars": "^4.0.0", From 70f1a7798be444792a0446f95163c5febf92c76f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 29 Jan 2018 08:47:43 -0500 Subject: [PATCH 0281/1971] Merge pull request #1311 from dgnball/remove_message Remove "More options" in New Message dialog --- packages/scratch-gui/src/containers/blocks.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index e546845e16..5be1498e15 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -238,6 +238,7 @@ class Blocks extends React.Component { handlePromptStart (message, defaultValue, callback, optTitle) { const p = {prompt: {callback, message, defaultValue}}; p.prompt.title = optTitle ? optTitle : 'New Variable'; + p.prompt.showMoreOptions = optTitle !== 'New Message'; this.setState(p); } handlePromptCallback (data) { @@ -278,6 +279,7 @@ class Blocks extends React.Component { Date: Mon, 29 Jan 2018 09:03:01 -0500 Subject: [PATCH 0282/1971] Merge pull request #1353 from LLK/greenkeeper/style-loader-0.20.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update style-loader to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index de25064c3c..96d559fce7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "scratch-vm": "0.1.0-prerelease.1516804276-prerelease.1516804295", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", - "style-loader": "^0.19.0", + "style-loader": "^0.20.0", "svg-to-image": "1.1.3", "text-encoding": "0.6.4", "wav-encoder": "1.3.0", From cf46bfe1a825c59a61f01a13de42637510e69bc0 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 29 Jan 2018 09:11:34 -0500 Subject: [PATCH 0283/1971] Merge pull request #1362 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1517010734-prerelease.1517010747 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.1.0-prerelease.1517010… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 96d559fce7..984b1dfd76 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.1.0-prerelease.20180124235741", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1516804276-prerelease.1516804295", + "scratch-vm": "0.1.0-prerelease.1517010734-prerelease.1517010747", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From 4a2b22ff87dfe8e04118e2a89f64b30a374786ad Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 29 Jan 2018 09:51:31 -0500 Subject: [PATCH 0284/1971] Merge pull request #1364 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1517236563-prerelease.1517236578 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 984b1dfd76..5b81e9fb1b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.1.0-prerelease.20180124235741", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1517010734-prerelease.1517010747", + "scratch-vm": "0.1.0-prerelease.1517236563-prerelease.1517236578", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From 426f2d7371d73c7c8aed0d4b92f59525fa8ebc8e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 29 Jan 2018 13:55:12 -0500 Subject: [PATCH 0285/1971] Merge pull request #1367 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1517250005 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5b81e9fb1b..6e849d74f9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1516198804", - "scratch-blocks": "0.1.0-prerelease.1516895260", + "scratch-blocks": "0.1.0-prerelease.1517250005", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.1.0-prerelease.20180124235741", "scratch-render": "0.1.0-prerelease.1516837442", From 04aaa5b4985f19eaee9e86c0ae36fc5a617757c2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 30 Jan 2018 10:45:50 -0500 Subject: [PATCH 0286/1971] Merge pull request #1369 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1517268276 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6e849d74f9..fe001bf402 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1516198804", - "scratch-blocks": "0.1.0-prerelease.1517250005", + "scratch-blocks": "0.1.0-prerelease.1517268276", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.1.0-prerelease.20180124235741", "scratch-render": "0.1.0-prerelease.1516837442", From 5e82217048fc67a5d2b48d5b170595aad6982908 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 30 Jan 2018 16:39:27 -0500 Subject: [PATCH 0287/1971] Merge pull request #1377 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1517344159-prerelease.1517344174 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index fe001bf402..7c31c3fb88 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.1.0-prerelease.20180124235741", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1517236563-prerelease.1517236578", + "scratch-vm": "0.1.0-prerelease.1517344159-prerelease.1517344174", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From 03ac04168b1c0de09240176edbfd4c0015609659 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 31 Jan 2018 16:27:48 -0500 Subject: [PATCH 0288/1971] Merge pull request #1385 from paulkaplan/manually-update-paint Update scratch-paint dependency because greenkeeper does not want to --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7c31c3fb88..cb4cf22451 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1517268276", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.1.0-prerelease.20180124235741", + "scratch-paint": "0.1.0-prerelease.20180130210131", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1517344159-prerelease.1517344174", From 97640681c9a7308e4062533012e5b697338276fa Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 31 Jan 2018 16:29:07 -0500 Subject: [PATCH 0289/1971] Merge pull request #1382 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1517409350-prerelease.1517409368 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cb4cf22451..7d463c7107 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.1.0-prerelease.20180130210131", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1517344159-prerelease.1517344174", + "scratch-vm": "0.1.0-prerelease.1517409350-prerelease.1517409368", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From 9f06670feabe5b70eb2f00bc4b1b631dd857a045 Mon Sep 17 00:00:00 2001 From: kchadha Date: Thu, 1 Feb 2018 11:19:29 -0500 Subject: [PATCH 0290/1971] Merge pull request #1389 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1517501102 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7d463c7107..156260f98d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1516198804", - "scratch-blocks": "0.1.0-prerelease.1517268276", + "scratch-blocks": "0.1.0-prerelease.1517501102", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.1.0-prerelease.20180130210131", "scratch-render": "0.1.0-prerelease.1516837442", From 242fde3ac1aebf818b4e04893262fd34ef3c0456 Mon Sep 17 00:00:00 2001 From: kchadha Date: Thu, 1 Feb 2018 11:20:05 -0500 Subject: [PATCH 0291/1971] Merge pull request #1371 from kchadha/modal-by-vartype Use variable type information in variable prompts to show/hide 'more options' --- packages/scratch-gui/src/containers/blocks.jsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 5be1498e15..a53b487600 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -235,10 +235,12 @@ class Blocks extends React.Component { setBlocks (blocks) { this.blocks = blocks; } - handlePromptStart (message, defaultValue, callback, optTitle) { + handlePromptStart (message, defaultValue, callback, optTitle, optVarType) { const p = {prompt: {callback, message, defaultValue}}; - p.prompt.title = optTitle ? optTitle : 'New Variable'; - p.prompt.showMoreOptions = optTitle !== 'New Message'; + p.prompt.title = optTitle ? optTitle : + this.ScratchBlocks.VARIABLE_MODAL_TITLE; + p.prompt.showMoreOptions = + optVarType !== this.ScratchBlocks.BROADCAST_MESSAGE_VARIABLE_TYPE; this.setState(p); } handlePromptCallback (data) { From 51344ce8e0d745b34e241b52842b441b43987cc0 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 2 Feb 2018 09:28:38 -0500 Subject: [PATCH 0292/1971] Merge pull request #1393 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1517513646-prerelease.1517513661 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 156260f98d..01a903e7d5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.1.0-prerelease.20180130210131", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1517409350-prerelease.1517409368", + "scratch-vm": "0.1.0-prerelease.1517513646-prerelease.1517513661", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From f1f1acd94d6559c6694564d51f3dc5b38f6fe600 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 2 Feb 2018 16:28:41 -0500 Subject: [PATCH 0293/1971] Merge pull request #1400 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1517597673-prerelease.1517597689 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 01a903e7d5..6730e05eb9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.1.0-prerelease.20180130210131", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1517513646-prerelease.1517513661", + "scratch-vm": "0.1.0-prerelease.1517597673-prerelease.1517597689", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From a934433363e4d93450399c5fff323195e8ecdb7b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 5 Feb 2018 10:21:19 -0500 Subject: [PATCH 0294/1971] Merge pull request #1404 from LLK/greenkeeper/react-modal-3.1.12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-modal to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6730e05eb9..b757d0f9fd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -77,7 +77,7 @@ "react-ga": "2.4.1", "react-intl": "2.4.0", "react-intl-redux": "0.6.0", - "react-modal": "3.1.11", + "react-modal": "3.1.12", "react-redux": "5.0.6", "react-responsive": "4.0.3", "react-style-proptype": "3.1.0", From 01132a2a12e69de69d572c8decad265c7f186022 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 7 Feb 2018 13:59:09 -0500 Subject: [PATCH 0295/1971] Merge pull request #1418 from paulkaplan/update-paint-again Update paint version for bugfixes --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b757d0f9fd..83efd7dd72 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1517501102", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.1.0-prerelease.20180130210131", + "scratch-paint": "0.1.0-prerelease.20180207174158", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1517597673-prerelease.1517597689", From 0dc768e2ad973e5ddba121ea69c9f52dc4c71fbb Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 7 Feb 2018 15:13:51 -0500 Subject: [PATCH 0296/1971] Merge pull request #1414 from paulkaplan/lower-click-threshold Lower long and double click timing to 400ms to match scratch 2 --- packages/scratch-gui/src/containers/stage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index cb2a38963e..1f4e62ed58 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -201,7 +201,7 @@ class Stage extends React.Component { mouseDownPosition: mousePosition, mouseDownTimeoutId: setTimeout( this.onStartDrag.bind(this, mousePosition[0], mousePosition[1]), - 500 + 400 ) }); } From e157ab6f327eb9059243448735fefe3fa2a89b47 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 8 Feb 2018 11:38:00 -0500 Subject: [PATCH 0297/1971] Merge pull request #1421 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1518098842-prerelease.1518098861 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 83efd7dd72..51237e2d92 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.1.0-prerelease.20180207174158", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1517597673-prerelease.1517597689", + "scratch-vm": "0.1.0-prerelease.1518098842-prerelease.1518098861", "selenium-webdriver": "3.5.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From bd38e4299267cc49c11035f90bbddf8ff722af2b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 8 Feb 2018 14:14:12 -0500 Subject: [PATCH 0298/1971] Merge pull request #1419 from LLK/greenkeeper/react-style-proptype-3.2.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-style-proptype to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 51237e2d92..7749fc06c6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -80,7 +80,7 @@ "react-modal": "3.1.12", "react-redux": "5.0.6", "react-responsive": "4.0.3", - "react-style-proptype": "3.1.0", + "react-style-proptype": "3.2.0", "react-tabs": "2.2.1", "react-test-renderer": "16.2.0", "react-tooltip": "3.4.0", From 09bd673c5eafa211102f6852b264bff73ca8b576 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 9 Feb 2018 09:50:09 -0500 Subject: [PATCH 0299/1971] Merge pull request #1429 from LLK/greenkeeper/react-modal-3.1.13 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-modal to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7749fc06c6..2e5efc9ead 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -77,7 +77,7 @@ "react-ga": "2.4.1", "react-intl": "2.4.0", "react-intl-redux": "0.6.0", - "react-modal": "3.1.12", + "react-modal": "3.1.13", "react-redux": "5.0.6", "react-responsive": "4.0.3", "react-style-proptype": "3.2.0", From ec244a64dacee7734b10e484dcb1e149870d3fd8 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 9 Feb 2018 11:33:32 -0500 Subject: [PATCH 0300/1971] Merge pull request #1432 from paulkaplan/fix-draggable-images Fix numerous draggable images in the GUI --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 538455f336..5807294541 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -28,6 +28,7 @@ const MenuBar = props => ( Scratch
@@ -42,6 +43,7 @@ const MenuBar = props => ( > From 23e2f7e98e043c199f3ef7141b3409365f310a1b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 12 Feb 2018 09:04:52 -0500 Subject: [PATCH 0301/1971] Merge pull request #1436 from LLK/greenkeeper/initial MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update dependencies to enable Greenkeeper 🌴 --- packages/scratch-gui/package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2e5efc9ead..7840f355b7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -42,7 +42,7 @@ "copy-webpack-plugin": "^4.3.0", "css-loader": "^0.28.7", "enzyme": "^3.1.0", - "enzyme-adapter-react-16": "1.1.0", + "enzyme-adapter-react-16": "1.1.1", "es6-object-assign": "1.1.0", "eslint": "^4.7.1", "eslint-config-scratch": "^5.0.0", @@ -53,7 +53,7 @@ "get-user-media-promise": "1.1.1", "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", "html-webpack-plugin": "^2.30.0", - "immutable": "3.8.1", + "immutable": "3.8.2", "jest": "^21.0.0", "lodash.bindall": "4.4.0", "lodash.debounce": "4.0.8", @@ -76,7 +76,7 @@ "react-draggable": "3.0.5", "react-ga": "2.4.1", "react-intl": "2.4.0", - "react-intl-redux": "0.6.0", + "react-intl-redux": "0.7.0", "react-modal": "3.1.13", "react-redux": "5.0.6", "react-responsive": "4.0.3", @@ -84,18 +84,18 @@ "react-tabs": "2.2.1", "react-test-renderer": "16.2.0", "react-tooltip": "3.4.0", - "redux": "3.7.0", + "redux": "3.7.2", "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1516198804", - "scratch-blocks": "0.1.0-prerelease.1517501102", + "scratch-blocks": "0.1.0-prerelease.1518210548", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.1.0-prerelease.20180207174158", + "scratch-paint": "0.1.0-prerelease.20180209163938", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1518098842-prerelease.1518098861", - "selenium-webdriver": "3.5.0", + "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", "svg-to-image": "1.1.3", From 939b3e53a4100c1c415dc7a1dc4ba9ee51b303b5 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 12 Feb 2018 10:45:24 -0500 Subject: [PATCH 0302/1971] Merge pull request #1434 from paulkaplan/stage-drag-threshold Apply a drag threshold to make clicking sprites easier --- packages/scratch-gui/src/containers/stage.jsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 1f4e62ed58..5095f86dd0 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -15,6 +15,7 @@ import { } from '../reducers/color-picker'; const colorPickerRadius = 20; +const dragThreshold = 3; // Same as the block drag threshold class Stage extends React.Component { constructor (props) { @@ -149,9 +150,13 @@ class Stage extends React.Component { this.pickX = mousePosition[0]; this.pickY = mousePosition[1]; - if (this.state.mouseDownTimeoutId !== null) { - this.cancelMouseDownTimeout(); - if (this.state.mouseDown && !this.state.isDragging) { + if (this.state.mouseDown && !this.state.isDragging) { + const distanceFromMouseDown = Math.sqrt( + Math.pow(mousePosition[0] - this.state.mouseDownPosition[0], 2) + + Math.pow(mousePosition[1] - this.state.mouseDownPosition[1], 2) + ); + if (distanceFromMouseDown > dragThreshold) { + this.cancelMouseDownTimeout(); this.onStartDrag(...this.state.mouseDownPosition); } } From f54fd08c796a57b1ab6c9d9d461c7960d9ca0b2a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 22 Feb 2018 14:21:44 -0500 Subject: [PATCH 0303/1971] Merge pull request #1352 from towerofnix/scrollwheel-events Detect wheel event on canvas and post data to VM --- packages/scratch-gui/src/containers/stage.jsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 5095f86dd0..c386112b38 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -31,6 +31,7 @@ class Stage extends React.Component { 'onMouseDown', 'onStartDrag', 'onStopDrag', + 'onWheel', 'updateRect', 'questionListener', 'setCanvas' @@ -98,6 +99,7 @@ class Stage extends React.Component { document.addEventListener('touchend', this.onMouseUp); canvas.addEventListener('mousedown', this.onMouseDown); canvas.addEventListener('touchstart', this.onMouseDown); + canvas.addEventListener('wheel', this.onWheel); } detachMouseEvents (canvas) { document.removeEventListener('mousemove', this.onMouseMove); @@ -106,6 +108,7 @@ class Stage extends React.Component { document.removeEventListener('touchend', this.onMouseUp); canvas.removeEventListener('mousedown', this.onMouseDown); canvas.removeEventListener('touchstart', this.onMouseDown); + canvas.removeEventListener('wheel', this.onWheel); } attachRectEvents () { window.addEventListener('resize', this.updateRect); @@ -232,6 +235,13 @@ class Stage extends React.Component { this.setState({colorInfo: null}); } } + onWheel (e) { + const data = { + deltaX: e.deltaX, + deltaY: e.deltaY + }; + this.props.vm.postIOData('mouseWheel', data); + } cancelMouseDownTimeout () { if (this.state.mouseDownTimeoutId !== null) { clearTimeout(this.state.mouseDownTimeoutId); From 8264079e237d040acdf4f7d3b083aeb4787cc6e4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 22 Feb 2018 14:25:15 -0500 Subject: [PATCH 0304/1971] Merge pull request #1440 from ivanixgames/fix1212c Fix issue 1212 --- packages/scratch-gui/src/containers/blocks.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index a53b487600..bedbbdbade 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -252,7 +252,10 @@ class Blocks extends React.Component { } handleCustomProceduresClose (data) { this.props.onRequestCloseCustomProcedures(data); - this.workspace.refreshToolboxSelection_(); + const ws = this.workspace; + ws.refreshToolboxSelection_(); + // @todo ensure this does not break on localization + ws.toolbox_.scrollToCategoryByName('My Blocks'); } render () { /* eslint-disable no-unused-vars */ From bf3333ad58c31b35524b9c88030c1fcf401487cf Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 22 Feb 2018 14:28:13 -0500 Subject: [PATCH 0305/1971] Merge pull request #1453 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180215230736 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7840f355b7..34081f7bd4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1518210548", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.1.0-prerelease.20180209163938", + "scratch-paint": "0.2.0-prerelease.20180215230736", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1518098842-prerelease.1518098861", From d9203a720ec62e9313fe051802c8cf8f7f6948ed Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 22 Feb 2018 15:33:53 -0500 Subject: [PATCH 0306/1971] Merge pull request #1485 from paulkaplan/greenkeeper-updates-2-22-18 Greenkeeper updates 2 22 18 --- packages/scratch-gui/package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 34081f7bd4..dd46c50ee3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -48,7 +48,7 @@ "eslint-config-scratch": "^5.0.0", "eslint-plugin-import": "^2.8.0", "eslint-plugin-react": "^7.5.1", - "file-loader": "1.1.6", + "file-loader": "1.1.9", "get-float-time-domain-data": "0.1.0", "get-user-media-promise": "1.1.1", "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", @@ -77,8 +77,8 @@ "react-ga": "2.4.1", "react-intl": "2.4.0", "react-intl-redux": "0.7.0", - "react-modal": "3.1.13", - "react-redux": "5.0.6", + "react-modal": "3.3.1", + "react-redux": "5.0.7", "react-responsive": "4.0.3", "react-style-proptype": "3.2.0", "react-tabs": "2.2.1", @@ -89,12 +89,12 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1516198804", - "scratch-blocks": "0.1.0-prerelease.1518210548", + "scratch-blocks": "0.1.0-prerelease.1519256473", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180215230736", + "scratch-paint": "0.2.0-prerelease.20180222192821", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1518098842-prerelease.1518098861", + "scratch-vm": "0.1.0-prerelease.1518812805-prerelease.1518812820", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From 447f907f8f368b6b2c8a53b94606ff91417ae95a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 26 Feb 2018 09:40:46 -0500 Subject: [PATCH 0307/1971] Merge pull request #1490 from fsih/duplicateCostume Duplicate costume --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index dd46c50ee3..e44c89a024 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180222192821", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1518812805-prerelease.1518812820", + "scratch-vm": "0.1.0-prerelease.1519653784-prerelease.1519653801", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From b32a859edd63b47c906d70c7d72dd5a75eb56bde Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 26 Feb 2018 17:31:34 -0500 Subject: [PATCH 0308/1971] Merge pull request #1468 from fsih/blockDrag Block drag --- packages/scratch-gui/package.json | 2 +- .../scratch-gui/src/lib/vm-listener-hoc.jsx | 36 +++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e44c89a024..685aae9135 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180222192821", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1519653784-prerelease.1519653801", + "scratch-vm": "0.1.0-prerelease.1519681201-prerelease.1519681262", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 081817c780..3ae81fdbbf 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -6,7 +6,9 @@ import VM from 'scratch-vm'; import {connect} from 'react-redux'; import {updateEditingTarget, updateTargets} from '../reducers/targets'; +import {updateBlockDrag} from '../reducers/block-drag'; import {updateMonitors} from '../reducers/monitors'; +import {setReceivedBlocks} from '../reducers/hovered-target'; /* * Higher Order Component to manage events emitted by the VM @@ -18,6 +20,7 @@ const vmListenerHOC = function (WrappedComponent) { constructor (props) { super(props); bindAll(this, [ + 'handleBlockDragEnd', 'handleKeyDown', 'handleKeyUp' ]); @@ -29,6 +32,8 @@ const vmListenerHOC = function (WrappedComponent) { // we need to start listening before mounting the wrapped component. this.props.vm.on('targetsUpdate', this.props.onTargetsUpdate); this.props.vm.on('MONITORS_UPDATE', this.props.onMonitorsUpdate); + this.props.vm.on('BLOCK_DRAG_UPDATE', this.props.onBlockDragUpdate); + this.props.vm.on('BLOCK_DRAG_END', this.handleBlockDragEnd); } componentDidMount () { @@ -37,12 +42,21 @@ const vmListenerHOC = function (WrappedComponent) { document.addEventListener('keyup', this.handleKeyUp); } } + shouldComponentUpdate () { + return false; + } componentWillUnmount () { if (this.props.attachKeyboardEvents) { document.removeEventListener('keydown', this.handleKeyDown); document.removeEventListener('keyup', this.handleKeyUp); } } + handleBlockDragEnd (blocks) { + if (this.props.hoveredSprite && this.props.hoveredSprite !== this.props.editingTarget) { + this.props.vm.shareBlocksToTarget(blocks, this.props.hoveredSprite); + this.props.onReceivedBlocks(true); + } + } handleKeyDown (e) { // Don't capture keys intended for Blockly inputs. if (e.target !== document && e.target !== document.body) return; @@ -74,9 +88,13 @@ const vmListenerHOC = function (WrappedComponent) { const { /* eslint-disable no-unused-vars */ attachKeyboardEvents, + editingTarget, + hoveredSprite, + onBlockDragUpdate, onKeyDown, onKeyUp, onMonitorsUpdate, + onReceivedBlocks, onTargetsUpdate, /* eslint-enable no-unused-vars */ ...props @@ -86,17 +104,23 @@ const vmListenerHOC = function (WrappedComponent) { } VMListener.propTypes = { attachKeyboardEvents: PropTypes.bool, + editingTarget: PropTypes.string, + hoveredSprite: PropTypes.string, + onBlockDragUpdate: PropTypes.func.isRequired, onKeyDown: PropTypes.func, onKeyUp: PropTypes.func, - onMonitorsUpdate: PropTypes.func, - onTargetsUpdate: PropTypes.func, + onMonitorsUpdate: PropTypes.func.isRequired, + onReceivedBlocks: PropTypes.func.isRequired, + onTargetsUpdate: PropTypes.func.isRequired, vm: PropTypes.instanceOf(VM).isRequired }; VMListener.defaultProps = { attachKeyboardEvents: true }; const mapStateToProps = state => ({ - vm: state.vm + vm: state.vm, + hoveredSprite: state.hoveredTarget.sprite, + editingTarget: state.targets.editingTarget }); const mapDispatchToProps = dispatch => ({ onTargetsUpdate: data => { @@ -105,6 +129,12 @@ const vmListenerHOC = function (WrappedComponent) { }, onMonitorsUpdate: monitorList => { dispatch(updateMonitors(monitorList)); + }, + onBlockDragUpdate: areBlocksOverGui => { + dispatch(updateBlockDrag(areBlocksOverGui)); + }, + onReceivedBlocks: receivedBlocks => { + dispatch(setReceivedBlocks(receivedBlocks)); } }); return connect( From ca15d16a52a9aa1981bb429099557e8b1cfdd27d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 27 Feb 2018 12:53:41 -0500 Subject: [PATCH 0309/1971] Merge pull request #1522 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180227161624 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 685aae9135..a58e55026c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1519256473", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180222192821", + "scratch-paint": "0.2.0-prerelease.20180227161624", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1519681201-prerelease.1519681262", From 4cdbcd6a69c5618e680f399569bcece637d3746c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 27 Feb 2018 13:04:53 -0500 Subject: [PATCH 0310/1971] Merge pull request #1523 from paulkaplan/fix-gui-updating Fix the GUI not updating when new project data is available --- .../scratch-gui/src/lib/vm-listener-hoc.jsx | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 3ae81fdbbf..8e7a0ca17d 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -8,7 +8,6 @@ import {connect} from 'react-redux'; import {updateEditingTarget, updateTargets} from '../reducers/targets'; import {updateBlockDrag} from '../reducers/block-drag'; import {updateMonitors} from '../reducers/monitors'; -import {setReceivedBlocks} from '../reducers/hovered-target'; /* * Higher Order Component to manage events emitted by the VM @@ -20,7 +19,6 @@ const vmListenerHOC = function (WrappedComponent) { constructor (props) { super(props); bindAll(this, [ - 'handleBlockDragEnd', 'handleKeyDown', 'handleKeyUp' ]); @@ -33,8 +31,6 @@ const vmListenerHOC = function (WrappedComponent) { this.props.vm.on('targetsUpdate', this.props.onTargetsUpdate); this.props.vm.on('MONITORS_UPDATE', this.props.onMonitorsUpdate); this.props.vm.on('BLOCK_DRAG_UPDATE', this.props.onBlockDragUpdate); - this.props.vm.on('BLOCK_DRAG_END', this.handleBlockDragEnd); - } componentDidMount () { if (this.props.attachKeyboardEvents) { @@ -42,21 +38,12 @@ const vmListenerHOC = function (WrappedComponent) { document.addEventListener('keyup', this.handleKeyUp); } } - shouldComponentUpdate () { - return false; - } componentWillUnmount () { if (this.props.attachKeyboardEvents) { document.removeEventListener('keydown', this.handleKeyDown); document.removeEventListener('keyup', this.handleKeyUp); } } - handleBlockDragEnd (blocks) { - if (this.props.hoveredSprite && this.props.hoveredSprite !== this.props.editingTarget) { - this.props.vm.shareBlocksToTarget(blocks, this.props.hoveredSprite); - this.props.onReceivedBlocks(true); - } - } handleKeyDown (e) { // Don't capture keys intended for Blockly inputs. if (e.target !== document && e.target !== document.body) return; @@ -88,13 +75,10 @@ const vmListenerHOC = function (WrappedComponent) { const { /* eslint-disable no-unused-vars */ attachKeyboardEvents, - editingTarget, - hoveredSprite, onBlockDragUpdate, onKeyDown, onKeyUp, onMonitorsUpdate, - onReceivedBlocks, onTargetsUpdate, /* eslint-enable no-unused-vars */ ...props @@ -104,13 +88,10 @@ const vmListenerHOC = function (WrappedComponent) { } VMListener.propTypes = { attachKeyboardEvents: PropTypes.bool, - editingTarget: PropTypes.string, - hoveredSprite: PropTypes.string, onBlockDragUpdate: PropTypes.func.isRequired, onKeyDown: PropTypes.func, onKeyUp: PropTypes.func, onMonitorsUpdate: PropTypes.func.isRequired, - onReceivedBlocks: PropTypes.func.isRequired, onTargetsUpdate: PropTypes.func.isRequired, vm: PropTypes.instanceOf(VM).isRequired }; @@ -132,9 +113,6 @@ const vmListenerHOC = function (WrappedComponent) { }, onBlockDragUpdate: areBlocksOverGui => { dispatch(updateBlockDrag(areBlocksOverGui)); - }, - onReceivedBlocks: receivedBlocks => { - dispatch(setReceivedBlocks(receivedBlocks)); } }); return connect( From 552b157052c8ae598998fe68b70fffb8a388e8d9 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 2 Mar 2018 10:34:20 -0500 Subject: [PATCH 0311/1971] Merge pull request #1539 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180302152015 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a58e55026c..3f1cdef79d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1519256473", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180227161624", + "scratch-paint": "0.2.0-prerelease.20180302152015", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1519681201-prerelease.1519681262", From a603ad0f1872beb9f7320e5435b30d23050b283c Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 2 Mar 2018 11:22:50 -0500 Subject: [PATCH 0312/1971] Merge pull request #1540 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180302160120 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3f1cdef79d..b8649d8c1e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1519256473", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180302152015", + "scratch-paint": "0.2.0-prerelease.20180302160120", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1519681201-prerelease.1519681262", From 16889718f040a4792c5b03465d5d08d3d173d7c1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 5 Mar 2018 16:45:57 -0500 Subject: [PATCH 0313/1971] Merge pull request #1561 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1520285704 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b8649d8c1e..c8a380483f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1516198804", - "scratch-blocks": "0.1.0-prerelease.1519256473", + "scratch-blocks": "0.1.0-prerelease.1520285704", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180302160120", "scratch-render": "0.1.0-prerelease.1516837442", From 1ab6b86020fa5a7245f9802ea620ebb5582e9620 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 6 Mar 2018 08:49:57 -0500 Subject: [PATCH 0314/1971] Merge pull request #1555 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180305152706 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c8a380483f..c9026365cb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1520285704", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180302160120", + "scratch-paint": "0.2.0-prerelease.20180305152706", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1519681201-prerelease.1519681262", From 6b772815f44b2181820dc3499af4b2000df55c3b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 6 Mar 2018 09:13:27 -0500 Subject: [PATCH 0315/1971] Merge pull request #1566 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1520269961-prerelease.1520269983 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.1.0-prerelease.1520269… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c9026365cb..4368dd81e1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180305152706", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1519681201-prerelease.1519681262", + "scratch-vm": "0.1.0-prerelease.1520269961-prerelease.1520269983", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From f69dbdac0599fb398dac9ec5986380c58e2c7d09 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 6 Mar 2018 09:41:40 -0500 Subject: [PATCH 0316/1971] Merge pull request #1567 from paulkaplan/update-deps-3-6 Update deps 3/6 --- packages/scratch-gui/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4368dd81e1..ab3f72d224 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -27,7 +27,7 @@ "react-dom": "^16.0.0" }, "devDependencies": { - "autoprefixer": "^7.1.3", + "autoprefixer": "^8.1.0", "babel-core": "^6.23.1", "babel-eslint": "^8.0.1", "babel-loader": "^7.1.0", @@ -48,11 +48,11 @@ "eslint-config-scratch": "^5.0.0", "eslint-plugin-import": "^2.8.0", "eslint-plugin-react": "^7.5.1", - "file-loader": "1.1.9", + "file-loader": "1.1.11", "get-float-time-domain-data": "0.1.0", "get-user-media-promise": "1.1.1", "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", - "html-webpack-plugin": "^2.30.0", + "html-webpack-plugin": "^3.0.5", "immutable": "3.8.2", "jest": "^21.0.0", "lodash.bindall": "4.4.0", From 402fb4884f55b7897da4a4540bfeee0c4403269b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 7 Mar 2018 08:46:23 -0500 Subject: [PATCH 0317/1971] Merge pull request #1578 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1520372503-prerelease.1520372519 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ab3f72d224..180b4718ca 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180305152706", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1520269961-prerelease.1520269983", + "scratch-vm": "0.1.0-prerelease.1520372503-prerelease.1520372519", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From ccb5b5958db6dc0ebf85c4c42fe9a94fc92d92d8 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 7 Mar 2018 08:46:47 -0500 Subject: [PATCH 0318/1971] Merge pull request #1577 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180306212946 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 180b4718ca..6380fe289a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1520285704", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180305152706", + "scratch-paint": "0.2.0-prerelease.20180306212946", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1520372503-prerelease.1520372519", From 8f40c0404ed0d8c27f4c56485df00bdeb150ff59 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 7 Mar 2018 11:36:18 -0500 Subject: [PATCH 0319/1971] Merge pull request #1584 from LLK/greenkeeper/chromedriver-2.36.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update chromedriver to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6380fe289a..355f2a86d2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -37,7 +37,7 @@ "babel-preset-es2015": "^6.22.0", "babel-preset-react": "^6.22.0", "buffer-loader": "0.0.1", - "chromedriver": "2.35.0", + "chromedriver": "2.36.0", "classnames": "2.2.5", "copy-webpack-plugin": "^4.3.0", "css-loader": "^0.28.7", From d841b2c2111f527aca154ed3745dceb95c62124a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 7 Mar 2018 15:12:08 -0500 Subject: [PATCH 0320/1971] Merge pull request #1587 from LLK/greenkeeper/react-responsive-4.0.5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-responsive to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 355f2a86d2..8adbd7ed41 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -79,7 +79,7 @@ "react-intl-redux": "0.7.0", "react-modal": "3.3.1", "react-redux": "5.0.7", - "react-responsive": "4.0.3", + "react-responsive": "4.0.5", "react-style-proptype": "3.2.0", "react-tabs": "2.2.1", "react-test-renderer": "16.2.0", From ccd0efd1f66531d9435c3ec41bf0d6e2893c4842 Mon Sep 17 00:00:00 2001 From: kchadha Date: Wed, 7 Mar 2018 17:15:04 -0500 Subject: [PATCH 0321/1971] Merge pull request #1532 from kchadha/report-mouse-up Always report mouse-up to VM and send flag indicating end of drag --- packages/scratch-gui/src/containers/stage.jsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index c386112b38..cf10d50019 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -186,18 +186,18 @@ class Stage extends React.Component { mouseDown: false, mouseDownPosition: null }); + const data = { + isDown: false, + x: x - this.rect.left, + y: y - this.rect.top, + canvasWidth: this.rect.width, + canvasHeight: this.rect.height, + wasDragged: this.state.isDragging + }; if (this.state.isDragging) { this.onStopDrag(); - } else { - const data = { - isDown: false, - x: x - this.rect.left, - y: y - this.rect.top, - canvasWidth: this.rect.width, - canvasHeight: this.rect.height - }; - this.props.vm.postIOData('mouse', data); } + this.props.vm.postIOData('mouse', data); } onMouseDown (e) { this.updateRect(); From 46f2f716ec1d8de5368d47d99b002a9b6c996647 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 8 Mar 2018 08:52:52 -0500 Subject: [PATCH 0322/1971] Merge pull request #1589 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180307192639 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8adbd7ed41..d7c0fa3637 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1520285704", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180306212946", + "scratch-paint": "0.2.0-prerelease.20180307192639", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1520372503-prerelease.1520372519", From 36debe8181e8c51ddd3e9eca2c012820d0ceddcb Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 8 Mar 2018 10:10:16 -0500 Subject: [PATCH 0323/1971] Merge pull request #1590 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1520519504 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d7c0fa3637..e1f574da0b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1516198804", - "scratch-blocks": "0.1.0-prerelease.1520285704", + "scratch-blocks": "0.1.0-prerelease.1520519504", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180307192639", "scratch-render": "0.1.0-prerelease.1516837442", From 003c7603713479add6da87e02ea54e852ecaca8f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 8 Mar 2018 13:02:46 -0500 Subject: [PATCH 0324/1971] Merge pull request #1592 from LLK/greenkeeper/react-style-proptype-3.2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-style-proptype to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e1f574da0b..7fec62aa73 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -80,7 +80,7 @@ "react-modal": "3.3.1", "react-redux": "5.0.7", "react-responsive": "4.0.5", - "react-style-proptype": "3.2.0", + "react-style-proptype": "3.2.1", "react-tabs": "2.2.1", "react-test-renderer": "16.2.0", "react-tooltip": "3.4.0", From 88309fff2ebc1fcfddc6d84fa4cd796647514027 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 9 Mar 2018 09:02:38 -0500 Subject: [PATCH 0325/1971] Merge pull request #1595 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180308191417 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7fec62aa73..94cd098b77 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1520519504", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180307192639", + "scratch-paint": "0.2.0-prerelease.20180308191417", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1520372503-prerelease.1520372519", From 2440583f8deb84792d387fff9cacb9e88aa42cfd Mon Sep 17 00:00:00 2001 From: kchadha Date: Fri, 9 Mar 2018 10:57:43 -0500 Subject: [PATCH 0326/1971] Merge pull request #1600 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1520606673-prerelease.1520606691 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 94cd098b77..9c7b3a6775 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180308191417", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1520372503-prerelease.1520372519", + "scratch-vm": "0.1.0-prerelease.1520606673-prerelease.1520606691", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From ab60f27d457c87a25f3b729c5c66f350c2e84457 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 9 Mar 2018 13:23:54 -0500 Subject: [PATCH 0327/1971] Merge pull request #1601 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180309170325 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9c7b3a6775..27814ab0c7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1520519504", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180308191417", + "scratch-paint": "0.2.0-prerelease.20180309170325", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1520606673-prerelease.1520606691", From c143c6557cecfbf009ff755947325b4a392017ef Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 15 Mar 2018 17:23:31 -0400 Subject: [PATCH 0328/1971] Merge pull request #1625 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1521142086 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.152… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 27814ab0c7..4e83821d82 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1516198804", - "scratch-blocks": "0.1.0-prerelease.1520519504", + "scratch-blocks": "0.1.0-prerelease.1521142086", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180309170325", "scratch-render": "0.1.0-prerelease.1516837442", From e59b1e55fb47a1a7f783f48b22668c2acfa0b630 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 16 Mar 2018 15:08:16 -0400 Subject: [PATCH 0329/1971] Merge pull request #1596 from fsih/cleanUpPaintWrapper Clean up some unused prop warnings --- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 8e7a0ca17d..ab1649cbcb 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -99,9 +99,7 @@ const vmListenerHOC = function (WrappedComponent) { attachKeyboardEvents: true }; const mapStateToProps = state => ({ - vm: state.vm, - hoveredSprite: state.hoveredTarget.sprite, - editingTarget: state.targets.editingTarget + vm: state.vm }); const mapDispatchToProps = dispatch => ({ onTargetsUpdate: data => { From 0eb2bf24b6d9ec5895eadab1276d09cb5581e5e9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 19 Mar 2018 12:44:43 -0700 Subject: [PATCH 0330/1971] Merge pull request #1616 from LLK/greenkeeper/react-modal-3.3.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-modal to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4e83821d82..49cf76f46f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -77,7 +77,7 @@ "react-ga": "2.4.1", "react-intl": "2.4.0", "react-intl-redux": "0.7.0", - "react-modal": "3.3.1", + "react-modal": "3.3.2", "react-redux": "5.0.7", "react-responsive": "4.0.5", "react-style-proptype": "3.2.1", From 532c58f7e15d8e2777c9c54cd1b97582a9ae4562 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 19 Mar 2018 17:01:22 -0400 Subject: [PATCH 0331/1971] Merge pull request #1620 from ericrosenbaum/bugfix/stable-scroll-position Hold scroll position on switching sprites --- packages/scratch-gui/src/containers/blocks.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index bedbbdbade..504291036f 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -95,9 +95,11 @@ class Blocks extends React.Component { } if (prevProps.toolboxXML !== this.props.toolboxXML) { - const selectedCategoryName = this.workspace.toolbox_.getSelectedItem().name_; + const categoryName = this.workspace.toolbox_.getSelectedCategoryName(); + const offset = this.workspace.toolbox_.getCategoryScrollOffset(); this.workspace.updateToolbox(this.props.toolboxXML); - this.workspace.toolbox_.setSelectedCategoryByName(selectedCategoryName); + const currentCategoryPos = this.workspace.toolbox_.getCategoryPositionByName(categoryName); + this.workspace.toolbox_.setFlyoutScrollPos(currentCategoryPos + offset); } if (this.props.isVisible === prevProps.isVisible) { return; From 8e6a849b59aaceb1abf2be2cac2a96d15536ff22 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 20 Mar 2018 06:29:57 -0700 Subject: [PATCH 0332/1971] Merge pull request #1633 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1521481406-prerelease.1521481427 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.1.0-prerelease.1521481… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 49cf76f46f..0262c91371 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180309170325", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1520606673-prerelease.1520606691", + "scratch-vm": "0.1.0-prerelease.1521481406-prerelease.1521481427", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From 81578bbcc31756b8709a60dc56eba8112768d0c1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 20 Mar 2018 06:30:13 -0700 Subject: [PATCH 0333/1971] Merge pull request #1634 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180316191214 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0262c91371..8ed741f2aa 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1521142086", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180309170325", + "scratch-paint": "0.2.0-prerelease.20180316191214", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1521481406-prerelease.1521481427", From 0281ea94567fd475a79fab302fdbe1d24fe77355 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 20 Mar 2018 10:33:47 -0400 Subject: [PATCH 0334/1971] Merge pull request #1631 from fsih/noPreventDefaultKeyboard Don't prevent default on keyboard events --- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index ab1649cbcb..3f1453b548 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -52,11 +52,6 @@ const vmListenerHOC = function (WrappedComponent) { keyCode: e.keyCode, isDown: true }); - - // Don't stop browser keyboard shortcuts - if (e.metaKey || e.altKey || e.ctrlKey) return; - - e.preventDefault(); } handleKeyUp (e) { // Always capture up events, From 89d15b34f210ad3fdc570d8188a82d7828aa7f7a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 20 Mar 2018 11:01:24 -0700 Subject: [PATCH 0335/1971] Merge pull request #1639 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1521560313 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8ed741f2aa..2ed5ff0e8f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1516198804", - "scratch-blocks": "0.1.0-prerelease.1521142086", + "scratch-blocks": "0.1.0-prerelease.1521560313", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180316191214", "scratch-render": "0.1.0-prerelease.1516837442", From a031be7aa3d56f169b0593fbb03ef0de6e06048d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 23 Mar 2018 09:31:28 -0400 Subject: [PATCH 0336/1971] Merge pull request #1641 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180320181154 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2ed5ff0e8f..043bbcd5aa 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1521560313", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180316191214", + "scratch-paint": "0.2.0-prerelease.20180320181154", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1521481406-prerelease.1521481427", From e1c965876a32ac302b4659d18fed3e6156c2750e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 23 Mar 2018 09:39:56 -0400 Subject: [PATCH 0337/1971] Merge pull request #1656 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1521811349 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 043bbcd5aa..caff1c1df6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1516198804", - "scratch-blocks": "0.1.0-prerelease.1521560313", + "scratch-blocks": "0.1.0-prerelease.1521811349", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180320181154", "scratch-render": "0.1.0-prerelease.1516837442", From 814a99dae05f077f721bd698b639dbdea96a1387 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 23 Mar 2018 09:51:49 -0400 Subject: [PATCH 0338/1971] Merge pull request #1657 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1521811835-prerelease.1521811852 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.1.0-prerelease.1521811… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index caff1c1df6..3db9d60283 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180320181154", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1521481406-prerelease.1521481427", + "scratch-vm": "0.1.0-prerelease.1521811835-prerelease.1521811852", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From 946162c39613783f884c8e7cca372024be4657d4 Mon Sep 17 00:00:00 2001 From: kchadha Date: Fri, 23 Mar 2018 10:48:11 -0400 Subject: [PATCH 0339/1971] Merge pull request #1621 from kchadha/loading-states Load Button Loading/Error State --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3db9d60283..38bd016867 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180320181154", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1521811835-prerelease.1521811852", + "scratch-vm": "0.1.0-prerelease.1521814818-prerelease.1521814838", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From b6076512ca5852cfcd8a9f49997410c825459ebb Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 26 Mar 2018 11:58:19 -0400 Subject: [PATCH 0340/1971] Merge pull request #1660 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.1521830325 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 38bd016867..15ca6a5df2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,9 +92,9 @@ "scratch-blocks": "0.1.0-prerelease.1521811349", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180320181154", - "scratch-render": "0.1.0-prerelease.1516837442", + "scratch-render": "0.1.0-prerelease.1521830325", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1521814818-prerelease.1521814838", + "scratch-vm": "0.1.0-prerelease.1522071980-prerelease.1522072000", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From 0d783c75801b1757bc810cdbfc96a410bb564fc3 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 26 Mar 2018 12:33:15 -0400 Subject: [PATCH 0341/1971] Merge pull request #1665 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180326153721 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 15ca6a5df2..7a9dd06867 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1521811349", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180320181154", + "scratch-paint": "0.2.0-prerelease.20180326153721", "scratch-render": "0.1.0-prerelease.1521830325", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1522071980-prerelease.1522072000", From 25fb65b3539c12658e5b738154dd932e738dc833 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 26 Mar 2018 13:07:43 -0400 Subject: [PATCH 0342/1971] Merge pull request #1640 from LLK/greenkeeper/chromedriver-2.37.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update chromedriver to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7a9dd06867..afcbdd259e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -37,7 +37,7 @@ "babel-preset-es2015": "^6.22.0", "babel-preset-react": "^6.22.0", "buffer-loader": "0.0.1", - "chromedriver": "2.36.0", + "chromedriver": "2.37.0", "classnames": "2.2.5", "copy-webpack-plugin": "^4.3.0", "css-loader": "^0.28.7", From 90676dc6251d6d7cfca514001230c6740d99689d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 26 Mar 2018 13:07:59 -0400 Subject: [PATCH 0343/1971] Merge pull request #1664 from LLK/greenkeeper/react-responsive-4.1.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-responsive to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index afcbdd259e..85f9b8dbc7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -79,7 +79,7 @@ "react-intl-redux": "0.7.0", "react-modal": "3.3.2", "react-redux": "5.0.7", - "react-responsive": "4.0.5", + "react-responsive": "4.1.0", "react-style-proptype": "3.2.1", "react-tabs": "2.2.1", "react-test-renderer": "16.2.0", From a4209466578417665b37c6fd484a230308e7a6c2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 28 Mar 2018 09:01:49 -0400 Subject: [PATCH 0344/1971] Merge pull request #1676 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180328125648 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 85f9b8dbc7..87361a6253 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1521811349", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180326153721", + "scratch-paint": "0.2.0-prerelease.20180328125648", "scratch-render": "0.1.0-prerelease.1521830325", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1522071980-prerelease.1522072000", From f4c1e9e8154135d0b979e96489646181abb536e3 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 28 Mar 2018 09:44:34 -0400 Subject: [PATCH 0345/1971] Merge pull request #1677 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180328130249 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 87361a6253..544cade35d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1521811349", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180328125648", + "scratch-paint": "0.2.0-prerelease.20180328130249", "scratch-render": "0.1.0-prerelease.1521830325", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1522071980-prerelease.1522072000", From df199f53e73d3d3e4b4fd3684f00d58fedbe582b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 28 Mar 2018 15:19:17 -0400 Subject: [PATCH 0346/1971] Merge pull request #1679 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1522249865-prerelease.1522249883 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 544cade35d..a46700619a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180328130249", "scratch-render": "0.1.0-prerelease.1521830325", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1522071980-prerelease.1522072000", + "scratch-vm": "0.1.0-prerelease.1522249865-prerelease.1522249883", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From ac310c4a3fdc0e28f6c15f73c3764a4357ad4d9c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 29 Mar 2018 10:01:31 -0400 Subject: [PATCH 0347/1971] Merge pull request #1683 from rschamp/feature/menubar Mock-up menu bar --- .../src/components/menu-bar/menu-bar.jsx | 156 +++++++++++++++--- 1 file changed, 132 insertions(+), 24 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 5807294541..587b51fc4b 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -6,15 +6,18 @@ import React from 'react'; import Box from '../box/box.jsx'; import Button from '../button/button.jsx'; -import LoadButton from '../../containers/load-button.jsx'; -import SaveButton from '../../containers/save-button.jsx'; +import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx'; import LanguageSelector from '../../containers/language-selector.jsx'; import {openFeedbackForm} from '../../reducers/modals'; import styles from './menu-bar.css'; +import mystuffIcon from './icon--mystuff.png'; +import profileIcon from './icon--profile.png'; import feedbackIcon from './icon--feedback.svg'; +import communityIcon from './icon--see-community.svg'; +import dropdownCaret from '../language-selector/dropdown-caret.svg'; import scratchLogo from './scratch-logo.svg'; const MenuBar = props => ( @@ -24,37 +27,142 @@ const MenuBar = props => ( })} >
-
- Scratch +
+
+ Scratch +
+
+ + + +
+
+ +
File
+
+
+
+ +
Edit
+
+
+
+
+
+ + + +
+
+ + + +
+
+ + +
- - -
-
+
+
+ +
+ +
+
+ +
+ + scratch-cat + +
+
+
); From fec76120aeb6b6887419d219cae95c43fb9ba811 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 29 Mar 2018 10:01:43 -0400 Subject: [PATCH 0348/1971] Merge pull request #1682 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1522264400 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.152… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a46700619a..07dc438e64 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1516198804", - "scratch-blocks": "0.1.0-prerelease.1521811349", + "scratch-blocks": "0.1.0-prerelease.1522264400", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180328130249", "scratch-render": "0.1.0-prerelease.1521830325", From 336c46c0c6573582bd51a5396f714b2ec287f986 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 29 Mar 2018 14:17:51 -0400 Subject: [PATCH 0349/1971] Merge pull request #1685 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180329174415 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 07dc438e64..c525b9c6ce 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1522264400", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180328130249", + "scratch-paint": "0.2.0-prerelease.20180329174415", "scratch-render": "0.1.0-prerelease.1521830325", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1522249865-prerelease.1522249883", From 77a4bb5cc02baad8da0d4163be46a94b6ff56ef6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 29 Mar 2018 14:18:43 -0400 Subject: [PATCH 0350/1971] Merge pull request #1686 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.1522346711 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c525b9c6ce..7b3409a350 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "scratch-blocks": "0.1.0-prerelease.1522264400", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180329174415", - "scratch-render": "0.1.0-prerelease.1521830325", + "scratch-render": "0.1.0-prerelease.1522346711", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1522249865-prerelease.1522249883", "selenium-webdriver": "3.6.0", From 57141f96b1ea1e42521f80ac768316dc550518ef Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 29 Mar 2018 15:38:45 -0400 Subject: [PATCH 0351/1971] Merge pull request #1688 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180329193314 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7b3409a350..bdf956c72e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1522264400", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180329174415", + "scratch-paint": "0.2.0-prerelease.20180329193314", "scratch-render": "0.1.0-prerelease.1522346711", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1522249865-prerelease.1522249883", From 40437c614706cf1c0964fb397e36a2ff0c2af008 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 29 Mar 2018 16:18:15 -0400 Subject: [PATCH 0352/1971] Merge pull request #1689 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180329200323 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index bdf956c72e..6bebf8b541 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1522264400", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180329193314", + "scratch-paint": "0.2.0-prerelease.20180329200323", "scratch-render": "0.1.0-prerelease.1522346711", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1522249865-prerelease.1522249883", From c5ea196c3d6e567aa4c1a10a902908583150d2eb Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 30 Mar 2018 09:50:37 -0400 Subject: [PATCH 0353/1971] Merge pull request #1696 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1522355889-prerelease.1522355906 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6bebf8b541..61ec4fcaf8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180329200323", "scratch-render": "0.1.0-prerelease.1522346711", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1522249865-prerelease.1522249883", + "scratch-vm": "0.1.0-prerelease.1522355889-prerelease.1522355906", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From a798f2b76a9a8f1c314f86be1be655c0e7c5feb7 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 30 Mar 2018 13:24:41 -0400 Subject: [PATCH 0354/1971] Merge pull request #1698 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180330172108 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 61ec4fcaf8..87418ac3eb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1522264400", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180329200323", + "scratch-paint": "0.2.0-prerelease.20180330172108", "scratch-render": "0.1.0-prerelease.1522346711", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1522355889-prerelease.1522355906", From cd1028703a38568d7b27a582147cc04ee8239b88 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 2 Apr 2018 13:08:44 -0400 Subject: [PATCH 0355/1971] Merge pull request #1706 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180402150533 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 87418ac3eb..3210f4a80e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1522264400", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180330172108", + "scratch-paint": "0.2.0-prerelease.20180402150533", "scratch-render": "0.1.0-prerelease.1522346711", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1522355889-prerelease.1522355906", From 967fb6061521afe3f7a82c933106f10c6a8e43bb Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 3 Apr 2018 11:30:07 -0400 Subject: [PATCH 0356/1971] Merge pull request #1711 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180403143543 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3210f4a80e..e9f49338f3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1522264400", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180402150533", + "scratch-paint": "0.2.0-prerelease.20180403143543", "scratch-render": "0.1.0-prerelease.1522346711", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1522355889-prerelease.1522355906", From f14f1b9e8c641da170192a3a32eadfc0d2f2f91e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 4 Apr 2018 09:25:57 -0400 Subject: [PATCH 0357/1971] Merge pull request #1716 from bartljak/issue1691 Language Selector Tool Tip Fix --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 587b51fc4b..3335b273fa 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -39,7 +39,7 @@ const MenuBar = props => (
From d84a262439e5bf40733ab69176f71cf4e86f4b30 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 4 Apr 2018 11:18:42 -0400 Subject: [PATCH 0358/1971] Merge pull request #1715 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180403230109 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e9f49338f3..18833429af 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1522264400", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180403143543", + "scratch-paint": "0.2.0-prerelease.20180403230109", "scratch-render": "0.1.0-prerelease.1522346711", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1522355889-prerelease.1522355906", From 3f4338d6d28a24ff946a5907de9834036391cc61 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 5 Apr 2018 14:11:40 -0400 Subject: [PATCH 0359/1971] Merge pull request #1723 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1522944347-prerelease.1522944370 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.1.0-prerelease.1522944… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 18833429af..b0749f5965 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180403230109", "scratch-render": "0.1.0-prerelease.1522346711", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1522355889-prerelease.1522355906", + "scratch-vm": "0.1.0-prerelease.1522944347-prerelease.1522944370", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From 9e49d727654bb313a401d99282e372b19b876b39 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 5 Apr 2018 14:12:05 -0400 Subject: [PATCH 0360/1971] Merge pull request #1724 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180404190532 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b0749f5965..f23c00f035 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1522264400", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180403230109", + "scratch-paint": "0.2.0-prerelease.20180404190532", "scratch-render": "0.1.0-prerelease.1522346711", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1522944347-prerelease.1522944370", From 92f03a93bc89122b7fa12b2cd9cf39a1a5298230 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 5 Apr 2018 19:03:05 -0400 Subject: [PATCH 0361/1971] Merge pull request #1729 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1522969014-prerelease.1522969034 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f23c00f035..685395e2c3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180404190532", "scratch-render": "0.1.0-prerelease.1522346711", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1522944347-prerelease.1522944370", + "scratch-vm": "0.1.0-prerelease.1522969014-prerelease.1522969034", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From 9f766e1397e3465c7b63e03b0be9540eaa9a6970 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 6 Apr 2018 10:43:42 -0400 Subject: [PATCH 0362/1971] Merge pull request #1733 from LLK/greenkeeper/scratch-vm-0.1.0-prerelease.1523023502-prerelease.1523023526 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 685395e2c3..448a6eebca 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180404190532", "scratch-render": "0.1.0-prerelease.1522346711", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1522969014-prerelease.1522969034", + "scratch-vm": "0.1.0-prerelease.1523024495", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From e97757bbc1ea934b2c65dd44e2980e3baf1fa2fd Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Fri, 6 Apr 2018 10:29:42 -0700 Subject: [PATCH 0363/1971] Merge pull request #1725 from cwillisf/fix-handle-extension-added Fix handleExtensionAdded when no targets present --- packages/scratch-gui/src/containers/blocks.jsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 504291036f..278a649bd2 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -221,11 +221,18 @@ class Blocks extends React.Component { } } handleExtensionAdded (blocksInfo) { - this.ScratchBlocks.defineBlocksWithJsonArray(blocksInfo.map(blockInfo => blockInfo.json)); - const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(); - const target = this.props.vm.editingTarget; - const toolboxXML = makeToolboxXML(target.isStage, target.id, dynamicBlocksXML); - this.props.updateToolboxState(toolboxXML); + // select JSON from each block info object then reject the pseudo-blocks which don't have JSON, like separators + // this actually defines blocks and MUST run regardless of the UI state + this.ScratchBlocks.defineBlocksWithJsonArray(blocksInfo.map(blockInfo => blockInfo.json).filter(x => x)); + + // update the toolbox view: this can be skipped if we're not looking at a target, etc. + const runtime = this.props.vm.runtime; + const target = runtime.getEditingTarget() || runtime.getTargetForStage(); + if (target) { + const dynamicBlocksXML = runtime.getBlocksXML(); + const toolboxXML = makeToolboxXML(target.isStage, target.id, dynamicBlocksXML); + this.props.updateToolboxState(toolboxXML); + } } handleBlocksInfoUpdate (blocksInfo) { // @todo Later we should replace this to avoid all the warnings from redefining blocks. From 77b051532c82e74d3d53d1243338322147868d31 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 9 Apr 2018 15:02:54 -0400 Subject: [PATCH 0364/1971] Merge pull request #1748 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1523296754 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.152… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 448a6eebca..a92b220c04 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1516198804", - "scratch-blocks": "0.1.0-prerelease.1522264400", + "scratch-blocks": "0.1.0-prerelease.1523296754", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180404190532", "scratch-render": "0.1.0-prerelease.1522346711", From 3d24f0d99f8e3860a589ad58787fb05ec193548b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 9 Apr 2018 15:03:06 -0400 Subject: [PATCH 0365/1971] Merge pull request #1749 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180409173029 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a92b220c04..7592c72920 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1523296754", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180404190532", + "scratch-paint": "0.2.0-prerelease.20180409173029", "scratch-render": "0.1.0-prerelease.1522346711", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1523024495", From 761ef074f1e94f9033c521ad539627c48271b7d5 Mon Sep 17 00:00:00 2001 From: kchadha Date: Mon, 9 Apr 2018 16:40:35 -0400 Subject: [PATCH 0366/1971] Merge pull request #1752 from paulkaplan/update-vm-04-09-18 Update VM manually in package json --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7592c72920..f322032a0f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180409173029", "scratch-render": "0.1.0-prerelease.1522346711", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1523024495", + "scratch-vm": "0.1.0-prerelease.1523297324", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From 26bc2e31c891a8be5ee20cedf983ee15cc46d985 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 11 Apr 2018 09:19:21 -0400 Subject: [PATCH 0367/1971] Merge pull request #1755 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180410152401 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f322032a0f..ff0f9adcb7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1523296754", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180409173029", + "scratch-paint": "0.2.0-prerelease.20180410152401", "scratch-render": "0.1.0-prerelease.1522346711", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1523297324", From bf85b94b5209992e2059baf6e92330b6115ca762 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 11 Apr 2018 13:43:08 -0400 Subject: [PATCH 0368/1971] Merge pull request #1754 from paulkaplan/sprite-tile-update Add costume size/sound length details to sprite tile and update styling --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ff0f9adcb7..8a58887443 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,9 +92,9 @@ "scratch-blocks": "0.1.0-prerelease.1523296754", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180410152401", - "scratch-render": "0.1.0-prerelease.1522346711", + "scratch-render": "0.1.0-prerelease.1523453612", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1523297324", + "scratch-vm": "0.1.0-prerelease.1523454397", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From 23e724480fcaca096851a3a2b79f4e25b50b3a89 Mon Sep 17 00:00:00 2001 From: kchadha Date: Wed, 11 Apr 2018 19:57:38 -0400 Subject: [PATCH 0369/1971] Merge pull request #1738 from kchadha/default-project-precompression Update default project with new more compact serialization. --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8a58887443..409c77f193 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180410152401", "scratch-render": "0.1.0-prerelease.1523453612", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1523454397", + "scratch-vm": "0.1.0-prerelease.1523489036", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From 16c57dffd121bd52f89920431990f5a25152336b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 12 Apr 2018 11:50:19 -0400 Subject: [PATCH 0370/1971] Merge pull request #1763 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1523542868 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 409c77f193..ae1a1eb689 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1516198804", - "scratch-blocks": "0.1.0-prerelease.1523296754", + "scratch-blocks": "0.1.0-prerelease.1523542868", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180410152401", "scratch-render": "0.1.0-prerelease.1523453612", From f842fb93d755dc2e012c765d24bd74790c4bf894 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 13 Apr 2018 09:11:08 -0400 Subject: [PATCH 0371/1971] Merge pull request #1764 from paulkaplan/sprite-drag Add editor drag mode for popping sprites of stage. --- packages/scratch-gui/src/containers/stage.jsx | 123 +++++++++++++++--- 1 file changed, 107 insertions(+), 16 deletions(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index cf10d50019..f3444af1a4 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -34,7 +34,11 @@ class Stage extends React.Component { 'onWheel', 'updateRect', 'questionListener', - 'setCanvas' + 'setCanvas', + 'setDragCanvas', + 'clearDragCanvas', + 'drawDragCanvas', + 'positionDragCanvas' ]); this.state = { mouseDownTimeoutId: null, @@ -164,12 +168,18 @@ class Stage extends React.Component { } } if (this.state.mouseDown && this.state.isDragging) { - const spritePosition = this.getScratchCoords(mousePosition[0], mousePosition[1]); - this.props.vm.postSpriteInfo({ - x: spritePosition[0] + this.state.dragOffset[0], - y: -(spritePosition[1] + this.state.dragOffset[1]), - force: true - }); + // Editor drag style only updates the drag canvas, does full update at the end of drag + // Non-editor drag style just updates the sprite continuously. + if (this.props.useEditorDragStyle) { + this.positionDragCanvas(mousePosition[0], mousePosition[1]); + } else { + const spritePosition = this.getScratchCoords(mousePosition[0], mousePosition[1]); + this.props.vm.postSpriteInfo({ + x: spritePosition[0] + this.state.dragOffset[0], + y: -(spritePosition[1] + this.state.dragOffset[1]), + force: true + }); + } } const coordinates = { x: mousePosition[0], @@ -181,6 +191,7 @@ class Stage extends React.Component { } onMouseUp (e) { const {x, y} = getEventXY(e); + const mousePosition = [x - this.rect.left, y - this.rect.top]; this.cancelMouseDownTimeout(); this.setState({ mouseDown: false, @@ -195,7 +206,7 @@ class Stage extends React.Component { wasDragged: this.state.isDragging }; if (this.state.isDragging) { - this.onStopDrag(); + this.onStopDrag(mousePosition[0], mousePosition[1]); } this.props.vm.postIOData('mouse', data); } @@ -248,11 +259,49 @@ class Stage extends React.Component { } this.setState({mouseDownTimeoutId: null}); } + drawDragCanvas (drawableData) { + const { + data, + width, + height, + x, + y + } = drawableData; + this.dragCanvas.width = width; + this.dragCanvas.height = height; + // Need to convert uint8array from WebGL readPixels into Uint8ClampedArray + // for ImageData constructor. Shares underlying buffer, so it is fast. + const imageData = new ImageData( + new Uint8ClampedArray(data.buffer), width, height); + this.dragCanvas.getContext('2d').putImageData(imageData, 0, 0); + // Position so that pick location is at (0, 0) so that positionDragCanvas() + // can use translation to move to mouse position smoothly. + this.dragCanvas.style.left = `${-x}px`; + this.dragCanvas.style.top = `${-y}px`; + this.dragCanvas.style.display = 'block'; + } + clearDragCanvas () { + this.dragCanvas.width = this.dragCanvas.height = 0; + this.dragCanvas.style.display = 'none'; + } + positionDragCanvas (mouseX, mouseY) { + // mouseX/Y are relative to stage top/left, and dragCanvas is already + // positioned so that the pick location is at (0,0). + this.dragCanvas.style.transform = `translate(${mouseX}px, ${mouseY}px)`; + } onStartDrag (x, y) { + if (this.state.dragId) return; const drawableId = this.renderer.pick(x, y); if (drawableId === null) return; const drawableData = this.renderer.extractDrawable(drawableId, x, y); const targetId = this.props.vm.getTargetIdForDrawableId(drawableId); + + // Only start drags on non-draggable targets in editor drag mode + if (!this.props.useEditorDragStyle) { + const target = this.props.vm.runtime.getTargetById(targetId); + if (!target.draggable) return; + } + if (targetId === null) return; this.props.vm.startDrag(targetId); this.setState({ @@ -260,28 +309,63 @@ class Stage extends React.Component { dragId: targetId, dragOffset: drawableData.scratchOffset }); + if (this.props.useEditorDragStyle) { + this.drawDragCanvas(drawableData); + this.positionDragCanvas(x, y); + this.props.vm.postSpriteInfo({visible: false}); + } } - onStopDrag () { - this.props.vm.stopDrag(this.state.dragId); - this.setState({ - isDragging: false, - dragOffset: null, - dragId: null - }); + onStopDrag (mouseX, mouseY) { + const dragId = this.state.dragId; + const commonStopDragActions = () => { + this.props.vm.stopDrag(dragId); + this.setState({ + isDragging: false, + dragOffset: null, + dragId: null + }); + }; + if (this.props.useEditorDragStyle) { + // Need to sequence these actions to prevent flickering. + const spriteInfo = {visible: true}; + // First update the sprite position if dropped in the stage. + if (mouseX > 0 && mouseX < this.rect.width && + mouseY > 0 && mouseY < this.rect.height) { + const spritePosition = this.getScratchCoords(mouseX, mouseY); + spriteInfo.x = spritePosition[0] + this.state.dragOffset[0]; + spriteInfo.y = -(spritePosition[1] + this.state.dragOffset[1]); + spriteInfo.force = true; + } + this.props.vm.postSpriteInfo(spriteInfo); + // Then clear the dragging canvas and stop drag (potentially slow if selecting sprite) + setTimeout(() => { + this.clearDragCanvas(); + setTimeout(() => { + commonStopDragActions(); + }, 30); + }, 30); + } else { + commonStopDragActions(); + } } setCanvas (canvas) { this.canvas = canvas; } + setDragCanvas (canvas) { + this.dragCanvas = canvas; + } render () { const { vm, // eslint-disable-line no-unused-vars onActivateColorPicker, // eslint-disable-line no-unused-vars + useEditorDragStyle, // eslint-disable-line no-unused-vars ...props } = this.props; return ( ({ isColorPicking: state.colorPicker.active, - isFullScreen: state.stageSize.isFullScreen + isFullScreen: state.stageSize.isFullScreen, + // Do not use editor drag style in fullscreen mode. + useEditorDragStyle: !state.stageSize.isFullScreen }); const mapDispatchToProps = dispatch => ({ From 67810dee5871f920676d48391e588e395e15cd2c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 13 Apr 2018 09:18:20 -0400 Subject: [PATCH 0372/1971] Merge pull request #1766 from rschamp/menubar-dropdowns Add File and Edit menu dropdowns --- .../src/components/menu-bar/menu-bar.jsx | 259 +++++++++++++----- 1 file changed, 189 insertions(+), 70 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 3335b273fa..2114875b01 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -8,8 +8,20 @@ import Box from '../box/box.jsx'; import Button from '../button/button.jsx'; import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx'; import LanguageSelector from '../../containers/language-selector.jsx'; +import ProjectLoader from '../../containers/project-loader.jsx'; +import Menu from '../../containers/menu.jsx'; +import {MenuItem, MenuSection} from '../menu/menu.jsx'; +import ProjectSaver from '../../containers/project-saver.jsx'; import {openFeedbackForm} from '../../reducers/modals'; +import { + openFileMenu, + closeFileMenu, + fileMenuOpen, + openEditMenu, + closeEditMenu, + editMenuOpen +} from '../../reducers/menus'; import styles from './menu-bar.css'; @@ -20,15 +32,74 @@ import communityIcon from './icon--see-community.svg'; import dropdownCaret from '../language-selector/dropdown-caret.svg'; import scratchLogo from './scratch-logo.svg'; -const MenuBar = props => ( - ( + + {children} + +); + +MenuBarItemTooltip.propTypes = { + children: PropTypes.node, + className: PropTypes.string, + id: PropTypes.string, + place: PropTypes.oneOf(['top', 'bottom', 'left', 'right']) +}; + +const MenuItemTooltip = ({id, children, className}) => ( + + {children} + +); + +MenuItemTooltip.propTypes = { + children: PropTypes.node, + className: PropTypes.string, + id: PropTypes.string +}; + +const MenuBarMenu = ({ + children, + onRequestClose, + open, + place = 'right' +}) => ( + + {children} + +); + +MenuBarMenu.propTypes = { + children: PropTypes.node, + onRequestClose: PropTypes.func, + open: PropTypes.bool, + place: PropTypes.oneOf(['left', 'right']) +}; + +const MenuBar = props => ( +
-
+
Scratch ( src={scratchLogo} />
-
- + - +
-
- +
File
+ -
File
-
+ + New + + + + Save now + + + Save as a copy + + + + {(renderFileInput, loadProject, loadProps) => ( + + Upload from your computer + {renderFileInput()} + + )} + {(saveProject, saveProps) => ( + + Download to your computer + + )} + +
-
- +
Edit
+ -
Edit
-
+ + Undo + + + Redo + + + + Turbo mode + + +
-
- +
+ - +
-
- +
+ - +
-
- +
+ - +
-
+
- -
+ +
- - + -
+
( src={dropdownCaret} />
- +
); MenuBar.propTypes = { - onGiveFeedback: PropTypes.func.isRequired + editMenuOpen: PropTypes.bool, + fileMenuOpen: PropTypes.bool, + onClickEdit: PropTypes.func, + onClickFile: PropTypes.func, + onGiveFeedback: PropTypes.func.isRequired, + onRequestCloseEdit: PropTypes.func, + onRequestCloseFile: PropTypes.func }; -const mapStateToProps = () => ({}); +const mapStateToProps = state => ({ + fileMenuOpen: fileMenuOpen(state), + editMenuOpen: editMenuOpen(state) +}); const mapDispatchToProps = dispatch => ({ - onGiveFeedback: () => { - dispatch(openFeedbackForm()); - } + onGiveFeedback: () => dispatch(openFeedbackForm()), + onClickFile: () => dispatch(openFileMenu()), + onRequestCloseFile: () => dispatch(closeFileMenu()), + onClickEdit: () => dispatch(openEditMenu()), + onRequestCloseEdit: () => dispatch(closeEditMenu()) }); export default connect( From 25a9ad6aa99dfa19f3e88dd44e052f377b25cd7a Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 13 Apr 2018 09:57:38 -0400 Subject: [PATCH 0373/1971] Merge pull request #1772 from rschamp/greenkeeper-pls/upgrade-vm Update scratch-vm --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ae1a1eb689..56b969c7f3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180410152401", "scratch-render": "0.1.0-prerelease.1523453612", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1523489036", + "scratch-vm": "0.1.0-prerelease.1523625559", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From 6207186c9e54a8392493855ad6a5f6cea6288ebe Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 13 Apr 2018 11:40:33 -0400 Subject: [PATCH 0374/1971] Merge pull request #1775 from paulkaplan/upgrade-vm-04-13-18 Update vm to include video motion fixes for other browsers --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 56b969c7f3..12cac9e0b6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180410152401", "scratch-render": "0.1.0-prerelease.1523453612", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1523625559", + "scratch-vm": "0.1.0-prerelease.1523633447", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From fdc38c7f93e7c989443898ee8282a18d03b436c5 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 13 Apr 2018 14:12:38 -0400 Subject: [PATCH 0375/1971] Merge pull request #1788 from paulkaplan/update-vm-again Update vm to include fixes for video transparency --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 12cac9e0b6..afebd1c9fe 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180410152401", "scratch-render": "0.1.0-prerelease.1523453612", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1523633447", + "scratch-vm": "0.1.0-prerelease.1523642820", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From 1d8cf89b89276e4dcafcfbab398f58e510f968df Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Tue, 17 Apr 2018 15:12:07 -0400 Subject: [PATCH 0376/1971] Merge pull request #1786 from thisandagain/bugfix/feedback Remove feedback form --- .../src/components/menu-bar/menu-bar.jsx | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 2114875b01..633b8130dd 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -13,7 +13,6 @@ import Menu from '../../containers/menu.jsx'; import {MenuItem, MenuSection} from '../menu/menu.jsx'; import ProjectSaver from '../../containers/project-saver.jsx'; -import {openFeedbackForm} from '../../reducers/modals'; import { openFileMenu, closeFileMenu, @@ -27,7 +26,6 @@ import styles from './menu-bar.css'; import mystuffIcon from './icon--mystuff.png'; import profileIcon from './icon--profile.png'; -import feedbackIcon from './icon--feedback.svg'; import communityIcon from './icon--see-community.svg'; import dropdownCaret from '../language-selector/dropdown-caret.svg'; import scratchLogo from './scratch-logo.svg'; @@ -220,19 +218,6 @@ const MenuBar = props => (
-
- -
({ }); const mapDispatchToProps = dispatch => ({ - onGiveFeedback: () => dispatch(openFeedbackForm()), onClickFile: () => dispatch(openFileMenu()), onRequestCloseFile: () => dispatch(closeFileMenu()), onClickEdit: () => dispatch(openEditMenu()), From 3269fb198bfb2a86dd8b4da86bca25bb17df84f0 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 19 Apr 2018 14:41:36 -0400 Subject: [PATCH 0377/1971] Merge pull request #1819 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1524157404 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.152… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index afebd1c9fe..163a8ee6be 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1516198804", - "scratch-blocks": "0.1.0-prerelease.1523542868", + "scratch-blocks": "0.1.0-prerelease.1524157404", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180410152401", "scratch-render": "0.1.0-prerelease.1523453612", From 04ddc139e362ae73b1f1ec0ccfa0486675c01e52 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 20 Apr 2018 09:27:50 -0400 Subject: [PATCH 0378/1971] Merge pull request #1822 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1524163302 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 163a8ee6be..6a996aaa8b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1516198804", - "scratch-blocks": "0.1.0-prerelease.1524157404", + "scratch-blocks": "0.1.0-prerelease.1524163302", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180410152401", "scratch-render": "0.1.0-prerelease.1523453612", From 409dc9ad1df4b43dbb494579bf78efbab32e469b Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 20 Apr 2018 10:38:25 -0400 Subject: [PATCH 0379/1971] Merge pull request #1824 from rschamp/feature/library-phase-ii Libraries Phase II --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 633b8130dd..f647b24df5 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -7,6 +7,7 @@ import React from 'react'; import Box from '../box/box.jsx'; import Button from '../button/button.jsx'; import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx'; +import Divider from '../divider/divider.jsx'; import LanguageSelector from '../../containers/language-selector.jsx'; import ProjectLoader from '../../containers/project-loader.jsx'; import Menu from '../../containers/menu.jsx'; @@ -181,7 +182,7 @@ const MenuBar = props => (
-
+
Date: Fri, 20 Apr 2018 12:19:45 -0400 Subject: [PATCH 0380/1971] Merge pull request #1830 from kchadha/update-scratch-vm Update scratch-vm version. --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6a996aaa8b..a9ce2c894e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180410152401", "scratch-render": "0.1.0-prerelease.1523453612", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1523642820", + "scratch-vm": "0.1.0-prerelease.1524239808", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From c649b0e8b095bbbcfeac415d66e164ac5b39f427 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 20 Apr 2018 13:11:58 -0400 Subject: [PATCH 0381/1971] Merge pull request #1829 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180420154430 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a9ce2c894e..c921215839 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1524163302", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180410152401", + "scratch-paint": "0.2.0-prerelease.20180420154430", "scratch-render": "0.1.0-prerelease.1523453612", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1524239808", From f8081c27458781b41dd3f493706e57c9d9e668d9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 20 Apr 2018 13:15:19 -0400 Subject: [PATCH 0382/1971] Merge pull request #1821 from LLK/greenkeeper/chromedriver-2.38.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update chromedriver to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c921215839..f7c8bc9430 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -37,7 +37,7 @@ "babel-preset-es2015": "^6.22.0", "babel-preset-react": "^6.22.0", "buffer-loader": "0.0.1", - "chromedriver": "2.37.0", + "chromedriver": "2.38.0", "classnames": "2.2.5", "copy-webpack-plugin": "^4.3.0", "css-loader": "^0.28.7", From b61d9b7ab501f906166e8982da935e4eed8cbc76 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 20 Apr 2018 16:10:01 -0400 Subject: [PATCH 0383/1971] Merge pull request #1832 from LLK/revert-1829-greenkeeper/scratch-paint-0.2.0-prerelease.20180420154430 Revert bitmap paint editor PR for now --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f7c8bc9430..0d8b55d889 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1524163302", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180420154430", + "scratch-paint": "0.2.0-prerelease.20180410152401", "scratch-render": "0.1.0-prerelease.1523453612", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1524239808", From 7b44a41140eaabd95f458b3c34b0b4bbe034e21e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 20 Apr 2018 16:13:11 -0400 Subject: [PATCH 0384/1971] Merge pull request #1834 from paulkaplan/update-vm-mp3 Update the vm to include fixes for downloading imported sounds --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0d8b55d889..03c60e8f0a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180410152401", "scratch-render": "0.1.0-prerelease.1523453612", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1524239808", + "scratch-vm": "0.1.0-prerelease.1524254811", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From ea5a65c17afac85306e8b52cef62515ea0f1ba2e Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Mon, 23 Apr 2018 10:04:35 -0400 Subject: [PATCH 0385/1971] Merge pull request #1448 from chrisgarrity/feature/untangle-redux Export GUI for use on www! --- packages/scratch-gui/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 03c60e8f0a..9ab75c221d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -2,14 +2,14 @@ "name": "scratch-gui", "version": "0.1.0", "description": "GraphicaL User Interface for creating and running Scratch 3.0 projects", - "main": "./src/index.js", + "main": "./dist/scratch-gui.js", "scripts": { "build": "npm run clean && webpack --progress --colors --bail", - "clean": "rimraf ./build && mkdirp build", + "clean": "rimraf ./build && mkdirp build && rimraf ./dist && mkdirp dist", "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", "i18n:src": "babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/ ./translations/", "start": "webpack-dev-server", - "test": "npm run test:lint && npm run test:unit && NODE_ENV=production npm run build && npm run test:integration", + "test": "npm run test:lint && npm run test:unit && npm run build && npm run test:integration", "test:integration": "jest --runInBand test[\\\\/]integration", "test:lint": "eslint . --ext .js,.jsx", "test:unit": "jest test[\\\\/]unit", From 7bf8bd2519c57adae65c73c5a063164feb65b0c2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 23 Apr 2018 10:16:05 -0400 Subject: [PATCH 0386/1971] Merge pull request #1833 from LLK/revert-1832-revert-1829-greenkeeper/scratch-paint-0.2.0-prerelease.20180420154430 Bring in the latest bitmap editor from scratch-paint --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9ab75c221d..6d185bfecd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1524163302", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180410152401", + "scratch-paint": "0.2.0-prerelease.20180420154430", "scratch-render": "0.1.0-prerelease.1523453612", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1524254811", From 3afa6f68fe5991cadf1366323be8deca48a568f8 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 23 Apr 2018 13:32:37 -0400 Subject: [PATCH 0387/1971] Merge pull request #1851 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180423143518 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6d185bfecd..903444e21b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1516198804", "scratch-blocks": "0.1.0-prerelease.1524163302", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180420154430", + "scratch-paint": "0.2.0-prerelease.20180423143518", "scratch-render": "0.1.0-prerelease.1523453612", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1524254811", From 59a8021c3d88e66538b2cc2eadef7957bd7dfcf2 Mon Sep 17 00:00:00 2001 From: kchadha Date: Mon, 23 Apr 2018 15:19:25 -0400 Subject: [PATCH 0388/1971] Merge pull request #1846 from kchadha/custom-proc-shadows Support new scratch-blocks API for translating custom proc mutations --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 903444e21b..432d01e293 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1516198804", - "scratch-blocks": "0.1.0-prerelease.1524163302", + "scratch-blocks": "0.1.0-prerelease.1524267558", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180423143518", "scratch-render": "0.1.0-prerelease.1523453612", From 4e5c6c1a92b10eaa35a5dfe35d1f2bacdfefc83d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 24 Apr 2018 15:40:08 -0400 Subject: [PATCH 0389/1971] Merge pull request #1861 from LLK/greenkeeper-pls/scratch-vm-0.1.0-prerelease.1524520946 Update scratch-vm --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 432d01e293..f1b1de9fb9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180423143518", "scratch-render": "0.1.0-prerelease.1523453612", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1524254811", + "scratch-vm": "0.1.0-prerelease.1524520946", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From 0b3e8d0f634606ee5c7da43827293c6232fb6595 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 24 Apr 2018 16:17:43 -0400 Subject: [PATCH 0390/1971] Merge pull request #1862 from LLK/revert-1861-greenkeeper-pls/scratch-vm-0.1.0-prerelease.1524520946 Revert "Update scratch-vm" --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f1b1de9fb9..432d01e293 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180423143518", "scratch-render": "0.1.0-prerelease.1523453612", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1524520946", + "scratch-vm": "0.1.0-prerelease.1524254811", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From 9959d961e8280bf8d756873c132ab186bf4969f0 Mon Sep 17 00:00:00 2001 From: kchadha Date: Wed, 25 Apr 2018 11:49:51 -0400 Subject: [PATCH 0391/1971] Merge pull request #1836 from kchadha/costume-file-upload Upload Costumes and Backdrops (vector and bitmap) --- packages/scratch-gui/package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 432d01e293..be23d9f7dc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,9 +92,10 @@ "scratch-blocks": "0.1.0-prerelease.1524267558", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180423143518", - "scratch-render": "0.1.0-prerelease.1523453612", + "scratch-render": "0.1.0-prerelease.20180423214437", + "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1524254811", + "scratch-vm": "0.1.0-prerelease.1524520946", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From b5288a0916b3ad6c70387a4190ed101d15aa331b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 25 Apr 2018 15:48:40 -0400 Subject: [PATCH 0392/1971] Merge pull request #1871 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20180425181730 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index be23d9f7dc..eb61763e06 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "scratch-blocks": "0.1.0-prerelease.1524267558", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180423143518", - "scratch-render": "0.1.0-prerelease.20180423214437", + "scratch-render": "0.1.0-prerelease.20180425181730", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1524520946", From 7c0bd67a7307db9017c7056b54554d4eb59cf1f6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 25 Apr 2018 15:49:43 -0400 Subject: [PATCH 0393/1971] Merge pull request #1804 from LLK/greenkeeper/scratch-audio-0.1.0-prerelease.1523977528 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-audio to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index eb61763e06..3200ea775b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -88,7 +88,7 @@ "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "0.1.0-prerelease.1516198804", + "scratch-audio": "0.1.0-prerelease.1523977528", "scratch-blocks": "0.1.0-prerelease.1524267558", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180423143518", From 0830c002619787052db00908e9f8ebe671756e50 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 26 Apr 2018 13:35:07 -0400 Subject: [PATCH 0394/1971] Merge pull request #1876 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180426161647 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3200ea775b..8279e2bf92 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1523977528", "scratch-blocks": "0.1.0-prerelease.1524267558", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180423143518", + "scratch-paint": "0.2.0-prerelease.20180426161647", "scratch-render": "0.1.0-prerelease.20180425181730", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", "scratch-storage": "0.4.0", From 50fcaeb0045c8a5d9fec08f066395cad773df139 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 26 Apr 2018 14:24:14 -0400 Subject: [PATCH 0395/1971] Merge pull request #1878 from fsih/renameGetCostume Rename getCostumeSvg to getCostume --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8279e2bf92..583e641e9e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -95,7 +95,7 @@ "scratch-render": "0.1.0-prerelease.20180425181730", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", "scratch-storage": "0.4.0", - "scratch-vm": "0.1.0-prerelease.1524520946", + "scratch-vm": "0.1.0-prerelease.1524765647", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From d41c25aa215122cd1c3783268ac42fa18599d554 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 27 Apr 2018 09:26:07 -0400 Subject: [PATCH 0396/1971] Merge pull request #1882 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1524774938 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.152… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 583e641e9e..3171e21213 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1523977528", - "scratch-blocks": "0.1.0-prerelease.1524267558", + "scratch-blocks": "0.1.0-prerelease.1524774938", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180426161647", "scratch-render": "0.1.0-prerelease.20180425181730", From 0165e2d9ba9b4896659fbe9934074cbd05da3464 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 27 Apr 2018 11:11:42 -0400 Subject: [PATCH 0397/1971] Framework and demo content for how-tos inside the editor (#1867) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WIP tips * WIP first card stack * WIP use tips reducer to show things to try * single card deck similar to mask sequence * collapse option * Add zoom card * WIP tips * Reorganized to redux * Add zooming to the gifs * Blue cards + navigation * Add tips to new nav bar * Remove blue * Update design with icons * deck updates wip * Updated card decks * Add videos to libraries * Update initial card and clicker preview * Add backdrop to initial card step. * WIP use vimeo player instead * New simple spin deck * Spin around and say hello decks * Update button and sub-card styles * Fix "tips" button in menu bar * Use move cursor * Fix cursor * Use same image for deck preview and library * Fix cursor again * Update clicker game deck * Add videos as single-step decks * Change tips in menu bar to ? icon * Change “tips” to “how-tos” in library * Add help icon to cards * Update deck library images * Add drag cover for youtube video to prevent catching mouseup * add a step to spin deck for clicking control * Blue bar style updates * Add step pips * Update clicker game name to run away * Fix image padding * Add change color step to spin deck * Add start sound step to run away deck * Update say deck to start with adding a sprite * Remove package lock * Remove zooming functionality * Remove unnecassary prototype changes * Split up rendering of cards into smaller parts * Use FormattedMessage for all strings * Automatic lint fixes * Almost finished with lint errors! * Fix more lint errors * Clean up CSS * Fix webpack config --- .../src/components/menu-bar/menu-bar.jsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index f647b24df5..16f7cfaf6e 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -14,6 +14,7 @@ import Menu from '../../containers/menu.jsx'; import {MenuItem, MenuSection} from '../menu/menu.jsx'; import ProjectSaver from '../../containers/project-saver.jsx'; +import {openTipsLibrary} from '../../reducers/modals'; import { openFileMenu, closeFileMenu, @@ -31,6 +32,8 @@ import communityIcon from './icon--see-community.svg'; import dropdownCaret from '../language-selector/dropdown-caret.svg'; import scratchLogo from './scratch-logo.svg'; +import helpIcon from './icon--help.svg'; + const MenuBarItemTooltip = ({ children, className, @@ -220,6 +223,15 @@ const MenuBar = props => (
+
+ +
({ }); const mapDispatchToProps = dispatch => ({ + onOpenTipLibrary: () => dispatch(openTipsLibrary()), onClickFile: () => dispatch(openFileMenu()), onRequestCloseFile: () => dispatch(closeFileMenu()), onClickEdit: () => dispatch(openEditMenu()), From 4fce68b6f5ee0aa35035d060420f174cb3040322 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 27 Apr 2018 11:17:06 -0400 Subject: [PATCH 0398/1971] Merge pull request #1879 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180426204040 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3171e21213..50063697ef 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1523977528", "scratch-blocks": "0.1.0-prerelease.1524774938", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180426161647", + "scratch-paint": "0.2.0-prerelease.20180426204040", "scratch-render": "0.1.0-prerelease.20180425181730", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", "scratch-storage": "0.4.0", From 9f727eca33f4460c3ea9ebde2fc4b9413713c418 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 27 Apr 2018 15:07:17 -0400 Subject: [PATCH 0399/1971] Merge pull request #1896 from LLK/revert-bitmap-paint Revert to pre-bitmap paint editor for deploying --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 50063697ef..d22c804d0f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1523977528", "scratch-blocks": "0.1.0-prerelease.1524774938", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180426204040", + "scratch-paint": "0.2.0-prerelease.20180410152401", "scratch-render": "0.1.0-prerelease.20180425181730", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", "scratch-storage": "0.4.0", From b917a9313f1c97873fa84a90695c94795c24a59d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 27 Apr 2018 16:23:11 -0400 Subject: [PATCH 0400/1971] Merge pull request #1889 from paulkaplan/integration-tests-for-how-tos Add a simple smoke test for the how-to library --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 16f7cfaf6e..7e098b65f0 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -224,6 +224,7 @@ const MenuBar = props => (
From dccf9ec4e798b74ef2cb0aaedf6484786e7c1184 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 27 Apr 2018 16:24:03 -0400 Subject: [PATCH 0401/1971] Merge pull request #1903 from LLK/revert-1896-revert-bitmap-paint Revert "Revert to pre-bitmap paint editor for deploying" --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d22c804d0f..50063697ef 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1523977528", "scratch-blocks": "0.1.0-prerelease.1524774938", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180410152401", + "scratch-paint": "0.2.0-prerelease.20180426204040", "scratch-render": "0.1.0-prerelease.20180425181730", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", "scratch-storage": "0.4.0", From 9c2a6a565f18858902a22c0ba5a1888b41aa0a9f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 27 Apr 2018 16:24:57 -0400 Subject: [PATCH 0402/1971] Merge pull request #1884 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20180427143155 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 50063697ef..2d4bcf9b71 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "scratch-blocks": "0.1.0-prerelease.1524774938", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180426204040", - "scratch-render": "0.1.0-prerelease.20180425181730", + "scratch-render": "0.1.0-prerelease.20180427143155", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1524765647", From 4f3479889e3ba556269918885caf6e3f0be5f7f0 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 27 Apr 2018 16:25:18 -0400 Subject: [PATCH 0403/1971] Merge pull request #1886 from LLK/greenkeeper/scratch-storage-0.4.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-storage to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2d4bcf9b71..dfe204e604 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-paint": "0.2.0-prerelease.20180426204040", "scratch-render": "0.1.0-prerelease.20180427143155", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", - "scratch-storage": "0.4.0", + "scratch-storage": "0.4.1", "scratch-vm": "0.1.0-prerelease.1524765647", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From b4c679c2042b83e968cea36e50bd356c3e52d086 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 30 Apr 2018 09:21:25 -0400 Subject: [PATCH 0404/1971] Merge pull request #1905 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1524865295 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index dfe204e604..92ce997232 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1523977528", - "scratch-blocks": "0.1.0-prerelease.1524774938", + "scratch-blocks": "0.1.0-prerelease.1524865295", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180426204040", "scratch-render": "0.1.0-prerelease.20180427143155", From 3ac4f34214a16af6f47a6ce35da5972db5d69a43 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 30 Apr 2018 10:28:22 -0400 Subject: [PATCH 0405/1971] Merge pull request #1883 from ericrosenbaum/bugfix/scroll-position-stability-2 Prevent category menu jump on sprite switch --- packages/scratch-gui/src/containers/blocks.jsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 278a649bd2..12eef6e913 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -99,7 +99,12 @@ class Blocks extends React.Component { const offset = this.workspace.toolbox_.getCategoryScrollOffset(); this.workspace.updateToolbox(this.props.toolboxXML); const currentCategoryPos = this.workspace.toolbox_.getCategoryPositionByName(categoryName); - this.workspace.toolbox_.setFlyoutScrollPos(currentCategoryPos + offset); + const currentCategoryLen = this.workspace.toolbox_.getCategoryLengthByName(categoryName); + if (offset < currentCategoryLen) { + this.workspace.toolbox_.setFlyoutScrollPos(currentCategoryPos + offset); + } else { + this.workspace.toolbox_.setFlyoutScrollPos(currentCategoryPos); + } } if (this.props.isVisible === prevProps.isVisible) { return; From bec1ef2bee14cbd4b7535bdd40cfbd9912f9aaaa Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 30 Apr 2018 10:35:20 -0400 Subject: [PATCH 0406/1971] Merge pull request #1910 from LLK/greenkeeper/chromedriver-2.38.2 chore(package): update chromedriver to version 2.38.2 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 92ce997232..aeb78bba96 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -37,7 +37,7 @@ "babel-preset-es2015": "^6.22.0", "babel-preset-react": "^6.22.0", "buffer-loader": "0.0.1", - "chromedriver": "2.38.0", + "chromedriver": "2.38.2", "classnames": "2.2.5", "copy-webpack-plugin": "^4.3.0", "css-loader": "^0.28.7", From 97c241f221db6ffaf1d69712eef950fa8bdc324f Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 30 Apr 2018 12:20:34 -0400 Subject: [PATCH 0407/1971] Merge pull request #1869 from ericrosenbaum/feature/key-droppability Post keyboard event.key --- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 3f1453b548..afd2fa4566 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -50,6 +50,7 @@ const vmListenerHOC = function (WrappedComponent) { this.props.vm.postIOData('keyboard', { keyCode: e.keyCode, + key: e.key, isDown: true }); } @@ -58,6 +59,7 @@ const vmListenerHOC = function (WrappedComponent) { // even those that have switched to other targets. this.props.vm.postIOData('keyboard', { keyCode: e.keyCode, + key: e.key, isDown: false }); From 73fc6e5e3d5de733e56f3768c7c8dc2edc58b45a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 30 Apr 2018 13:44:03 -0400 Subject: [PATCH 0408/1971] Merge pull request #1914 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1525108498 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.152… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index aeb78bba96..f7a06cae8d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1523977528", - "scratch-blocks": "0.1.0-prerelease.1524865295", + "scratch-blocks": "0.1.0-prerelease.1525108498", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180426204040", "scratch-render": "0.1.0-prerelease.20180427143155", From 2009734d872acc291bed02bca8f0a014f1dfe490 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 30 Apr 2018 15:10:06 -0400 Subject: [PATCH 0409/1971] Merge pull request #1915 from paulkaplan/fix-costume-crash Protect against showing costume tab for target that does not exist. --- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index afd2fa4566..3713263206 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -5,7 +5,7 @@ import VM from 'scratch-vm'; import {connect} from 'react-redux'; -import {updateEditingTarget, updateTargets} from '../reducers/targets'; +import {updateTargets} from '../reducers/targets'; import {updateBlockDrag} from '../reducers/block-drag'; import {updateMonitors} from '../reducers/monitors'; @@ -100,8 +100,7 @@ const vmListenerHOC = function (WrappedComponent) { }); const mapDispatchToProps = dispatch => ({ onTargetsUpdate: data => { - dispatch(updateEditingTarget(data.editingTarget)); - dispatch(updateTargets(data.targetList)); + dispatch(updateTargets(data.targetList, data.editingTarget)); }, onMonitorsUpdate: monitorList => { dispatch(updateMonitors(monitorList)); From 544b5a794ef680a502ba08d1b133c2e52cca4aff Mon Sep 17 00:00:00 2001 From: kchadha Date: Mon, 30 Apr 2018 16:58:00 -0400 Subject: [PATCH 0410/1971] Merge pull request #1880 from kchadha/video-refactor Refactor Video IO --- packages/scratch-gui/package.json | 2 +- packages/scratch-gui/src/containers/stage.jsx | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f7a06cae8d..8a4c90e805 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -95,7 +95,7 @@ "scratch-render": "0.1.0-prerelease.20180427143155", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", "scratch-storage": "0.4.1", - "scratch-vm": "0.1.0-prerelease.1524765647", + "scratch-vm": "0.1.0-prerelease.1525095619", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index f3444af1a4..96fdf71a68 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -6,6 +6,7 @@ import VM from 'scratch-vm'; import {connect} from 'react-redux'; import {getEventXY} from '../lib/touch-utils'; +import VideoProvider from '../lib/video/video-provider'; import StageComponent from '../components/stage/stage.jsx'; @@ -57,6 +58,7 @@ class Stage extends React.Component { this.renderer = new Renderer(this.canvas); this.props.vm.attachRenderer(this.renderer); this.props.vm.runtime.addListener('QUESTION', this.questionListener); + this.props.vm.setVideoProvider(new VideoProvider()); } shouldComponentUpdate (nextProps, nextState) { return this.props.width !== nextProps.width || From de748db6d42cc290d2bfebf4d64ec0349973a256 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Tue, 1 May 2018 10:30:42 -0400 Subject: [PATCH 0411/1971] Player mode (#1909) * Player mode Add isPlayerOnly property on GUI. Move isFullScreen into mode reducer with isPlayerOnly. Full screen needs to be a separate prop so that GUI knows what to show when exiting full-screen (either player-only or full editor). - set `gui-body * {box-sizing: border-box;}` instead of expecting it to be set by the enclosing component/playground, and removed uses of content-box (adjusting sizes as needed) - Added `!isPlayerOnly` to the conditions for draggable sprites - rewrote player example to use player mode. --- packages/scratch-gui/src/containers/stage.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 96fdf71a68..0820b1741a 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -394,9 +394,9 @@ Stage.defaultProps = { const mapStateToProps = state => ({ isColorPicking: state.colorPicker.active, - isFullScreen: state.stageSize.isFullScreen, - // Do not use editor drag style in fullscreen mode. - useEditorDragStyle: !state.stageSize.isFullScreen + isFullScreen: state.mode.isFullScreen, + // Do not use editor drag style in fullscreen or player mode. + useEditorDragStyle: !(state.mode.isFullScreen || state.mode.isPlayerOnly) }); const mapDispatchToProps = dispatch => ({ From f962b8b23e15dae567c32978dc13575726d5b6e1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 1 May 2018 12:51:12 -0400 Subject: [PATCH 0412/1971] Merge pull request #1922 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1525125321 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8a4c90e805..a81864032c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1523977528", - "scratch-blocks": "0.1.0-prerelease.1525108498", + "scratch-blocks": "0.1.0-prerelease.1525125321", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180426204040", "scratch-render": "0.1.0-prerelease.20180427143155", From 3d50b8448ca78928915c4609dd9105a15c6c06a1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 1 May 2018 17:03:13 -0400 Subject: [PATCH 0413/1971] Merge pull request #1932 from paulkaplan/fix-dragging-nothing Fix errors when dragging in player mode --- packages/scratch-gui/src/containers/stage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 0820b1741a..57614135f9 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -297,6 +297,7 @@ class Stage extends React.Component { if (drawableId === null) return; const drawableData = this.renderer.extractDrawable(drawableId, x, y); const targetId = this.props.vm.getTargetIdForDrawableId(drawableId); + if (targetId === null) return; // Only start drags on non-draggable targets in editor drag mode if (!this.props.useEditorDragStyle) { @@ -304,7 +305,6 @@ class Stage extends React.Component { if (!target.draggable) return; } - if (targetId === null) return; this.props.vm.startDrag(targetId); this.setState({ isDragging: true, From 5437f2b0dd45b5b5742058bfb9c32dd389788a6c Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 2 May 2018 09:54:38 -0400 Subject: [PATCH 0414/1971] Merge pull request #1866 from fsih/updateBitmap Bitmap import and export --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a81864032c..9bad1e75d9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,11 +91,11 @@ "scratch-audio": "0.1.0-prerelease.1523977528", "scratch-blocks": "0.1.0-prerelease.1525125321", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180426204040", + "scratch-paint": "0.2.0-prerelease.20180501202212", "scratch-render": "0.1.0-prerelease.20180427143155", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", "scratch-storage": "0.4.1", - "scratch-vm": "0.1.0-prerelease.1525095619", + "scratch-vm": "0.1.0-prerelease.1525209256", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From badbfb9017399d849fa3e484a6a8f92cf15095ff Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 2 May 2018 10:38:43 -0400 Subject: [PATCH 0415/1971] Merge pull request #1942 from paulkaplan/enforce-no-strings Make remaining text translatable, and prevent future regressions --- .../src/components/menu-bar/menu-bar.jsx | 79 ++++++++++++++++--- 1 file changed, 68 insertions(+), 11 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 7e098b65f0..d5a4b9fa1a 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -123,20 +123,43 @@ const MenuBar = props => ( })} onMouseUp={props.onClickFile} > -
File
+
+ +
- New + + + - Save now + + + - Save as a copy + + @@ -145,7 +168,11 @@ const MenuBar = props => ( onClick={loadProject} {...loadProps} > - Upload from your computer + {renderFileInput()} )} @@ -154,7 +181,11 @@ const MenuBar = props => ( onClick={saveProject} {...saveProps} > - Download to your computer + )} @@ -166,20 +197,44 @@ const MenuBar = props => ( })} onMouseUp={props.onClickEdit} > -
Edit
+
+ +
- Undo + + + - Redo + + + - Turbo mode + + + @@ -262,7 +317,9 @@ const MenuBar = props => ( className={styles.profileIcon} src={profileIcon} /> - scratch-cat + + {'scratch-cat' /* @todo username */} + Date: Wed, 2 May 2018 11:09:25 -0400 Subject: [PATCH 0416/1971] Merge pull request #1941 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20180502115145 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9bad1e75d9..7a98289832 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "scratch-blocks": "0.1.0-prerelease.1525125321", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180501202212", - "scratch-render": "0.1.0-prerelease.20180427143155", + "scratch-render": "0.1.0-prerelease.20180502115145", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", "scratch-storage": "0.4.1", "scratch-vm": "0.1.0-prerelease.1525209256", From 7e5bcf7069c1cbb838ee3229966025f248010b1c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 2 May 2018 16:05:55 -0400 Subject: [PATCH 0417/1971] Merge pull request #1951 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180502192058 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7a98289832..db23986910 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1523977528", "scratch-blocks": "0.1.0-prerelease.1525125321", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180501202212", + "scratch-paint": "0.2.0-prerelease.20180502192058", "scratch-render": "0.1.0-prerelease.20180502115145", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", "scratch-storage": "0.4.1", From a55a07ffa474e30fa3c43d8a33e8fb29b791847e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 3 May 2018 11:34:14 -0400 Subject: [PATCH 0418/1971] Merge pull request #1953 from paulkaplan/unsupported-errors Make error catching and reporting better --- packages/scratch-gui/src/containers/blocks.jsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 12eef6e913..9e924629ea 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -12,6 +12,7 @@ import Prompt from './prompt.jsx'; import BlocksComponent from '../components/blocks/blocks.jsx'; import ExtensionLibrary from './extension-library.jsx'; import CustomProcedures from './custom-procedures.jsx'; +import errorBoundaryHOC from '../lib/error-boundary-hoc.jsx'; import {connect} from 'react-redux'; import {updateToolbox} from '../reducers/toolbox'; @@ -415,7 +416,9 @@ const mapDispatchToProps = dispatch => ({ } }); -export default connect( - mapStateToProps, - mapDispatchToProps -)(Blocks); +export default errorBoundaryHOC('Blocks')( + connect( + mapStateToProps, + mapDispatchToProps + )(Blocks) +); From e27c6e8cd034e5d8b93d9452528ca47ecf4955d1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 3 May 2018 16:45:06 -0400 Subject: [PATCH 0419/1971] Merge pull request #1960 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180503182655 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index db23986910..5294a27952 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "scratch-audio": "0.1.0-prerelease.1523977528", "scratch-blocks": "0.1.0-prerelease.1525125321", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180502192058", + "scratch-paint": "0.2.0-prerelease.20180503182655", "scratch-render": "0.1.0-prerelease.20180502115145", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", "scratch-storage": "0.4.1", From 4d3d7a1d681c474431126e1786d53f9a42390e62 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Fri, 4 May 2018 10:23:33 -0400 Subject: [PATCH 0420/1971] Merge pull request #1969 from thisandagain/feature/feedback Restore feedback button with link to the forums --- .../src/components/menu-bar/menu-bar.jsx | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index d5a4b9fa1a..8a5fb5bb7d 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -27,6 +27,7 @@ import { import styles from './menu-bar.css'; import mystuffIcon from './icon--mystuff.png'; +import feedbackIcon from './icon--feedback.svg'; import profileIcon from './icon--profile.png'; import communityIcon from './icon--see-community.svg'; import dropdownCaret from '../language-selector/dropdown-caret.svg'; @@ -277,6 +278,25 @@ const MenuBar = props => (
+
Date: Fri, 4 May 2018 14:12:27 -0400 Subject: [PATCH 0421/1971] Merge pull request #1978 from paulkaplan/update-deps-for-deploy Update deps for deploy --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5294a27952..ecee630fd0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,11 +91,11 @@ "scratch-audio": "0.1.0-prerelease.1523977528", "scratch-blocks": "0.1.0-prerelease.1525125321", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180503182655", + "scratch-paint": "0.2.0-prerelease.20180504180528", "scratch-render": "0.1.0-prerelease.20180502115145", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", "scratch-storage": "0.4.1", - "scratch-vm": "0.1.0-prerelease.1525209256", + "scratch-vm": "0.1.0-prerelease.1525457332", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From 820d1603f3b7e9119fcb2a62149bd7c4180ee4bb Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 4 May 2018 14:49:40 -0400 Subject: [PATCH 0422/1971] Merge pull request #1980 from paulkaplan/sudo-update-deps-for-release Update vm with real offsetting fix --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ecee630fd0..3d6662bbaf 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -95,7 +95,7 @@ "scratch-render": "0.1.0-prerelease.20180502115145", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", "scratch-storage": "0.4.1", - "scratch-vm": "0.1.0-prerelease.1525457332", + "scratch-vm": "0.1.0-prerelease.1525459625", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From a374292cb11c848088206b6af01fef2ef1779261 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 4 May 2018 16:52:18 -0400 Subject: [PATCH 0423/1971] Merge pull request #1973 from gnarf/limit-toolbox-updates Limit toolbox updates --- .../scratch-gui/src/containers/blocks.jsx | 83 ++++++++++++++----- 1 file changed, 63 insertions(+), 20 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 9e924629ea..d5bec2f252 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -59,6 +59,7 @@ class Blocks extends React.Component { prompt: null }; this.onTargetsUpdate = debounce(this.onTargetsUpdate, 100); + this.toolboxUpdateQueue = []; } componentDidMount () { this.ScratchBlocks.FieldColourSlider.activateEyedropper_ = this.props.onActivateColorPicker; @@ -71,6 +72,14 @@ class Blocks extends React.Component { ); this.workspace = this.ScratchBlocks.inject(this.blocks, workspaceConfig); + // we actually never want the workspace to enable "refresh toolbox" - this basically re-renders the + // entire toolbox every time we reset the workspace. We call updateToolbox as a part of + // componentDidUpdate so the toolbox will still correctly be updated + this.setToolboxRefreshEnabled = this.workspace.setToolboxRefreshEnabled.bind(this.workspace); + this.workspace.setToolboxRefreshEnabled = () => { + this.setToolboxRefreshEnabled(false); + }; + // @todo change this when blockly supports UI events addFunctionListener(this.workspace, 'translate', this.onWorkspaceMetricsChange); addFunctionListener(this.workspace, 'zoom', this.onWorkspaceMetricsChange); @@ -96,16 +105,11 @@ class Blocks extends React.Component { } if (prevProps.toolboxXML !== this.props.toolboxXML) { - const categoryName = this.workspace.toolbox_.getSelectedCategoryName(); - const offset = this.workspace.toolbox_.getCategoryScrollOffset(); - this.workspace.updateToolbox(this.props.toolboxXML); - const currentCategoryPos = this.workspace.toolbox_.getCategoryPositionByName(categoryName); - const currentCategoryLen = this.workspace.toolbox_.getCategoryLengthByName(categoryName); - if (offset < currentCategoryLen) { - this.workspace.toolbox_.setFlyoutScrollPos(currentCategoryPos + offset); - } else { - this.workspace.toolbox_.setFlyoutScrollPos(currentCategoryPos); - } + // rather than update the toolbox "sync" -- update it in the next frame + clearTimeout(this.toolboxUpdateTimeout); + this.toolboxUpdateTimeout = setTimeout(() => { + this.updateToolbox(); + }, 0); } if (this.props.isVisible === prevProps.isVisible) { return; @@ -123,7 +127,42 @@ class Blocks extends React.Component { componentWillUnmount () { this.detachVM(); this.workspace.dispose(); + clearTimeout(this.toolboxUpdateTimeout); + } + + updateToolbox () { + this.toolboxUpdateTimeout = false; + + const categoryName = this.workspace.toolbox_.getSelectedCategoryName(); + const offset = this.workspace.toolbox_.getCategoryScrollOffset(); + this.workspace.updateToolbox(this.props.toolboxXML); + // In order to catch any changes that mutate the toolbox during "normal runtime" + // (variable changes/etc), re-enable toolbox refresh. + // Using the setter function will rerender the entire toolbox which we just rendered. + this.workspace.toolboxRefreshEnabled_ = true; + + const currentCategoryPos = this.workspace.toolbox_.getCategoryPositionByName(categoryName); + const currentCategoryLen = this.workspace.toolbox_.getCategoryLengthByName(categoryName); + if (offset < currentCategoryLen) { + this.workspace.toolbox_.setFlyoutScrollPos(currentCategoryPos + offset); + } else { + this.workspace.toolbox_.setFlyoutScrollPos(currentCategoryPos); + } + + const queue = this.toolboxUpdateQueue; + this.toolboxUpdateQueue = []; + queue.forEach(fn => fn()); } + + withToolboxUpdates (fn) { + // if there is a queued toolbox update, we need to wait + if (this.toolboxUpdateTimeout) { + this.toolboxUpdateQueue.push(fn); + } else { + fn(); + } + } + attachVM () { this.workspace.addChangeListener(this.props.vm.blockListener); this.flyoutWorkspace = this.workspace @@ -152,15 +191,19 @@ class Blocks extends React.Component { this.props.vm.removeListener('EXTENSION_ADDED', this.handleExtensionAdded); this.props.vm.removeListener('BLOCKSINFO_UPDATE', this.handleBlocksInfoUpdate); } + updateToolboxBlockValue (id, value) { - const block = this.workspace - .getFlyout() - .getWorkspace() - .getBlockById(id); - if (block) { - block.inputList[0].fieldRow[0].setValue(value); - } + this.withToolboxUpdates(() => { + const block = this.workspace + .getFlyout() + .getWorkspace() + .getBlockById(id); + if (block) { + block.inputList[0].fieldRow[0].setValue(value); + } + }); } + onTargetsUpdate () { if (this.props.vm.editingTarget) { ['glide', 'move', 'set'].forEach(prefix => { @@ -213,8 +256,6 @@ class Blocks extends React.Component { // Remove and reattach the workspace listener (but allow flyout events) this.workspace.removeChangeListener(this.props.vm.blockListener); const dom = this.ScratchBlocks.Xml.textToDom(data.xml); - // @todo This line rerenders toolbox, and the change in the toolbox XML also rerenders the toolbox. - // We should only rerender the toolbox once. See https://github.com/LLK/scratch-gui/issues/901 this.ScratchBlocks.Xml.clearWorkspaceAndLoadFromXml(dom, this.workspace); this.workspace.addChangeListener(this.props.vm.blockListener); @@ -245,7 +286,9 @@ class Blocks extends React.Component { this.handleExtensionAdded(blocksInfo); } handleCategorySelected (categoryName) { - this.workspace.toolbox_.setSelectedCategoryByName(categoryName); + this.withToolboxUpdates(() => { + this.workspace.toolbox_.setSelectedCategoryByName(categoryName); + }); } setBlocks (blocks) { this.blocks = blocks; From 00655f6ff5b92762b8d31e941393818e6c0d49c0 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 4 May 2018 21:14:34 -0400 Subject: [PATCH 0424/1971] Merge pull request #1985 from LLK/update-scratch-blocks Bump scratch-blocks version --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3d6662bbaf..439bf30799 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1523977528", - "scratch-blocks": "0.1.0-prerelease.1525125321", + "scratch-blocks": "0.1.0-prerelease.1525467275", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180504180528", "scratch-render": "0.1.0-prerelease.20180502115145", From 311ee5dbfc705bceb5dc183479763782d4878aa7 Mon Sep 17 00:00:00 2001 From: kchadha Date: Mon, 7 May 2018 10:40:51 -0400 Subject: [PATCH 0425/1971] Merge pull request #1967 from kchadha/sprite-upload Sprite upload --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 439bf30799..94658acec5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -95,7 +95,7 @@ "scratch-render": "0.1.0-prerelease.20180502115145", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", "scratch-storage": "0.4.1", - "scratch-vm": "0.1.0-prerelease.1525459625", + "scratch-vm": "0.1.0-prerelease.1525460669", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.20.0", From cbbc5561e659ada6f571b3d6b0ab3ce20925aa3d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 8 May 2018 16:33:49 -0400 Subject: [PATCH 0426/1971] Merge pull request #2000 from mzgoddard/webpack-4 Update to webpack 4 --- packages/scratch-gui/package.json | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 94658acec5..e6db8573c1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -39,8 +39,8 @@ "buffer-loader": "0.0.1", "chromedriver": "2.38.2", "classnames": "2.2.5", - "copy-webpack-plugin": "^4.3.0", - "css-loader": "^0.28.7", + "copy-webpack-plugin": "^4.5.1", + "css-loader": "^0.28.11", "enzyme": "^3.1.0", "enzyme-adapter-react-16": "1.1.1", "es6-object-assign": "1.1.0", @@ -52,7 +52,7 @@ "get-float-time-domain-data": "0.1.0", "get-user-media-promise": "1.1.1", "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", - "html-webpack-plugin": "^3.0.5", + "html-webpack-plugin": "^3.2.0", "immutable": "3.8.2", "jest": "^21.0.0", "lodash.bindall": "4.4.0", @@ -65,11 +65,11 @@ "mkdirp": "^0.5.1", "platform": "1.3.5", "postcss-import": "^11.0.0", - "postcss-loader": "^2.0.5", + "postcss-loader": "^2.1.4", "postcss-simple-vars": "^4.0.0", "prop-types": "^15.5.10", "raf": "^3.4.0", - "raw-loader": "0.5.1", + "raw-loader": "^0.5.1", "react": "16.2.0", "react-contextmenu": "2.9.2", "react-dom": "16.2.0", @@ -93,18 +93,20 @@ "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180504180528", "scratch-render": "0.1.0-prerelease.20180502115145", - "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", "scratch-storage": "0.4.1", + "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", "scratch-vm": "0.1.0-prerelease.1525460669", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", - "style-loader": "^0.20.0", + "style-loader": "^0.21.0", "svg-to-image": "1.1.3", "text-encoding": "0.6.4", + "uglifyjs-webpack-plugin": "^1.2.5", "wav-encoder": "1.3.0", "web-audio-test-api": "^0.5.2", - "webpack": "^3.6.0", - "webpack-dev-server": "^2.9.7", + "webpack": "^4.6.0", + "webpack-cli": "^2.0.15", + "webpack-dev-server": "^3.1.3", "xhr": "2.4.1" }, "jest": { From abc739fbcefb1df7c0e29a35d76d3c01df7f202d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 8 May 2018 16:52:50 -0400 Subject: [PATCH 0427/1971] Merge pull request #1999 from mzgoddard/babel-preset-env Replace babel-preset-es2015 with babel-preset-env --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e6db8573c1..1e21d631f3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -34,7 +34,7 @@ "babel-plugin-syntax-dynamic-import": "^6.18.0", "babel-plugin-transform-async-to-generator": "^6.24.1", "babel-plugin-transform-object-rest-spread": "^6.22.0", - "babel-preset-es2015": "^6.22.0", + "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.22.0", "buffer-loader": "0.0.1", "chromedriver": "2.38.2", From 23d7928f55a23cdcf3466d9770158c5167275d32 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 8 May 2018 16:54:48 -0400 Subject: [PATCH 0428/1971] Merge pull request #2001 from mzgoddard/arraybuffer-loader Replace buffer-loader with arraybuffer-loader --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1e21d631f3..801de7534d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -27,6 +27,7 @@ "react-dom": "^16.0.0" }, "devDependencies": { + "arraybuffer-loader": "^1.0.3", "autoprefixer": "^8.1.0", "babel-core": "^6.23.1", "babel-eslint": "^8.0.1", @@ -36,7 +37,6 @@ "babel-plugin-transform-object-rest-spread": "^6.22.0", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.22.0", - "buffer-loader": "0.0.1", "chromedriver": "2.38.2", "classnames": "2.2.5", "copy-webpack-plugin": "^4.5.1", From 649881f0d908c6c2087fd427679f32545afb24e2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 9 May 2018 09:40:15 -0400 Subject: [PATCH 0429/1971] Merge pull request #2008 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20180508205430 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 801de7534d..ce7805c22e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "scratch-blocks": "0.1.0-prerelease.1525467275", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180504180528", - "scratch-render": "0.1.0-prerelease.20180502115145", + "scratch-render": "0.1.0-prerelease.20180508205430", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", "scratch-vm": "0.1.0-prerelease.1525460669", From fb8d627dbe1f62372c387ec070e4160b97ed304f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 9 May 2018 15:05:57 -0400 Subject: [PATCH 0430/1971] Merge pull request #2014 from paulkaplan/readonly-monitors Add "read-only" slider and list monitors. --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ce7805c22e..58a2f40eac 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -95,7 +95,7 @@ "scratch-render": "0.1.0-prerelease.20180508205430", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", - "scratch-vm": "0.1.0-prerelease.1525460669", + "scratch-vm": "0.1.0-prerelease.1525837283", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From fb4aac284a7aed89a5a023e26388c310eeeadffe Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 9 May 2018 18:04:10 -0400 Subject: [PATCH 0431/1971] Merge pull request #2016 from paulkaplan/fix-monitors Update VM to support importing monitors --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 58a2f40eac..cad8ee262a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -95,7 +95,7 @@ "scratch-render": "0.1.0-prerelease.20180508205430", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", - "scratch-vm": "0.1.0-prerelease.1525837283", + "scratch-vm": "0.1.0-prerelease.1525901935", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From 6023439edad88844317f0ddf60ad2dff4684dae2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 10 May 2018 18:34:27 -0400 Subject: [PATCH 0432/1971] Merge pull request #2031 from paulkaplan/update-deps-block-visibility Update vm and blocks for block visibility --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cad8ee262a..7d6d129dec 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,13 +89,13 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1523977528", - "scratch-blocks": "0.1.0-prerelease.1525467275", + "scratch-blocks": "0.1.0-prerelease.1525981783", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180504180528", "scratch-render": "0.1.0-prerelease.20180508205430", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", - "scratch-vm": "0.1.0-prerelease.1525901935", + "scratch-vm": "0.1.0-prerelease.1525981075", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From 6813330b584e0cf9c01328130ae3180a14ffdb7b Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 11 May 2018 00:23:37 -0400 Subject: [PATCH 0433/1971] Merge pull request #2023 from fsih/attachSvgRenderer Send svg renderer to vm --- packages/scratch-gui/package.json | 6 +++--- packages/scratch-gui/src/containers/stage.jsx | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7d6d129dec..8a2e018920 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,10 +91,10 @@ "scratch-audio": "0.1.0-prerelease.1523977528", "scratch-blocks": "0.1.0-prerelease.1525981783", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180504180528", - "scratch-render": "0.1.0-prerelease.20180508205430", + "scratch-paint": "0.2.0-prerelease.20180510175214", + "scratch-render": "0.1.0-prerelease.20180510210730", "scratch-storage": "0.4.1", - "scratch-svg-renderer": "0.1.0-prerelease.20180423193917", + "scratch-svg-renderer": "0.1.0-prerelease.20180510181711", "scratch-vm": "0.1.0-prerelease.1525981075", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 57614135f9..2bdaa8208d 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -7,6 +7,7 @@ import {connect} from 'react-redux'; import {getEventXY} from '../lib/touch-utils'; import VideoProvider from '../lib/video/video-provider'; +import {SVGRenderer as V2SVGAdapter} from 'scratch-svg-renderer'; import StageComponent from '../components/stage/stage.jsx'; @@ -57,6 +58,7 @@ class Stage extends React.Component { this.updateRect(); this.renderer = new Renderer(this.canvas); this.props.vm.attachRenderer(this.renderer); + this.props.vm.attachV2SVGAdapter(new V2SVGAdapter()); this.props.vm.runtime.addListener('QUESTION', this.questionListener); this.props.vm.setVideoProvider(new VideoProvider()); } From 7fd838abeb8ddd615d4cfa75f0e5e747f8a3dd64 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 11 May 2018 11:27:45 -0400 Subject: [PATCH 0434/1971] Merge pull request #2035 from LLK/update-vm Update scratch-vm dependency --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8a2e018920..ffe8167cc9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -95,7 +95,7 @@ "scratch-render": "0.1.0-prerelease.20180510210730", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180510181711", - "scratch-vm": "0.1.0-prerelease.1525981075", + "scratch-vm": "0.1.0-prerelease.1526052228", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From 678dea6e457484358f80f02ad7400a7906e9233b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 11 May 2018 11:29:41 -0400 Subject: [PATCH 0435/1971] Merge pull request #2019 from paulkaplan/interactive-monitor Make slider and list monitors update variables in the VM --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ffe8167cc9..dfc4177d1a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -84,6 +84,7 @@ "react-tabs": "2.2.1", "react-test-renderer": "16.2.0", "react-tooltip": "3.4.0", + "react-virtualized": "9.18.5", "redux": "3.7.2", "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", From c0196f9fd16f43ecee50788f40112b8fd7a72e73 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 11 May 2018 11:48:53 -0400 Subject: [PATCH 0436/1971] Merge pull request #2036 from fsih/addDeps Add dependencies that are updated by change to svg-renderer --- packages/scratch-gui/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index dfc4177d1a..faf8b58173 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,10 +92,10 @@ "scratch-audio": "0.1.0-prerelease.1523977528", "scratch-blocks": "0.1.0-prerelease.1525981783", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180510175214", - "scratch-render": "0.1.0-prerelease.20180510210730", + "scratch-paint": "0.2.0-prerelease.20180511152319", + "scratch-render": "0.1.0-prerelease.20180511151033", "scratch-storage": "0.4.1", - "scratch-svg-renderer": "0.1.0-prerelease.20180510181711", + "scratch-svg-renderer": "0.1.0-prerelease.20180511144653", "scratch-vm": "0.1.0-prerelease.1526052228", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 2ee5f5211d65e6f80b1b78835356c5370bffc4c9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 11 May 2018 14:17:55 -0400 Subject: [PATCH 0437/1971] Merge pull request #2040 from paulkaplan/fixup-smoketest Fixes from smoke test --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index faf8b58173..a18257d84c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -93,7 +93,7 @@ "scratch-blocks": "0.1.0-prerelease.1525981783", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180511152319", - "scratch-render": "0.1.0-prerelease.20180511151033", + "scratch-render": "0.1.0-prerelease.20180511181411", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180511144653", "scratch-vm": "0.1.0-prerelease.1526052228", From bc071acd9e4c487f8f4c110ec2fc2b6ac6d23635 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 11 May 2018 14:51:24 -0400 Subject: [PATCH 0438/1971] Merge pull request #2044 from paulkaplan/fixup-bubble-font Update scratch render again to fix speech bubbles again --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a18257d84c..756df383e5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -93,7 +93,7 @@ "scratch-blocks": "0.1.0-prerelease.1525981783", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180511152319", - "scratch-render": "0.1.0-prerelease.20180511181411", + "scratch-render": "0.1.0-prerelease.20180511184906", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180511144653", "scratch-vm": "0.1.0-prerelease.1526052228", From d2e32371db29a4c6be58e01ff688486483cb6540 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Sun, 13 May 2018 14:58:07 -0400 Subject: [PATCH 0439/1971] Merge pull request #2046 from LukeSchlangen/fix/remove-stage-question-listener remove question event listener on stage unmount --- packages/scratch-gui/src/containers/stage.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 2bdaa8208d..ee3154a220 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -83,6 +83,7 @@ class Stage extends React.Component { this.detachMouseEvents(this.canvas); this.detachRectEvents(); this.stopColorPickingLoop(); + this.props.vm.runtime.removeListener('QUESTION', this.questionListener); } questionListener (question) { this.setState({question: question}); From 9591359f9b236eacc81ef038177069a42b5e9f79 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 14 May 2018 13:42:26 -0400 Subject: [PATCH 0440/1971] Merge pull request #2058 from paulkaplan/fix-ie11 Update scratch-render to get the babel-built version of fonts --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 756df383e5..9da639d9da 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -93,7 +93,7 @@ "scratch-blocks": "0.1.0-prerelease.1525981783", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180511152319", - "scratch-render": "0.1.0-prerelease.20180511184906", + "scratch-render": "0.1.0-prerelease.20180514172756", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180511144653", "scratch-vm": "0.1.0-prerelease.1526052228", From a1e01ab82d51dc05c8c2c4f65934fb87cf124836 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 14 May 2018 15:10:07 -0400 Subject: [PATCH 0441/1971] Merge pull request #2062 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180514184134 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9da639d9da..1d14bd4fc8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "scratch-audio": "0.1.0-prerelease.1523977528", "scratch-blocks": "0.1.0-prerelease.1525981783", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180511152319", + "scratch-paint": "0.2.0-prerelease.20180514184134", "scratch-render": "0.1.0-prerelease.20180514172756", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180511144653", From 9e61b73e85404ca5cbe15a98856b2f5643812663 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 14 May 2018 15:37:18 -0400 Subject: [PATCH 0442/1971] Merge pull request #2064 from paulkaplan/update-svg-renderer-dep Update scratch-svg-renderer to fix IE11 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1d14bd4fc8..63a92ab87c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -95,7 +95,7 @@ "scratch-paint": "0.2.0-prerelease.20180514184134", "scratch-render": "0.1.0-prerelease.20180514172756", "scratch-storage": "0.4.1", - "scratch-svg-renderer": "0.1.0-prerelease.20180511144653", + "scratch-svg-renderer": "0.1.0-prerelease.20180514170126", "scratch-vm": "0.1.0-prerelease.1526052228", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 4ad6b7047366941ad97a036819d3046cc499275e Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 14 May 2018 17:17:34 -0400 Subject: [PATCH 0443/1971] Merge pull request #2041 from ericrosenbaum/feature/scroll-by-id Interact with blocks categories by id instead of by name --- packages/scratch-gui/package.json | 4 ++-- packages/scratch-gui/src/containers/blocks.jsx | 13 ++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 63a92ab87c..2a5e742a78 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -90,13 +90,13 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1523977528", - "scratch-blocks": "0.1.0-prerelease.1525981783", + "scratch-blocks": "0.1.0-prerelease.1526329848", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180514184134", "scratch-render": "0.1.0-prerelease.20180514172756", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180514170126", - "scratch-vm": "0.1.0-prerelease.1526052228", + "scratch-vm": "0.1.0-prerelease.1526320682", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index d5bec2f252..64671ebf3f 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -133,7 +133,7 @@ class Blocks extends React.Component { updateToolbox () { this.toolboxUpdateTimeout = false; - const categoryName = this.workspace.toolbox_.getSelectedCategoryName(); + const categoryId = this.workspace.toolbox_.getSelectedCategoryId(); const offset = this.workspace.toolbox_.getCategoryScrollOffset(); this.workspace.updateToolbox(this.props.toolboxXML); // In order to catch any changes that mutate the toolbox during "normal runtime" @@ -141,8 +141,8 @@ class Blocks extends React.Component { // Using the setter function will rerender the entire toolbox which we just rendered. this.workspace.toolboxRefreshEnabled_ = true; - const currentCategoryPos = this.workspace.toolbox_.getCategoryPositionByName(categoryName); - const currentCategoryLen = this.workspace.toolbox_.getCategoryLengthByName(categoryName); + const currentCategoryPos = this.workspace.toolbox_.getCategoryPositionById(categoryId); + const currentCategoryLen = this.workspace.toolbox_.getCategoryLengthById(categoryId); if (offset < currentCategoryLen) { this.workspace.toolbox_.setFlyoutScrollPos(currentCategoryPos + offset); } else { @@ -285,9 +285,9 @@ class Blocks extends React.Component { // @todo Later we should replace this to avoid all the warnings from redefining blocks. this.handleExtensionAdded(blocksInfo); } - handleCategorySelected (categoryName) { + handleCategorySelected (categoryId) { this.withToolboxUpdates(() => { - this.workspace.toolbox_.setSelectedCategoryByName(categoryName); + this.workspace.toolbox_.setSelectedCategoryById(categoryId); }); } setBlocks (blocks) { @@ -312,8 +312,7 @@ class Blocks extends React.Component { this.props.onRequestCloseCustomProcedures(data); const ws = this.workspace; ws.refreshToolboxSelection_(); - // @todo ensure this does not break on localization - ws.toolbox_.scrollToCategoryByName('My Blocks'); + ws.toolbox_.scrollToCategoryById('myBlocks'); } render () { /* eslint-disable no-unused-vars */ From 54112ec463ef3948bac01d902930b122fff357a1 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 17 May 2018 09:13:39 -0400 Subject: [PATCH 0444/1971] Merge pull request #2082 from paulkaplan/fix-monitor-scaling Fix monitor scaling, positioning and draggability in fullscreen mode --- packages/scratch-gui/src/containers/stage.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index ee3154a220..b66efa2c50 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -363,7 +363,6 @@ class Stage extends React.Component { const { vm, // eslint-disable-line no-unused-vars onActivateColorPicker, // eslint-disable-line no-unused-vars - useEditorDragStyle, // eslint-disable-line no-unused-vars ...props } = this.props; return ( From f507e684736cbef9ade3c9be68171ee535731058 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 17 May 2018 11:01:45 -0400 Subject: [PATCH 0445/1971] Merge pull request #2084 from LLK/ericrosenbaum-patch-1 Update scratch-blocks (flyout bottom padding) --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2a5e742a78..5207bfb525 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -90,7 +90,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1523977528", - "scratch-blocks": "0.1.0-prerelease.1526329848", + "scratch-blocks": "0.1.0-prerelease.1526507753", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180514184134", "scratch-render": "0.1.0-prerelease.20180514172756", From a7d7f016e3aa1c97569357e2b559e35019d7020e Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 17 May 2018 16:53:40 -0400 Subject: [PATCH 0446/1971] Merge pull request #2093 from fsih/updateFontTool Update scratch paint --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5207bfb525..ce6ce18544 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "scratch-audio": "0.1.0-prerelease.1523977528", "scratch-blocks": "0.1.0-prerelease.1526507753", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180514184134", + "scratch-paint": "0.2.0-prerelease.20180517194052", "scratch-render": "0.1.0-prerelease.20180514172756", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180514170126", From 025312d06b2644eedea8febd4a9ab16e88f98009 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 21 May 2018 10:25:41 -0400 Subject: [PATCH 0447/1971] Merge pull request #2104 from LLK/smoke Smoke test branch -> Develop --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ce6ce18544..86d8f10a8a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "scratch-audio": "0.1.0-prerelease.1523977528", "scratch-blocks": "0.1.0-prerelease.1526507753", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180517194052", + "scratch-paint": "0.2.0-prerelease.20180521140257", "scratch-render": "0.1.0-prerelease.20180514172756", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180514170126", From 0095461a95f909700dfad29300313ca1c91ff50e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 21 May 2018 11:23:27 -0400 Subject: [PATCH 0448/1971] Merge pull request #2107 from paulkaplan/fix-field-showing Hide field editors when a modal is shown --- packages/scratch-gui/src/containers/blocks.jsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 64671ebf3f..00c395726a 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -96,10 +96,16 @@ class Blocks extends React.Component { this.props.toolboxXML !== nextProps.toolboxXML || this.props.extensionLibraryVisible !== nextProps.extensionLibraryVisible || this.props.customProceduresVisible !== nextProps.customProceduresVisible || - this.props.locale !== nextProps.locale + this.props.locale !== nextProps.locale || + this.props.anyModalVisible !== nextProps.anyModalVisible ); } componentDidUpdate (prevProps) { + // If any modals are open, call hideChaff to close z-indexed field editors + if (this.props.anyModalVisible && !prevProps.anyModalVisible) { + this.ScratchBlocks.hideChaff(); + } + if (prevProps.locale !== this.props.locale) { this.props.vm.setLocale(this.props.locale, this.props.messages); } @@ -317,6 +323,7 @@ class Blocks extends React.Component { render () { /* eslint-disable no-unused-vars */ const { + anyModalVisible, customProceduresVisible, extensionLibraryVisible, options, @@ -368,6 +375,7 @@ class Blocks extends React.Component { } Blocks.propTypes = { + anyModalVisible: PropTypes.bool, customProceduresVisible: PropTypes.bool, extensionLibraryVisible: PropTypes.bool, isVisible: PropTypes.bool, @@ -437,6 +445,7 @@ Blocks.defaultProps = { }; const mapStateToProps = state => ({ + anyModalVisible: Object.keys(state.modals).some(key => state.modals[key]), extensionLibraryVisible: state.modals.extensionLibrary, locale: state.intl.locale, messages: state.intl.messages, From ae8db182035f02af2de55df1b446ce3c1ef38d9a Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 21 May 2018 14:05:27 -0400 Subject: [PATCH 0449/1971] Merge pull request #2111 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180521172423 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 86d8f10a8a..8d9d27b0a1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "scratch-audio": "0.1.0-prerelease.1523977528", "scratch-blocks": "0.1.0-prerelease.1526507753", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180521140257", + "scratch-paint": "0.2.0-prerelease.20180521172423", "scratch-render": "0.1.0-prerelease.20180514172756", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180514170126", From 29c721dbb247013796cffc03aadccfc2ba7118f5 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 21 May 2018 16:48:28 -0400 Subject: [PATCH 0450/1971] Merge pull request #2117 from fsih/updateDeps Pull in optional quirks mode changes --- packages/scratch-gui/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8d9d27b0a1..bb38d36fec 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -93,10 +93,10 @@ "scratch-blocks": "0.1.0-prerelease.1526507753", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180521172423", - "scratch-render": "0.1.0-prerelease.20180514172756", + "scratch-render": "0.1.0-prerelease.20180521201254", "scratch-storage": "0.4.1", - "scratch-svg-renderer": "0.1.0-prerelease.20180514170126", - "scratch-vm": "0.1.0-prerelease.1526320682", + "scratch-svg-renderer": "0.1.0-prerelease.20180521194642", + "scratch-vm": "0.1.0-prerelease.1526929817", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From 7e1e31f121736cd6b516e50ca8100684c4b640eb Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 22 May 2018 08:58:23 -0400 Subject: [PATCH 0451/1971] Merge pull request #2115 from paulkaplan/fix-fullscreen-inputs Hide block inputs when entering fullscreen mode --- packages/scratch-gui/src/containers/blocks.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 00c395726a..20c0c19fce 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -445,7 +445,8 @@ Blocks.defaultProps = { }; const mapStateToProps = state => ({ - anyModalVisible: Object.keys(state.modals).some(key => state.modals[key]), + anyModalVisible: Object.keys(state.modals).some(key => state.modals[key]) || + state.mode.isFullScreen, extensionLibraryVisible: state.modals.extensionLibrary, locale: state.intl.locale, messages: state.intl.messages, From 02a6f6841b6d3cf070109230c2f7679476e6351c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 22 May 2018 12:49:13 -0400 Subject: [PATCH 0452/1971] Merge pull request #2123 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20180522141925 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-render to version 0.1.0-prerelease.201… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index bb38d36fec..cbbb2ce7c7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -93,7 +93,7 @@ "scratch-blocks": "0.1.0-prerelease.1526507753", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180521172423", - "scratch-render": "0.1.0-prerelease.20180521201254", + "scratch-render": "0.1.0-prerelease.20180522141925", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180521194642", "scratch-vm": "0.1.0-prerelease.1526929817", From f545f1c465aa8cdb942594a8332c770d2fa3fe2f Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Tue, 22 May 2018 15:05:29 -0400 Subject: [PATCH 0453/1971] Merge pull request #2083 from chrisgarrity/feature/refactor-gui-store Feature/refactor gui store Needed for https://github.com/LLK/scratch-www/issues/1874 --- .../src/components/menu-bar/menu-bar.jsx | 28 +++++++++++++++---- .../scratch-gui/src/containers/blocks.jsx | 12 ++++---- packages/scratch-gui/src/containers/stage.jsx | 6 ++-- .../scratch-gui/src/lib/vm-listener-hoc.jsx | 2 +- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 8a5fb5bb7d..7742e210e3 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -15,6 +15,7 @@ import {MenuItem, MenuSection} from '../menu/menu.jsx'; import ProjectSaver from '../../containers/project-saver.jsx'; import {openTipsLibrary} from '../../reducers/modals'; +import {setPlayer} from '../../reducers/mode'; import { openFileMenu, closeFileMenu, @@ -263,19 +264,33 @@ const MenuBar = props => (
- + {props.enableCommunity ? - + : + + + + }
@@ -352,12 +367,14 @@ const MenuBar = props => ( MenuBar.propTypes = { editMenuOpen: PropTypes.bool, + enableCommunity: PropTypes.bool, fileMenuOpen: PropTypes.bool, onClickEdit: PropTypes.func, onClickFile: PropTypes.func, onOpenTipLibrary: PropTypes.func, onRequestCloseEdit: PropTypes.func, - onRequestCloseFile: PropTypes.func + onRequestCloseFile: PropTypes.func, + onSeeCommunity: PropTypes.func }; const mapStateToProps = state => ({ @@ -370,7 +387,8 @@ const mapDispatchToProps = dispatch => ({ onClickFile: () => dispatch(openFileMenu()), onRequestCloseFile: () => dispatch(closeFileMenu()), onClickEdit: () => dispatch(openEditMenu()), - onRequestCloseEdit: () => dispatch(closeEditMenu()) + onRequestCloseEdit: () => dispatch(closeEditMenu()), + onSeeCommunity: () => dispatch(setPlayer(true)) }); export default connect( diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 20c0c19fce..b275b463f6 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -445,13 +445,15 @@ Blocks.defaultProps = { }; const mapStateToProps = state => ({ - anyModalVisible: Object.keys(state.modals).some(key => state.modals[key]) || - state.mode.isFullScreen, - extensionLibraryVisible: state.modals.extensionLibrary, + anyModalVisible: ( + Object.keys(state.scratchGui.modals).some(key => state.scratchGui.modals[key]) || + state.scratchGui.mode.isFullScreen + ), + extensionLibraryVisible: state.scratchGui.modals.extensionLibrary, locale: state.intl.locale, messages: state.intl.messages, - toolboxXML: state.toolbox.toolboxXML, - customProceduresVisible: state.customProcedures.active + toolboxXML: state.scratchGui.toolbox.toolboxXML, + customProceduresVisible: state.scratchGui.customProcedures.active }); const mapDispatchToProps = dispatch => ({ diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index b66efa2c50..881c9c929a 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -395,10 +395,10 @@ Stage.defaultProps = { }; const mapStateToProps = state => ({ - isColorPicking: state.colorPicker.active, - isFullScreen: state.mode.isFullScreen, + isColorPicking: state.scratchGui.colorPicker.active, + isFullScreen: state.scratchGui.mode.isFullScreen, // Do not use editor drag style in fullscreen or player mode. - useEditorDragStyle: !(state.mode.isFullScreen || state.mode.isPlayerOnly) + useEditorDragStyle: !(state.scratchGui.mode.isFullScreen || state.scratchGui.mode.isPlayerOnly) }); const mapDispatchToProps = dispatch => ({ diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 3713263206..3fd9e2096f 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -96,7 +96,7 @@ const vmListenerHOC = function (WrappedComponent) { attachKeyboardEvents: true }; const mapStateToProps = state => ({ - vm: state.vm + vm: state.scratchGui.vm }); const mapDispatchToProps = dispatch => ({ onTargetsUpdate: data => { From 79ccd7c3d665fee1280e8306cc0be5081214a17f Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 23 May 2018 18:13:21 -0400 Subject: [PATCH 0454/1971] Merge pull request #2139 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180523195410 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cbbb2ce7c7..193f253df6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "scratch-audio": "0.1.0-prerelease.1523977528", "scratch-blocks": "0.1.0-prerelease.1526507753", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180521172423", + "scratch-paint": "0.2.0-prerelease.20180523195410", "scratch-render": "0.1.0-prerelease.20180522141925", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180521194642", From ffbcd7bc23bcdeac6d819aff71169b8576cf0e70 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 24 May 2018 11:59:39 -0400 Subject: [PATCH 0455/1971] Merge pull request #2143 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180523204456 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 193f253df6..7d0af53cd0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "scratch-audio": "0.1.0-prerelease.1523977528", "scratch-blocks": "0.1.0-prerelease.1526507753", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180523195410", + "scratch-paint": "0.2.0-prerelease.20180523204456", "scratch-render": "0.1.0-prerelease.20180522141925", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180521194642", From 67d7d262126d3ef0875c6b3eec074fb2e3a47c63 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 24 May 2018 15:18:25 -0400 Subject: [PATCH 0456/1971] Merge pull request #2131 from paulkaplan/username-block Show username block in sensing --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7d0af53cd0..d098b748ec 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "scratch-render": "0.1.0-prerelease.20180522141925", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180521194642", - "scratch-vm": "0.1.0-prerelease.1526929817", + "scratch-vm": "0.1.0-prerelease.1527185283", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From 74ae8f258ed8a6b7516b91968be08674fb6a9059 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 24 May 2018 16:02:56 -0400 Subject: [PATCH 0457/1971] Merge pull request #2146 from paulkaplan/fix-toolbox-refresh-after-tabbing Fix toolbox refreshing for new variables after switching tabs --- packages/scratch-gui/src/containers/blocks.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index b275b463f6..4141e6cc87 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -125,6 +125,8 @@ class Blocks extends React.Component { if (this.props.isVisible) { // Scripts tab this.workspace.setVisible(true); this.props.vm.refreshWorkspace(); + // Re-enable toolbox refreshes without causing one. See #updateToolbox for more info. + this.workspace.toolboxRefreshEnabled_ = true; window.dispatchEvent(new Event('resize')); } else { this.workspace.setVisible(false); From 149a6864aaab2651120fbaaaef31b75ac31a9748 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 24 May 2018 16:44:05 -0400 Subject: [PATCH 0458/1971] Merge pull request #1991 from rschamp/platform-bowser Replace platform with bowser for browser detection --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d098b748ec..81be261313 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -37,6 +37,7 @@ "babel-plugin-transform-object-rest-spread": "^6.22.0", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.22.0", + "bowser": "1.9.3", "chromedriver": "2.38.2", "classnames": "2.2.5", "copy-webpack-plugin": "^4.5.1", @@ -63,7 +64,6 @@ "lodash.pick": "4.4.0", "minilog": "3.1.0", "mkdirp": "^0.5.1", - "platform": "1.3.5", "postcss-import": "^11.0.0", "postcss-loader": "^2.1.4", "postcss-simple-vars": "^4.0.0", From 99092b17e395ca8d5cd4f4d4b7ed1efa471100e7 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 24 May 2018 19:22:15 -0400 Subject: [PATCH 0459/1971] Merge pull request #2147 from fsih/updateDeps Update deps --- packages/scratch-gui/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 81be261313..859904a0df 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,11 +92,11 @@ "scratch-audio": "0.1.0-prerelease.1523977528", "scratch-blocks": "0.1.0-prerelease.1526507753", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180523204456", - "scratch-render": "0.1.0-prerelease.20180522141925", + "scratch-paint": "0.2.0-prerelease.20180524212223", + "scratch-render": "0.1.0-prerelease.20180524221242", "scratch-storage": "0.4.1", - "scratch-svg-renderer": "0.1.0-prerelease.20180521194642", - "scratch-vm": "0.1.0-prerelease.1527185283", + "scratch-svg-renderer": "0.1.0-prerelease.20180524210316", + "scratch-vm": "0.1.0-prerelease.1527198751", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From c55b05c2e3d685bda7a9e23b22f9fb2b638e0f58 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 25 May 2018 09:47:40 -0400 Subject: [PATCH 0460/1971] Merge pull request #2150 from kchadha/update-dependencies Update scratch-vm and scratch-render --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 859904a0df..1dfd5fdd1a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -93,10 +93,10 @@ "scratch-blocks": "0.1.0-prerelease.1526507753", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180524212223", - "scratch-render": "0.1.0-prerelease.20180524221242", + "scratch-render": "0.1.0-prerelease.20180525134043", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180524210316", - "scratch-vm": "0.1.0-prerelease.1527198751", + "scratch-vm": "0.1.0-prerelease.1527254075", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From 78711d4f0864edf850629187bdf230a086c6e62b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 30 May 2018 09:12:04 -0400 Subject: [PATCH 0461/1971] Merge pull request #2184 from paulkaplan/disable-sounds Disable sounds in scratch-blocks --- packages/scratch-gui/src/containers/blocks.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 4141e6cc87..ac1fd3ee3b 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -438,7 +438,8 @@ Blocks.defaultOptions = { dragShadowOpacity: 0.6 }, comments: false, - collapse: false + collapse: false, + sounds: false }; Blocks.defaultProps = { From d8d10d812827e5e21f40343e446797add22d8447 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 30 May 2018 09:33:13 -0400 Subject: [PATCH 0462/1971] Merge pull request #2186 from paulkaplan/update-deps-june Update dependencies --- packages/scratch-gui/package.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1dfd5fdd1a..53c8b27fed 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -38,7 +38,7 @@ "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.22.0", "bowser": "1.9.3", - "chromedriver": "2.38.2", + "chromedriver": "2.38.3", "classnames": "2.2.5", "copy-webpack-plugin": "^4.5.1", "css-loader": "^0.28.11", @@ -51,7 +51,7 @@ "eslint-plugin-react": "^7.5.1", "file-loader": "1.1.11", "get-float-time-domain-data": "0.1.0", - "get-user-media-promise": "1.1.1", + "get-user-media-promise": "1.1.4", "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", "html-webpack-plugin": "^3.2.0", "immutable": "3.8.2", @@ -74,17 +74,17 @@ "react-contextmenu": "2.9.2", "react-dom": "16.2.0", "react-draggable": "3.0.5", - "react-ga": "2.4.1", + "react-ga": "2.5.3", "react-intl": "2.4.0", "react-intl-redux": "0.7.0", - "react-modal": "3.3.2", + "react-modal": "3.4.4", "react-redux": "5.0.7", "react-responsive": "4.1.0", "react-style-proptype": "3.2.1", - "react-tabs": "2.2.1", + "react-tabs": "2.2.2", "react-test-renderer": "16.2.0", - "react-tooltip": "3.4.0", - "react-virtualized": "9.18.5", + "react-tooltip": "3.6.0", + "react-virtualized": "9.19.1", "redux": "3.7.2", "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", @@ -108,7 +108,7 @@ "webpack": "^4.6.0", "webpack-cli": "^2.0.15", "webpack-dev-server": "^3.1.3", - "xhr": "2.4.1" + "xhr": "2.5.0" }, "jest": { "setupFiles": [ From a81600d45587531d49501dcbba7e6cea9bdb5902 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 31 May 2018 10:50:01 -0400 Subject: [PATCH 0463/1971] Merge pull request #2193 from LLK/ericrosenbaum-patch-1 update scratch-vm --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 53c8b27fed..e1230e341a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "scratch-render": "0.1.0-prerelease.20180525134043", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180524210316", - "scratch-vm": "0.1.0-prerelease.1527254075", + "scratch-vm": "0.1.0-prerelease.1527690816", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From cfd073fddb91d33ee62f6d141596573bb38e0017 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 31 May 2018 14:09:36 -0400 Subject: [PATCH 0464/1971] Merge pull request #2196 from paulkaplan/fix-speech-updates Update scratch vm, render, audio and blocks --- packages/scratch-gui/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e1230e341a..3628501b94 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,14 +89,14 @@ "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "0.1.0-prerelease.1523977528", - "scratch-blocks": "0.1.0-prerelease.1526507753", + "scratch-audio": "0.1.0-prerelease.1525799078", + "scratch-blocks": "0.1.0-prerelease.1527723332", "scratch-l10n": "2.0.20180108132626", "scratch-paint": "0.2.0-prerelease.20180524212223", - "scratch-render": "0.1.0-prerelease.20180525134043", + "scratch-render": "0.1.0-prerelease.20180529202714", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180524210316", - "scratch-vm": "0.1.0-prerelease.1527690816", + "scratch-vm": "0.1.0-prerelease.1527782925", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From dddadebd626f81582a8c88041743fc0f1b23b7e3 Mon Sep 17 00:00:00 2001 From: kchadha Date: Thu, 31 May 2018 18:35:18 -0400 Subject: [PATCH 0465/1971] Merge pull request #2201 from kchadha/comment-css CSS updates for rendering block comments and update VM. --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3628501b94..77042dabed 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "scratch-render": "0.1.0-prerelease.20180529202714", "scratch-storage": "0.4.1", "scratch-svg-renderer": "0.1.0-prerelease.20180524210316", - "scratch-vm": "0.1.0-prerelease.1527782925", + "scratch-vm": "0.1.0-prerelease.1527804956", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From ee110ce4b2009e05a4d52daca4ad3275a3389a6f Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 31 May 2018 21:43:25 -0400 Subject: [PATCH 0466/1971] Merge pull request #2204 from rschamp/src-index-scratch-deps Update all scratch-* dependencies --- packages/scratch-gui/package.json | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 77042dabed..268f07fe25 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -78,6 +78,7 @@ "react-intl": "2.4.0", "react-intl-redux": "0.7.0", "react-modal": "3.4.4", + "react-popover": "0.5.7", "react-redux": "5.0.7", "react-responsive": "4.1.0", "react-style-proptype": "3.2.1", @@ -89,13 +90,13 @@ "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "0.1.0-prerelease.1525799078", - "scratch-blocks": "0.1.0-prerelease.1527723332", - "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180524212223", - "scratch-render": "0.1.0-prerelease.20180529202714", - "scratch-storage": "0.4.1", - "scratch-svg-renderer": "0.1.0-prerelease.20180524210316", + "scratch-audio": "0.1.0-prerelease.1527803318", + "scratch-blocks": "0.1.0-prerelease.1527805550", + "scratch-l10n": "2.0.20180511152022", + "scratch-paint": "0.2.0-prerelease.20180531225024", + "scratch-render": "0.1.0-prerelease.20180531213042", + "scratch-storage": "0.5.0", + "scratch-svg-renderer": "0.1.0-prerelease.20180531214630", "scratch-vm": "0.1.0-prerelease.1527804956", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From cd9949a16b6a41779a87cef16fed68d9919b7ea8 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 1 Jun 2018 11:01:48 -0400 Subject: [PATCH 0467/1971] Merge pull request #2210 from paulkaplan/fix-stack-glow Fix stack glow in toolbox by updating scratch-blocks --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 268f07fe25..62efcec6ea 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1527803318", - "scratch-blocks": "0.1.0-prerelease.1527805550", + "scratch-blocks": "0.1.0-prerelease.1527865144", "scratch-l10n": "2.0.20180511152022", "scratch-paint": "0.2.0-prerelease.20180531225024", "scratch-render": "0.1.0-prerelease.20180531213042", From dbafbb67052f19560f012231290e61c2119047e5 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 4 Jun 2018 11:24:19 -0400 Subject: [PATCH 0468/1971] Merge pull request #2224 from LLK/greenkeeper/initial MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update dependencies to enable Greenkeeper 🌴 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 62efcec6ea..706fee957b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,8 +96,8 @@ "scratch-paint": "0.2.0-prerelease.20180531225024", "scratch-render": "0.1.0-prerelease.20180531213042", "scratch-storage": "0.5.0", + "scratch-vm": "0.1.0-prerelease.1527875455", "scratch-svg-renderer": "0.1.0-prerelease.20180531214630", - "scratch-vm": "0.1.0-prerelease.1527804956", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From 6b7d7d2dd845f64f92ca3ea16c4559057ff75b45 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Mon, 4 Jun 2018 16:32:05 -0400 Subject: [PATCH 0469/1971] Merge pull request #2234 from LLK/greenkeeper/scratch-l10n-3.0.20180604162003 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 706fee957b..970a7cab2b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1527803318", "scratch-blocks": "0.1.0-prerelease.1527865144", - "scratch-l10n": "2.0.20180511152022", + "scratch-l10n": "3.0.20180604162003", "scratch-paint": "0.2.0-prerelease.20180531225024", "scratch-render": "0.1.0-prerelease.20180531213042", "scratch-storage": "0.5.0", From 5995f8e4a109c9bd63dfca09aeaf45254e462ce3 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 5 Jun 2018 11:33:26 -0400 Subject: [PATCH 0470/1971] Merge pull request #2244 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20180605145739 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 970a7cab2b..299624a835 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-blocks": "0.1.0-prerelease.1527865144", "scratch-l10n": "3.0.20180604162003", "scratch-paint": "0.2.0-prerelease.20180531225024", - "scratch-render": "0.1.0-prerelease.20180531213042", + "scratch-render": "0.1.0-prerelease.20180605145739", "scratch-storage": "0.5.0", "scratch-vm": "0.1.0-prerelease.1527875455", "scratch-svg-renderer": "0.1.0-prerelease.20180531214630", From 9337760f4a800e50d00190f613df56421be21391 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 5 Jun 2018 11:34:13 -0400 Subject: [PATCH 0471/1971] Merge pull request #2242 from LLK/greenkeeper/react-tooltip-3.6.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-tooltip to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 299624a835..d851ace82f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -84,7 +84,7 @@ "react-style-proptype": "3.2.1", "react-tabs": "2.2.2", "react-test-renderer": "16.2.0", - "react-tooltip": "3.6.0", + "react-tooltip": "3.6.1", "react-virtualized": "9.19.1", "redux": "3.7.2", "redux-mock-store": "^1.2.3", From a2001c4d40720bb09fad2dbd762aca02da5083ea Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 5 Jun 2018 12:22:24 -0400 Subject: [PATCH 0472/1971] Merge pull request #2248 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20180605154326 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d851ace82f..75e32734dc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "scratch-render": "0.1.0-prerelease.20180605145739", "scratch-storage": "0.5.0", "scratch-vm": "0.1.0-prerelease.1527875455", - "scratch-svg-renderer": "0.1.0-prerelease.20180531214630", + "scratch-svg-renderer": "0.2.0-prerelease.20180605154326", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From d756a0c56b08fdf0bf7d69b5c0bbc22bc370c917 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 5 Jun 2018 12:22:38 -0400 Subject: [PATCH 0473/1971] Merge pull request #2250 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180605153807 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 75e32734dc..853efeec6e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -93,7 +93,7 @@ "scratch-audio": "0.1.0-prerelease.1527803318", "scratch-blocks": "0.1.0-prerelease.1527865144", "scratch-l10n": "3.0.20180604162003", - "scratch-paint": "0.2.0-prerelease.20180531225024", + "scratch-paint": "0.2.0-prerelease.20180605153807", "scratch-render": "0.1.0-prerelease.20180605145739", "scratch-storage": "0.5.0", "scratch-vm": "0.1.0-prerelease.1527875455", From 163827965f8c0bd456ecc9dd9aef342daab1ec5b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 5 Jun 2018 14:12:27 -0400 Subject: [PATCH 0474/1971] Merge pull request #2233 from paulkaplan/sauce-smoke-test Add IE11 and Safari 9/10 tests to browser smoke test using Sauce --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 853efeec6e..bca47a9946 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -13,6 +13,7 @@ "test:integration": "jest --runInBand test[\\\\/]integration", "test:lint": "eslint . --ext .js,.jsx", "test:unit": "jest test[\\\\/]unit", + "test:smoke": "jest --runInBand test[\\\\/]smoke", "watch": "webpack --progress --colors --watch" }, "author": "Massachusetts Institute of Technology", From 4c73a12fc86f79ea943de785028a02ef70403c84 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 6 Jun 2018 10:15:59 -0400 Subject: [PATCH 0475/1971] Merge pull request #2243 from LLK/greenkeeper/scratch-audio-0.1.0-prerelease.1528210666 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-audio to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index bca47a9946..de2a41a7e0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "0.1.0-prerelease.1527803318", + "scratch-audio": "0.1.0-prerelease.1528210666", "scratch-blocks": "0.1.0-prerelease.1527865144", "scratch-l10n": "3.0.20180604162003", "scratch-paint": "0.2.0-prerelease.20180605153807", From 2f401444af4e6294b2bca8c25fd120104f2a25da Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 7 Jun 2018 10:48:49 -0400 Subject: [PATCH 0476/1971] Merge pull request #2263 from LLK/greenkeeper/chromedriver-2.39.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update chromedriver to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index de2a41a7e0..cb064a466a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -39,7 +39,7 @@ "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.22.0", "bowser": "1.9.3", - "chromedriver": "2.38.3", + "chromedriver": "2.39.0", "classnames": "2.2.5", "copy-webpack-plugin": "^4.5.1", "css-loader": "^0.28.11", From d45392a633a890f8b078d0a664333feb6993c75b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 7 Jun 2018 11:07:11 -0400 Subject: [PATCH 0477/1971] Merge pull request #2271 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20180607141644 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cb064a466a..2e225cafc6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-render": "0.1.0-prerelease.20180605145739", "scratch-storage": "0.5.0", "scratch-vm": "0.1.0-prerelease.1527875455", - "scratch-svg-renderer": "0.2.0-prerelease.20180605154326", + "scratch-svg-renderer": "0.2.0-prerelease.20180607141644", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From c085c1b37712061bf27d873f227a007ebf1ec7ef Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 7 Jun 2018 11:08:37 -0400 Subject: [PATCH 0478/1971] Merge pull request #2272 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180607142353 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2e225cafc6..5d05869322 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-audio": "0.1.0-prerelease.1528210666", "scratch-blocks": "0.1.0-prerelease.1527865144", "scratch-l10n": "3.0.20180604162003", - "scratch-paint": "0.2.0-prerelease.20180605153807", + "scratch-paint": "0.2.0-prerelease.20180607142353", "scratch-render": "0.1.0-prerelease.20180605145739", "scratch-storage": "0.5.0", "scratch-vm": "0.1.0-prerelease.1527875455", From bfca6ca78c88df6228a901d53fcc9eea62afa207 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 7 Jun 2018 11:56:45 -0400 Subject: [PATCH 0479/1971] Merge pull request #2275 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180607153112 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5d05869322..fc10d0a8bc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-audio": "0.1.0-prerelease.1528210666", "scratch-blocks": "0.1.0-prerelease.1527865144", "scratch-l10n": "3.0.20180604162003", - "scratch-paint": "0.2.0-prerelease.20180607142353", + "scratch-paint": "0.2.0-prerelease.20180607153112", "scratch-render": "0.1.0-prerelease.20180605145739", "scratch-storage": "0.5.0", "scratch-vm": "0.1.0-prerelease.1527875455", From 513a0acc57baa217ddb6b664183871b74fd476a1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 7 Jun 2018 11:58:28 -0400 Subject: [PATCH 0480/1971] Merge pull request #2274 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1528384994 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index fc10d0a8bc..925a77b2b6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1528210666", - "scratch-blocks": "0.1.0-prerelease.1527865144", + "scratch-blocks": "0.1.0-prerelease.1528384994", "scratch-l10n": "3.0.20180604162003", "scratch-paint": "0.2.0-prerelease.20180607153112", "scratch-render": "0.1.0-prerelease.20180605145739", From 967523ff8e9192a1efdce35b76299e02a66d0fa2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 7 Jun 2018 13:53:28 -0400 Subject: [PATCH 0481/1971] Merge pull request #2277 from paulkaplan/update-vm Update scratch vm --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 925a77b2b6..ea968fd8e0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "scratch-paint": "0.2.0-prerelease.20180607153112", "scratch-render": "0.1.0-prerelease.20180605145739", "scratch-storage": "0.5.0", - "scratch-vm": "0.1.0-prerelease.1527875455", + "scratch-vm": "0.1.0-prerelease.1528384542", "scratch-svg-renderer": "0.2.0-prerelease.20180607141644", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 21e6f2dc7e94107f6a7763150cf85eea1c40fce0 Mon Sep 17 00:00:00 2001 From: kchadha Date: Thu, 7 Jun 2018 20:50:24 -0400 Subject: [PATCH 0482/1971] Merge pull request #2265 from ericrosenbaum/feature/when-touching Add when touching hat --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ea968fd8e0..4e60e963fa 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,12 +92,12 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1528210666", - "scratch-blocks": "0.1.0-prerelease.1528384994", + "scratch-blocks": "0.1.0-prerelease.1528400332", "scratch-l10n": "3.0.20180604162003", "scratch-paint": "0.2.0-prerelease.20180607153112", "scratch-render": "0.1.0-prerelease.20180605145739", "scratch-storage": "0.5.0", - "scratch-vm": "0.1.0-prerelease.1528384542", + "scratch-vm": "0.1.0-prerelease.1528399883", "scratch-svg-renderer": "0.2.0-prerelease.20180607141644", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 2317077cd76177c1fc39791bdac2bef350b8912e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 11 Jun 2018 11:46:25 -0400 Subject: [PATCH 0483/1971] Merge pull request #2282 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180607215543 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4e60e963fa..475c11042f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-audio": "0.1.0-prerelease.1528210666", "scratch-blocks": "0.1.0-prerelease.1528400332", "scratch-l10n": "3.0.20180604162003", - "scratch-paint": "0.2.0-prerelease.20180607153112", + "scratch-paint": "0.2.0-prerelease.20180607215543", "scratch-render": "0.1.0-prerelease.20180605145739", "scratch-storage": "0.5.0", "scratch-vm": "0.1.0-prerelease.1528399883", From db989081e148803c53612c22883470d12fb2708e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 11 Jun 2018 11:46:45 -0400 Subject: [PATCH 0484/1971] Merge pull request #2284 from LLK/greenkeeper/classnames-2.2.6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update classnames to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 475c11042f..25127c143a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -40,7 +40,7 @@ "babel-preset-react": "^6.22.0", "bowser": "1.9.3", "chromedriver": "2.39.0", - "classnames": "2.2.5", + "classnames": "2.2.6", "copy-webpack-plugin": "^4.5.1", "css-loader": "^0.28.11", "enzyme": "^3.1.0", From 445d50c34d3fe314ada5aceca3241e21771c359b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 11 Jun 2018 11:47:31 -0400 Subject: [PATCH 0485/1971] Merge pull request #2304 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1528492670 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 25127c143a..6354f3fb79 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1528210666", - "scratch-blocks": "0.1.0-prerelease.1528400332", + "scratch-blocks": "0.1.0-prerelease.1528492670", "scratch-l10n": "3.0.20180604162003", "scratch-paint": "0.2.0-prerelease.20180607215543", "scratch-render": "0.1.0-prerelease.20180605145739", From 0d52204cb7efb5bc0f5af464aaaad1faf347518e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 11 Jun 2018 14:39:17 -0400 Subject: [PATCH 0486/1971] Merge pull request #2293 from LLK/greenkeeper/chromedriver-2.40.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update chromedriver to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6354f3fb79..f123f173af 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -39,7 +39,7 @@ "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.22.0", "bowser": "1.9.3", - "chromedriver": "2.39.0", + "chromedriver": "2.40.0", "classnames": "2.2.6", "copy-webpack-plugin": "^4.5.1", "css-loader": "^0.28.11", From b5f5c3cf8579c7641493a37b1f1191ac9092c8b2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 11 Jun 2018 15:43:29 -0400 Subject: [PATCH 0487/1971] Merge pull request #2314 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1528736515 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f123f173af..da38a14790 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1528210666", - "scratch-blocks": "0.1.0-prerelease.1528492670", + "scratch-blocks": "0.1.0-prerelease.1528736515", "scratch-l10n": "3.0.20180604162003", "scratch-paint": "0.2.0-prerelease.20180607215543", "scratch-render": "0.1.0-prerelease.20180605145739", From c695faccf72942329e0c435534fb9ffabb4aa779 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 11 Jun 2018 15:43:47 -0400 Subject: [PATCH 0488/1971] Merge pull request #2312 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180611155341 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index da38a14790..4d8c0f8c60 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "scratch-audio": "0.1.0-prerelease.1528210666", "scratch-blocks": "0.1.0-prerelease.1528736515", "scratch-l10n": "3.0.20180604162003", - "scratch-paint": "0.2.0-prerelease.20180607215543", + "scratch-paint": "0.2.0-prerelease.20180611155341", "scratch-render": "0.1.0-prerelease.20180605145739", "scratch-storage": "0.5.0", "scratch-vm": "0.1.0-prerelease.1528399883", From b7db2985fb5e8ebea7e286d08d3808915bde4369 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 12 Jun 2018 09:14:56 -0400 Subject: [PATCH 0489/1971] Merge pull request #2316 from LLK/greenkeeper/scratch-storage-0.5.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-storage to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4d8c0f8c60..b2583beaac 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "scratch-l10n": "3.0.20180604162003", "scratch-paint": "0.2.0-prerelease.20180611155341", "scratch-render": "0.1.0-prerelease.20180605145739", - "scratch-storage": "0.5.0", + "scratch-storage": "0.5.1", "scratch-vm": "0.1.0-prerelease.1528399883", "scratch-svg-renderer": "0.2.0-prerelease.20180607141644", "selenium-webdriver": "3.6.0", From ec1d131467c7389722c0c29ccb8ea082e57a1b6c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 12 Jun 2018 09:16:59 -0400 Subject: [PATCH 0490/1971] Merge pull request #2278 from LLK/greenkeeper/scratch-audio-0.1.0-prerelease.1528394075 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-audio to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b2583beaac..e734535c61 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "0.1.0-prerelease.1528210666", + "scratch-audio": "0.1.0-prerelease.1528394075", "scratch-blocks": "0.1.0-prerelease.1528736515", "scratch-l10n": "3.0.20180604162003", "scratch-paint": "0.2.0-prerelease.20180611155341", From 09c71c201435652008cea8696382882f1a814190 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 12 Jun 2018 09:33:34 -0400 Subject: [PATCH 0491/1971] Merge pull request #2319 from LLK/greenkeeper/scratch-l10n-3.0.20180611175036 chore(package): update scratch-l10n to version 3.0.20180611175036 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e734535c61..d95f917d5e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -93,7 +93,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1528394075", "scratch-blocks": "0.1.0-prerelease.1528736515", - "scratch-l10n": "3.0.20180604162003", + "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180611155341", "scratch-render": "0.1.0-prerelease.20180605145739", "scratch-storage": "0.5.1", From ea174acac067d7db8bb21c2236131c862aaff8b3 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 12 Jun 2018 15:46:34 -0400 Subject: [PATCH 0492/1971] Merge pull request #2321 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1528823380 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d95f917d5e..0ee5305b14 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1528394075", - "scratch-blocks": "0.1.0-prerelease.1528736515", + "scratch-blocks": "0.1.0-prerelease.1528823380", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180611155341", "scratch-render": "0.1.0-prerelease.20180605145739", From 7642d25275e3fd8d069d4b0ea040fb190d124d35 Mon Sep 17 00:00:00 2001 From: kchadha Date: Tue, 12 Jun 2018 18:52:12 -0400 Subject: [PATCH 0493/1971] Merge pull request #2322 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1528843394 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0ee5305b14..a4200eef15 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1528394075", - "scratch-blocks": "0.1.0-prerelease.1528823380", + "scratch-blocks": "0.1.0-prerelease.1528843394", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180611155341", "scratch-render": "0.1.0-prerelease.20180605145739", From 9ce259c90c5a3d6c124b2febf673b48c2f23dca4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 13 Jun 2018 13:10:33 -0400 Subject: [PATCH 0494/1971] Merge pull request #2327 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1528907522 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a4200eef15..8bbb75ebb2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1528394075", - "scratch-blocks": "0.1.0-prerelease.1528843394", + "scratch-blocks": "0.1.0-prerelease.1528907522", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180611155341", "scratch-render": "0.1.0-prerelease.20180605145739", From cecea3497b111b70808f932fe2f3f12b7468c9c3 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 13 Jun 2018 11:17:51 -0700 Subject: [PATCH 0495/1971] Merge pull request #2281 from cwillisf/stage-size-toggle Stage size toggle --- packages/scratch-gui/package.json | 3 ++- packages/scratch-gui/src/containers/blocks.jsx | 10 +++++++++- packages/scratch-gui/src/containers/stage.jsx | 9 ++++----- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8bbb75ebb2..ecc476e14a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -57,6 +57,7 @@ "html-webpack-plugin": "^3.2.0", "immutable": "3.8.2", "jest": "^21.0.0", + "keymirror": "0.1.1", "lodash.bindall": "4.4.0", "lodash.debounce": "4.0.8", "lodash.defaultsdeep": "4.6.0", @@ -97,8 +98,8 @@ "scratch-paint": "0.2.0-prerelease.20180611155341", "scratch-render": "0.1.0-prerelease.20180605145739", "scratch-storage": "0.5.1", - "scratch-vm": "0.1.0-prerelease.1528399883", "scratch-svg-renderer": "0.2.0-prerelease.20180607141644", + "scratch-vm": "0.1.0-prerelease.1528399883", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index ac1fd3ee3b..5f1ec793e6 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -13,6 +13,7 @@ import BlocksComponent from '../components/blocks/blocks.jsx'; import ExtensionLibrary from './extension-library.jsx'; import CustomProcedures from './custom-procedures.jsx'; import errorBoundaryHOC from '../lib/error-boundary-hoc.jsx'; +import {STAGE_DISPLAY_SIZES} from '../lib/layout-constants'; import {connect} from 'react-redux'; import {updateToolbox} from '../reducers/toolbox'; @@ -97,7 +98,8 @@ class Blocks extends React.Component { this.props.extensionLibraryVisible !== nextProps.extensionLibraryVisible || this.props.customProceduresVisible !== nextProps.customProceduresVisible || this.props.locale !== nextProps.locale || - this.props.anyModalVisible !== nextProps.anyModalVisible + this.props.anyModalVisible !== nextProps.anyModalVisible || + this.props.stageSize !== nextProps.stageSize ); } componentDidUpdate (prevProps) { @@ -118,6 +120,10 @@ class Blocks extends React.Component { }, 0); } if (this.props.isVisible === prevProps.isVisible) { + if (this.props.stageSize !== prevProps.stageSize) { + // force workspace to redraw for the new stage size + window.dispatchEvent(new Event('resize')); + } return; } // @todo hack to resize blockly manually in case resize happened while hidden @@ -329,6 +335,7 @@ class Blocks extends React.Component { customProceduresVisible, extensionLibraryVisible, options, + stageSize, vm, isVisible, onActivateColorPicker, @@ -409,6 +416,7 @@ Blocks.propTypes = { comments: PropTypes.bool, collapse: PropTypes.bool }), + stageSize: PropTypes.oneOf(Object.keys(STAGE_DISPLAY_SIZES)).isRequired, toolboxXML: PropTypes.string, updateToolboxState: PropTypes.func, vm: PropTypes.instanceOf(VM).isRequired diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 881c9c929a..0013307f8d 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -5,6 +5,7 @@ import Renderer from 'scratch-render'; import VM from 'scratch-vm'; import {connect} from 'react-redux'; +import {STAGE_DISPLAY_SIZES} from '../lib/layout-constants'; import {getEventXY} from '../lib/touch-utils'; import VideoProvider from '../lib/video/video-provider'; import {SVGRenderer as V2SVGAdapter} from 'scratch-svg-renderer'; @@ -63,8 +64,7 @@ class Stage extends React.Component { this.props.vm.setVideoProvider(new VideoProvider()); } shouldComponentUpdate (nextProps, nextState) { - return this.props.width !== nextProps.width || - this.props.height !== nextProps.height || + return this.props.stageSize !== nextProps.stageSize || this.props.isColorPicking !== nextProps.isColorPicking || this.state.colorInfo !== nextState.colorInfo || this.props.isFullScreen !== nextProps.isFullScreen || @@ -380,14 +380,13 @@ class Stage extends React.Component { } Stage.propTypes = { - height: PropTypes.number, isColorPicking: PropTypes.bool, isFullScreen: PropTypes.bool.isRequired, onActivateColorPicker: PropTypes.func, onDeactivateColorPicker: PropTypes.func, + stageSize: PropTypes.oneOf(Object.keys(STAGE_DISPLAY_SIZES)).isRequired, useEditorDragStyle: PropTypes.bool, - vm: PropTypes.instanceOf(VM).isRequired, - width: PropTypes.number + vm: PropTypes.instanceOf(VM).isRequired }; Stage.defaultProps = { From 78cfb8ed9129f5773ee68fdb43d74872efc079ae Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 13 Jun 2018 17:15:32 -0400 Subject: [PATCH 0496/1971] Merge pull request #2335 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20180613184320 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ecc476e14a..1256277bb4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-paint": "0.2.0-prerelease.20180611155341", "scratch-render": "0.1.0-prerelease.20180605145739", "scratch-storage": "0.5.1", - "scratch-svg-renderer": "0.2.0-prerelease.20180607141644", + "scratch-svg-renderer": "0.2.0-prerelease.20180613184320", "scratch-vm": "0.1.0-prerelease.1528399883", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From e6e633099ce1ad0aca3acc5297dc16c993a24ae9 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 13 Jun 2018 17:17:41 -0400 Subject: [PATCH 0497/1971] Merge pull request #2333 from rschamp/prune-gh-pages Prune gh-pages to match our branch list after each build --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1256277bb4..050272cc74 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -7,6 +7,7 @@ "build": "npm run clean && webpack --progress --colors --bail", "clean": "rimraf ./build && mkdirp build && rimraf ./dist && mkdirp dist", "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", + "prune": "./prune-gh-pages.sh", "i18n:src": "babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/ ./translations/", "start": "webpack-dev-server", "test": "npm run test:lint && npm run test:unit && npm run build && npm run test:integration", From 4a5f1a3caafe19f3d8d5c97c8d5fe5804c5bbc99 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 14 Jun 2018 09:24:54 -0400 Subject: [PATCH 0498/1971] Merge pull request #2342 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180613212020 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 050272cc74..f90bac5026 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "scratch-audio": "0.1.0-prerelease.1528394075", "scratch-blocks": "0.1.0-prerelease.1528907522", "scratch-l10n": "3.0.20180611175036", - "scratch-paint": "0.2.0-prerelease.20180611155341", + "scratch-paint": "0.2.0-prerelease.20180613212020", "scratch-render": "0.1.0-prerelease.20180605145739", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180613184320", From 6015f6ba3a66cc2a137f436dae9ec772de19c57d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 14 Jun 2018 09:25:50 -0400 Subject: [PATCH 0499/1971] Merge pull request #2337 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20180613192740 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f90bac5026..7b3f9b2bd0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "scratch-blocks": "0.1.0-prerelease.1528907522", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180613212020", - "scratch-render": "0.1.0-prerelease.20180605145739", + "scratch-render": "0.1.0-prerelease.20180613192740", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180613184320", "scratch-vm": "0.1.0-prerelease.1528399883", From 721b7cfe02a4c3a0473305cbda487cc8e004f976 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 14 Jun 2018 10:25:18 -0400 Subject: [PATCH 0500/1971] Merge pull request #2344 from LLK/update-vm-99 Update scratch-vm to bring in new APIs --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7b3f9b2bd0..fc0a437b2b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -100,7 +100,7 @@ "scratch-render": "0.1.0-prerelease.20180613192740", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180613184320", - "scratch-vm": "0.1.0-prerelease.1528399883", + "scratch-vm": "0.1.0-prerelease.1528920435", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From 5ec895ce296f04befa28af4f1b30a909dd0aa09b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 14 Jun 2018 10:43:19 -0400 Subject: [PATCH 0501/1971] Merge pull request #2345 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180614144007 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index fc0a437b2b..232013399a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "scratch-audio": "0.1.0-prerelease.1528394075", "scratch-blocks": "0.1.0-prerelease.1528907522", "scratch-l10n": "3.0.20180611175036", - "scratch-paint": "0.2.0-prerelease.20180613212020", + "scratch-paint": "0.2.0-prerelease.20180614144007", "scratch-render": "0.1.0-prerelease.20180613192740", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180613184320", From fecc47e6acb268bbde5fc41a16fd3f1a41b7e95b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 14 Jun 2018 11:44:51 -0400 Subject: [PATCH 0502/1971] Merge pull request #2347 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180614154247 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 232013399a..f01db91c6a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "scratch-audio": "0.1.0-prerelease.1528394075", "scratch-blocks": "0.1.0-prerelease.1528907522", "scratch-l10n": "3.0.20180611175036", - "scratch-paint": "0.2.0-prerelease.20180614144007", + "scratch-paint": "0.2.0-prerelease.20180614154247", "scratch-render": "0.1.0-prerelease.20180613192740", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180613184320", From 34031d43284b10348574cd12a634b477c4e50b76 Mon Sep 17 00:00:00 2001 From: kchadha Date: Thu, 14 Jun 2018 11:50:10 -0400 Subject: [PATCH 0503/1971] Merge pull request #2346 from kchadha/enable-comments Enable comments --- packages/scratch-gui/src/containers/blocks.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 5f1ec793e6..0363924214 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -445,7 +445,7 @@ Blocks.defaultOptions = { fieldShadow: 'rgba(255, 255, 255, 0.3)', dragShadowOpacity: 0.6 }, - comments: false, + comments: true, collapse: false, sounds: false }; From e2ef03f90cdc8cc8c633214832cd4fca0c879bd6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 14 Jun 2018 11:59:21 -0400 Subject: [PATCH 0504/1971] Merge pull request #2348 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180614155043 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f01db91c6a..505ee32924 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "scratch-audio": "0.1.0-prerelease.1528394075", "scratch-blocks": "0.1.0-prerelease.1528907522", "scratch-l10n": "3.0.20180611175036", - "scratch-paint": "0.2.0-prerelease.20180614154247", + "scratch-paint": "0.2.0-prerelease.20180614155043", "scratch-render": "0.1.0-prerelease.20180613192740", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180613184320", From 02d9002f15d82e77986206a08b37c8b0c32c18ba Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 14 Jun 2018 13:21:42 -0400 Subject: [PATCH 0505/1971] Merge pull request #2350 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180614161221 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 505ee32924..6b4b0f97e4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "scratch-audio": "0.1.0-prerelease.1528394075", "scratch-blocks": "0.1.0-prerelease.1528907522", "scratch-l10n": "3.0.20180611175036", - "scratch-paint": "0.2.0-prerelease.20180614155043", + "scratch-paint": "0.2.0-prerelease.20180614161221", "scratch-render": "0.1.0-prerelease.20180613192740", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180613184320", From b5982c405cb491f5bcf865a77417490a0641ce9c Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 14 Jun 2018 13:53:48 -0400 Subject: [PATCH 0506/1971] Merge pull request #2326 from chrisgarrity/feature/enable-language-selector Enable language selection menu --- .../src/components/menu-bar/menu-bar.jsx | 98 +++++++++++++++---- 1 file changed, 80 insertions(+), 18 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 7742e210e3..e4de61ffde 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -1,6 +1,6 @@ import classNames from 'classnames'; import {connect} from 'react-redux'; -import {FormattedMessage} from 'react-intl'; +import {defineMessages, FormattedMessage, injectIntl, intlShape} from 'react-intl'; import PropTypes from 'prop-types'; import React from 'react'; @@ -22,7 +22,10 @@ import { fileMenuOpen, openEditMenu, closeEditMenu, - editMenuOpen + editMenuOpen, + openLanguageMenu, + closeLanguageMenu, + languageMenuOpen } from '../../reducers/menus'; import styles from './menu-bar.css'; @@ -32,29 +35,56 @@ import feedbackIcon from './icon--feedback.svg'; import profileIcon from './icon--profile.png'; import communityIcon from './icon--see-community.svg'; import dropdownCaret from '../language-selector/dropdown-caret.svg'; +import languageIcon from '../language-selector/language-icon.svg'; + import scratchLogo from './scratch-logo.svg'; import helpIcon from './icon--help.svg'; +const ariaMessages = defineMessages({ + language: { + id: 'gui.menuBar.LanguageSelector', + defaultMessage: 'language selector', + description: 'accessibility text for the language selection menu' + }, + howTo: { + id: 'gui.menuBar.howToLibrary', + defaultMessage: 'How-to Library', + description: 'accessibility text for the how-to library button' + } +}); + const MenuBarItemTooltip = ({ children, className, + enable, id, place = 'bottom' -}) => ( - - {children} - -); +}) => { + if (enable) { + return ( + + {children} + + ); + } + return ( + + {children} + + ); +}; + MenuBarItemTooltip.propTypes = { children: PropTypes.node, className: PropTypes.string, + enable: PropTypes.bool, id: PropTypes.string, place: PropTypes.oneOf(['top', 'bottom', 'left', 'right']) }; @@ -111,12 +141,37 @@ const MenuBar = props => ( src={scratchLogo} />
-
+
- +
+ + +
+ + + +
(
@@ -369,17 +424,22 @@ MenuBar.propTypes = { editMenuOpen: PropTypes.bool, enableCommunity: PropTypes.bool, fileMenuOpen: PropTypes.bool, + intl: intlShape, + languageMenuOpen: PropTypes.bool, onClickEdit: PropTypes.func, onClickFile: PropTypes.func, + onClickLanguage: PropTypes.func, onOpenTipLibrary: PropTypes.func, onRequestCloseEdit: PropTypes.func, onRequestCloseFile: PropTypes.func, + onRequestCloseLanguage: PropTypes.func, onSeeCommunity: PropTypes.func }; const mapStateToProps = state => ({ fileMenuOpen: fileMenuOpen(state), - editMenuOpen: editMenuOpen(state) + editMenuOpen: editMenuOpen(state), + languageMenuOpen: languageMenuOpen(state) }); const mapDispatchToProps = dispatch => ({ @@ -388,10 +448,12 @@ const mapDispatchToProps = dispatch => ({ onRequestCloseFile: () => dispatch(closeFileMenu()), onClickEdit: () => dispatch(openEditMenu()), onRequestCloseEdit: () => dispatch(closeEditMenu()), + onClickLanguage: () => dispatch(openLanguageMenu()), + onRequestCloseLanguage: () => dispatch(closeLanguageMenu()), onSeeCommunity: () => dispatch(setPlayer(true)) }); -export default connect( +export default injectIntl(connect( mapStateToProps, mapDispatchToProps -)(MenuBar); +)(MenuBar)); From 99846cd79f3fb6a62abef7158ba2d6427a4a8f46 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 14 Jun 2018 15:32:50 -0400 Subject: [PATCH 0507/1971] Merge pull request #2317 from mzgoddard/green-player use new SoundPlayer in SoundLibrary --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6b4b0f97e4..3b709da65b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -93,7 +93,7 @@ "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "0.1.0-prerelease.1528394075", + "scratch-audio": "0.1.0-prerelease.1528996828", "scratch-blocks": "0.1.0-prerelease.1528907522", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180614161221", From 542921a65cb282b5cd3fd71c62ff6ec800bdaa53 Mon Sep 17 00:00:00 2001 From: kchadha Date: Thu, 14 Jun 2018 18:44:00 -0400 Subject: [PATCH 0508/1971] Merge pull request #2356 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1529012349 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3b709da65b..5d9d3f11ff 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1528996828", - "scratch-blocks": "0.1.0-prerelease.1528907522", + "scratch-blocks": "0.1.0-prerelease.1529012349", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180614161221", "scratch-render": "0.1.0-prerelease.20180613192740", From 6961ddb53e28e1ecd3b9c78d32cbf061f74115f2 Mon Sep 17 00:00:00 2001 From: kchadha Date: Thu, 14 Jun 2018 22:59:57 -0400 Subject: [PATCH 0509/1971] Merge pull request #2357 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1529016587 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5d9d3f11ff..2042d219ad 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1528996828", - "scratch-blocks": "0.1.0-prerelease.1529012349", + "scratch-blocks": "0.1.0-prerelease.1529016587", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180614161221", "scratch-render": "0.1.0-prerelease.20180613192740", From d90ee41d622c1d35340f7238db05b230fc9f41df Mon Sep 17 00:00:00 2001 From: kchadha Date: Thu, 14 Jun 2018 23:02:27 -0400 Subject: [PATCH 0510/1971] Merge pull request #2358 from kchadha/update-vm Update scratch-vm. --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2042d219ad..42dfc6d552 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -100,7 +100,7 @@ "scratch-render": "0.1.0-prerelease.20180613192740", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180613184320", - "scratch-vm": "0.1.0-prerelease.1528920435", + "scratch-vm": "0.1.0-prerelease.1529017807", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From 96270c3757acada9825680c8c4b59606512df40a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 15 Jun 2018 09:40:15 -0400 Subject: [PATCH 0511/1971] Merge pull request #2360 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20180615131212 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 42dfc6d552..a5875e0f7b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "scratch-blocks": "0.1.0-prerelease.1529016587", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180614161221", - "scratch-render": "0.1.0-prerelease.20180613192740", + "scratch-render": "0.1.0-prerelease.20180615131212", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180613184320", "scratch-vm": "0.1.0-prerelease.1529017807", From a9beaf0d45be2d500cae0c8fb51fbfc69fedce47 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 15 Jun 2018 12:17:57 -0400 Subject: [PATCH 0512/1971] Merge pull request #2365 from kchadha/disable-comments Disable comments --- packages/scratch-gui/src/containers/blocks.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 0363924214..5f1ec793e6 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -445,7 +445,7 @@ Blocks.defaultOptions = { fieldShadow: 'rgba(255, 255, 255, 0.3)', dragShadowOpacity: 0.6 }, - comments: true, + comments: false, collapse: false, sounds: false }; From 6a834a5e3cd2810a4116ed5160e5fa776045e707 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 15 Jun 2018 16:05:32 -0400 Subject: [PATCH 0513/1971] Merge pull request #2369 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1529085495 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.152… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a5875e0f7b..8b3132e317 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1528996828", - "scratch-blocks": "0.1.0-prerelease.1529016587", + "scratch-blocks": "0.1.0-prerelease.1529085495", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180614161221", "scratch-render": "0.1.0-prerelease.20180615131212", From c62ceafa4cbd7d2e9b7dfa272114693ffe1e673e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 18 Jun 2018 11:16:18 -0400 Subject: [PATCH 0514/1971] Merge pull request #2377 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1529329760 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8b3132e317..0fa0c80bbb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1528996828", - "scratch-blocks": "0.1.0-prerelease.1529085495", + "scratch-blocks": "0.1.0-prerelease.1529329760", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180614161221", "scratch-render": "0.1.0-prerelease.20180615131212", From d0b71d5f186cf0bc556484ad7729063da4ff26c8 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 18 Jun 2018 11:51:18 -0400 Subject: [PATCH 0515/1971] Merge pull request #2379 from kchadha/enable-comments Enable comments. --- packages/scratch-gui/src/containers/blocks.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 5f1ec793e6..0363924214 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -445,7 +445,7 @@ Blocks.defaultOptions = { fieldShadow: 'rgba(255, 255, 255, 0.3)', dragShadowOpacity: 0.6 }, - comments: false, + comments: true, collapse: false, sounds: false }; From 16803ab00edff3381ba962126b93566e75c76114 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 18 Jun 2018 15:54:27 -0400 Subject: [PATCH 0516/1971] Merge pull request #2386 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20180618173030 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0fa0c80bbb..04e2e73316 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "scratch-blocks": "0.1.0-prerelease.1529329760", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180614161221", - "scratch-render": "0.1.0-prerelease.20180615131212", + "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180613184320", "scratch-vm": "0.1.0-prerelease.1529017807", From 9790292749bcea40deecc75d7e9cb6d3d96affe3 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 18 Jun 2018 15:54:45 -0400 Subject: [PATCH 0517/1971] Merge pull request #2382 from LLK/greenkeeper/scratch-audio-0.1.0-prerelease.20180618171838 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-audio to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 04e2e73316..df643f217f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -93,7 +93,7 @@ "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "0.1.0-prerelease.1528996828", + "scratch-audio": "0.1.0-prerelease.20180618171838", "scratch-blocks": "0.1.0-prerelease.1529329760", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180614161221", From 2ba149780914399375d5c88702f099813e949b87 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 19 Jun 2018 08:46:35 -0400 Subject: [PATCH 0518/1971] Merge pull request #2383 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1529342508 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index df643f217f..f6c2771f04 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180618171838", - "scratch-blocks": "0.1.0-prerelease.1529329760", + "scratch-blocks": "0.1.0-prerelease.1529342508", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180614161221", "scratch-render": "0.1.0-prerelease.20180618173030", From 0d61b5470d18f16e4de79bc0c1570c772b7095db Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 19 Jun 2018 10:55:48 -0400 Subject: [PATCH 0519/1971] Merge pull request #2385 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20180618172917 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f6c2771f04..5fa27543f0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -99,7 +99,7 @@ "scratch-paint": "0.2.0-prerelease.20180614161221", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", - "scratch-svg-renderer": "0.2.0-prerelease.20180613184320", + "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", "scratch-vm": "0.1.0-prerelease.1529017807", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From e726903e43b5a4c9d47464809a34a0f8fed07e34 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 19 Jun 2018 10:56:42 -0400 Subject: [PATCH 0520/1971] Merge pull request #2384 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180618172346 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5fa27543f0..5b64ffb4be 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "scratch-audio": "0.1.0-prerelease.20180618171838", "scratch-blocks": "0.1.0-prerelease.1529342508", "scratch-l10n": "3.0.20180611175036", - "scratch-paint": "0.2.0-prerelease.20180614161221", + "scratch-paint": "0.2.0-prerelease.20180618172346", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", From 8837b2137cf88d9bd8a18450c84109c18003a2d7 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 19 Jun 2018 12:09:45 -0400 Subject: [PATCH 0521/1971] Merge pull request #2397 from paulkaplan/update-vm-june-19 Update scratch vm to include sharing APIs --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5b64ffb4be..c598483d7c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -100,7 +100,7 @@ "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", - "scratch-vm": "0.1.0-prerelease.1529017807", + "scratch-vm": "0.1.0-prerelease.1529422910", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From dfe3aba766193c4c45e00fbefce7d9b33d223207 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 19 Jun 2018 14:29:52 -0400 Subject: [PATCH 0522/1971] Merge pull request #2400 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180619182645 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c598483d7c..be7a89e1a0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "scratch-audio": "0.1.0-prerelease.20180618171838", "scratch-blocks": "0.1.0-prerelease.1529342508", "scratch-l10n": "3.0.20180611175036", - "scratch-paint": "0.2.0-prerelease.20180618172346", + "scratch-paint": "0.2.0-prerelease.20180619182645", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", From f582d148cf5d32b82e1a85f39de68346f03bf707 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 21 Jun 2018 15:10:57 -0400 Subject: [PATCH 0523/1971] Merge pull request #2417 from chrisgarrity/feature/localize-categories Localize category names in default toolbox --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index be7a89e1a0..c51758645a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180618171838", - "scratch-blocks": "0.1.0-prerelease.1529342508", + "scratch-blocks": "0.1.0-prerelease.1529499405", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180619182645", "scratch-render": "0.1.0-prerelease.20180618173030", From 793186b50367b97f487b2e5f6e7a9820bf53401d Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 21 Jun 2018 18:24:27 -0400 Subject: [PATCH 0524/1971] Merge pull request #2425 from thisandagain/bugfix/2264 Changes to language in "File" menu --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index e4de61ffde..164e9c78e7 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -226,7 +226,7 @@ const MenuBar = props => ( {...loadProps} > @@ -239,8 +239,8 @@ const MenuBar = props => ( {...saveProps} > From fbe6186d70079412dac7501d65ec1901db5e011e Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 21 Jun 2018 22:15:02 -0400 Subject: [PATCH 0525/1971] Merge pull request #2405 from chrisgarrity/feature/switch-languages Initial version of loading or switching language. --- packages/scratch-gui/package.json | 3 +-- .../scratch-gui/src/containers/blocks.jsx | 21 ++++++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c51758645a..c41835048f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -79,7 +79,6 @@ "react-draggable": "3.0.5", "react-ga": "2.5.3", "react-intl": "2.4.0", - "react-intl-redux": "0.7.0", "react-modal": "3.4.4", "react-popover": "0.5.7", "react-redux": "5.0.7", @@ -94,7 +93,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180618171838", - "scratch-blocks": "0.1.0-prerelease.1529499405", + "scratch-blocks": "0.1.0-prerelease.1529616842", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180619182645", "scratch-render": "0.1.0-prerelease.20180618173030", diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 0363924214..1b411d6336 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -52,7 +52,8 @@ class Blocks extends React.Component { 'onVisualReport', 'onWorkspaceUpdate', 'onWorkspaceMetricsChange', - 'setBlocks' + 'setBlocks', + 'setLocale' ]); this.ScratchBlocks.prompt = this.handlePromptStart; this.state = { @@ -86,7 +87,7 @@ class Blocks extends React.Component { addFunctionListener(this.workspace, 'zoom', this.onWorkspaceMetricsChange); this.attachVM(); - this.props.vm.setLocale(this.props.locale, this.props.messages); + this.setLocale(); analytics.pageview('/editors/blocks'); } @@ -109,7 +110,7 @@ class Blocks extends React.Component { } if (prevProps.locale !== this.props.locale) { - this.props.vm.setLocale(this.props.locale, this.props.messages); + this.setLocale(); } if (prevProps.toolboxXML !== this.props.toolboxXML) { @@ -144,6 +145,16 @@ class Blocks extends React.Component { clearTimeout(this.toolboxUpdateTimeout); } + setLocale () { + this.workspace.getFlyout().setRecyclingEnabled(false); + this.ScratchBlocks.ScratchMsgs.setLocale(this.props.locale); + this.props.vm.setLocale(this.props.locale, this.props.messages); + + this.workspace.updateToolbox(this.props.toolboxXML); + this.props.vm.refreshWorkspace(); + this.workspace.getFlyout().setRecyclingEnabled(true); + } + updateToolbox () { this.toolboxUpdateTimeout = false; @@ -461,8 +472,8 @@ const mapStateToProps = state => ({ state.scratchGui.mode.isFullScreen ), extensionLibraryVisible: state.scratchGui.modals.extensionLibrary, - locale: state.intl.locale, - messages: state.intl.messages, + locale: state.locales.locale, + messages: state.locales.messages, toolboxXML: state.scratchGui.toolbox.toolboxXML, customProceduresVisible: state.scratchGui.customProcedures.active }); From 502f1741321aa1e803a92ee7e1def89a0ac839d7 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 22 Jun 2018 07:26:13 -0400 Subject: [PATCH 0526/1971] Merge pull request #2434 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180621213404 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c41835048f..8a4d41e094 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -95,7 +95,7 @@ "scratch-audio": "0.1.0-prerelease.20180618171838", "scratch-blocks": "0.1.0-prerelease.1529616842", "scratch-l10n": "3.0.20180611175036", - "scratch-paint": "0.2.0-prerelease.20180619182645", + "scratch-paint": "0.2.0-prerelease.20180621213404", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", From 2a6bd39383e04d701f77913b1fb433875b767dc3 Mon Sep 17 00:00:00 2001 From: Mx Corey Frang Date: Fri, 22 Jun 2018 10:45:32 -0400 Subject: [PATCH 0527/1971] Merge pull request #2439 from ericrosenbaum/feature/update-vm-and-audio Update scratch-audio and scratch-vm --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8a4d41e094..2e1c8b3c3d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,14 +92,14 @@ "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "0.1.0-prerelease.20180618171838", + "scratch-audio": "0.1.0-prerelease.20180621210133", "scratch-blocks": "0.1.0-prerelease.1529616842", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180621213404", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", - "scratch-vm": "0.1.0-prerelease.1529422910", + "scratch-vm": "0.1.0-prerelease.1529676447", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From c9ee8c6e37f18c4ec8071552bed8dc006825b169 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Fri, 22 Jun 2018 14:00:23 -0400 Subject: [PATCH 0528/1971] Merge pull request #2438 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1529676337 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 Updates the scratch_msgs to support many locales. --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2e1c8b3c3d..c4a085caf7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -93,7 +93,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180621210133", - "scratch-blocks": "0.1.0-prerelease.1529616842", + "scratch-blocks": "0.1.0-prerelease.1529676337", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180621213404", "scratch-render": "0.1.0-prerelease.20180618173030", From 5b28f253e9c91089853890cfeb946a76909d6f7a Mon Sep 17 00:00:00 2001 From: kchadha Date: Mon, 25 Jun 2018 11:47:38 -0400 Subject: [PATCH 0529/1971] Merge pull request #2416 from kchadha/sprite-save-load Sprite3 Save/Load --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c4a085caf7..eae20fa2ea 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -99,7 +99,7 @@ "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", - "scratch-vm": "0.1.0-prerelease.1529676447", + "scratch-vm": "0.1.0-prerelease.1529940524", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From a07cf1ac320cad8920b541773fb189d743b2ab99 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Mon, 25 Jun 2018 14:55:00 -0400 Subject: [PATCH 0530/1971] Merge pull request #2459 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1529946580 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index eae20fa2ea..e927affde6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -93,7 +93,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180621210133", - "scratch-blocks": "0.1.0-prerelease.1529676337", + "scratch-blocks": "0.1.0-prerelease.1529946580", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180621213404", "scratch-render": "0.1.0-prerelease.20180618173030", From 0fee609b44a86a44a239c61ab58899b3240b8eb6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 26 Jun 2018 11:21:41 -0400 Subject: [PATCH 0531/1971] Merge pull request #2465 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1530023760 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e927affde6..5d8ebf8df3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -93,7 +93,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180621210133", - "scratch-blocks": "0.1.0-prerelease.1529946580", + "scratch-blocks": "0.1.0-prerelease.1530023760", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180621213404", "scratch-render": "0.1.0-prerelease.20180618173030", From 4e773b67bb620800d9d5ba5e01ce8183f171df18 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 26 Jun 2018 11:24:17 -0400 Subject: [PATCH 0532/1971] Merge pull request #2469 from LLK/greenkeeper/scratch-l10n-3.0.20180621153937 chore(package): update scratch-l10n to version 3.0.20180621153937 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5d8ebf8df3..f8116de09e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180621210133", "scratch-blocks": "0.1.0-prerelease.1530023760", - "scratch-l10n": "3.0.20180611175036", + "scratch-l10n": "3.0.20180621153937", "scratch-paint": "0.2.0-prerelease.20180621213404", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", From 030aec6dc51c88a629760c1ad4361e64844af671 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 26 Jun 2018 11:25:29 -0400 Subject: [PATCH 0533/1971] Merge pull request #2451 from LLK/greenkeeper/react-virtualized-9.20.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-virtualized to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f8116de09e..767e6505f9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -87,7 +87,7 @@ "react-tabs": "2.2.2", "react-test-renderer": "16.2.0", "react-tooltip": "3.6.1", - "react-virtualized": "9.19.1", + "react-virtualized": "9.20.0", "redux": "3.7.2", "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", From 9ae7c84fb35bd17757c69a95aabb73584637321c Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 26 Jun 2018 16:30:52 -0400 Subject: [PATCH 0534/1971] Merge pull request #2464 from rschamp/save-canvas Hack to make the renderer own the stage canvas --- packages/scratch-gui/src/containers/stage.jsx | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 0013307f8d..01fc3ec7c7 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -37,7 +37,6 @@ class Stage extends React.Component { 'onWheel', 'updateRect', 'questionListener', - 'setCanvas', 'setDragCanvas', 'clearDragCanvas', 'drawDragCanvas', @@ -52,16 +51,22 @@ class Stage extends React.Component { colorInfo: null, question: null }; + if (this.props.vm.runtime.renderer) { + this.renderer = this.props.vm.runtime.renderer; + this.canvas = this.props.vm.runtime.renderer._gl.canvas; + } else { + this.canvas = document.createElement('canvas'); + this.renderer = new Renderer(this.canvas); + this.props.vm.attachRenderer(this.renderer); + } + this.props.vm.attachV2SVGAdapter(new V2SVGAdapter()); + this.props.vm.setVideoProvider(new VideoProvider()); } componentDidMount () { this.attachRectEvents(); this.attachMouseEvents(this.canvas); this.updateRect(); - this.renderer = new Renderer(this.canvas); - this.props.vm.attachRenderer(this.renderer); - this.props.vm.attachV2SVGAdapter(new V2SVGAdapter()); this.props.vm.runtime.addListener('QUESTION', this.questionListener); - this.props.vm.setVideoProvider(new VideoProvider()); } shouldComponentUpdate (nextProps, nextState) { return this.props.stageSize !== nextProps.stageSize || @@ -353,9 +358,6 @@ class Stage extends React.Component { commonStopDragActions(); } } - setCanvas (canvas) { - this.canvas = canvas; - } setDragCanvas (canvas) { this.dragCanvas = canvas; } @@ -367,7 +369,7 @@ class Stage extends React.Component { } = this.props; return ( Date: Tue, 26 Jun 2018 16:42:42 -0400 Subject: [PATCH 0535/1971] Merge pull request #2488 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180626202149 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 767e6505f9..ab6ddbe49a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -95,7 +95,7 @@ "scratch-audio": "0.1.0-prerelease.20180621210133", "scratch-blocks": "0.1.0-prerelease.1530023760", "scratch-l10n": "3.0.20180621153937", - "scratch-paint": "0.2.0-prerelease.20180621213404", + "scratch-paint": "0.2.0-prerelease.20180626202149", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", From ad8668932db2d70f8c9e43671712dac8397bf858 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 27 Jun 2018 10:24:40 -0400 Subject: [PATCH 0536/1971] Merge pull request #2497 from LLK/greenkeeper/scratch-l10n-3.0.20180627134459 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ab6ddbe49a..72431cd174 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,7 +94,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180621210133", "scratch-blocks": "0.1.0-prerelease.1530023760", - "scratch-l10n": "3.0.20180621153937", + "scratch-l10n": "3.0.20180627134459", "scratch-paint": "0.2.0-prerelease.20180626202149", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", From 339b05408a94092c650a38811378a1458ec4c29b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 27 Jun 2018 10:46:11 -0400 Subject: [PATCH 0537/1971] Merge pull request #2495 from LLK/greenkeeper/scratch-audio-0.1.0-prerelease.20180625202813 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-audio to version 0.1.0-prerelease.2018… --- packages/scratch-gui/package.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 72431cd174..6e29635aeb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -92,7 +92,11 @@ "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "0.1.0-prerelease.20180621210133", + "scratch-audio": "0.1.0-prerelease.20180625202813", + "scratch-blocks": "0.1.0-prerelease.1529946580", + "scratch-l10n": "3.0.20180611175036", + "scratch-paint": "0.2.0-prerelease.20180621213404", + "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1530023760", "scratch-l10n": "3.0.20180627134459", "scratch-paint": "0.2.0-prerelease.20180626202149", From 5d1b29f5ed0e26ff121ff73544604d57dd50d7d9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 28 Jun 2018 08:44:11 -0400 Subject: [PATCH 0538/1971] Merge pull request #2407 from paulkaplan/backpack-step-5 Backpack step 5: saving costumes and sounds --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6e29635aeb..ff5ec086d9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -39,6 +39,7 @@ "babel-plugin-transform-object-rest-spread": "^6.22.0", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.22.0", + "base64-loader": "1.0.0", "bowser": "1.9.3", "chromedriver": "2.40.0", "classnames": "2.2.6", From 7352b2c318e8174e52da34e776e50c113f49790f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 28 Jun 2018 11:11:32 -0400 Subject: [PATCH 0539/1971] Merge pull request #2508 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1530135682 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ff5ec086d9..aeaa493f10 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -94,11 +94,11 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1529946580", + "scratch-blocks": "0.1.0-prerelease.1530135682", "scratch-l10n": "3.0.20180611175036", "scratch-paint": "0.2.0-prerelease.20180621213404", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1530023760", + "scratch-blocks": "0.1.0-prerelease.1530135682", "scratch-l10n": "3.0.20180627134459", "scratch-paint": "0.2.0-prerelease.20180626202149", "scratch-render": "0.1.0-prerelease.20180618173030", From 4b8129ea0cb0312efe3c6c6c26bbaff70ea3450d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 28 Jun 2018 11:49:04 -0400 Subject: [PATCH 0540/1971] Merge pull request #2511 from paulkaplan/fix-stage-styling Correctly apply react style object to the dom element renderer --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index aeaa493f10..ac6d497f81 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,6 +110,7 @@ "style-loader": "^0.21.0", "svg-to-image": "1.1.3", "text-encoding": "0.6.4", + "to-style": "1.3.3", "uglifyjs-webpack-plugin": "^1.2.5", "wav-encoder": "1.3.0", "web-audio-test-api": "^0.5.2", From c073d5f435b0ef4d0e0fc5efef2f04d77177188e Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 28 Jun 2018 13:39:06 -0400 Subject: [PATCH 0541/1971] Merge pull request #2506 from chrisgarrity/issue/2500-localize-extension-lib Use react-intl to translate extensions library --- .../src/lib/libraries/extensions/index.jsx | 167 ++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 packages/scratch-gui/src/lib/libraries/extensions/index.jsx diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx new file mode 100644 index 0000000000..71404f8fb0 --- /dev/null +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -0,0 +1,167 @@ +import React from 'react'; +import {FormattedMessage} from 'react-intl'; + +import musicImage from './music.png'; +import penImage from './pen.png'; +import videoImage from './video-sensing.png'; +import speechImage from './speech.png'; +import microbitImage from './microbit.png'; +import wedoImage from './wedo.png'; +import ev3Image from './ev3.png'; +import boostImage from './boost.png'; +import translateImage from './translate.png'; + +export default [ + { + name: ( + + ), + extensionId: 'music', + iconURL: musicImage, + description: ( + + ), + featured: true + }, + { + name: ( + + ), + extensionId: 'pen', + iconURL: penImage, + description: ( + + ), + featured: true + }, + { + name: ( + + ), + extensionId: 'translate', + iconURL: translateImage, + description: ( + + ), + featured: true + }, + { + name: ( + + ), + extensionId: 'videoSensing', + iconURL: videoImage, + description: ( + + ), + featured: true + }, + { + name: ( + + ), + extensionId: 'speech', + iconURL: speechImage, + description: ( + + ), + featured: true, + disabled: true + }, + { + name: 'Micro:bit', + extensionId: 'microbit', + iconURL: microbitImage, + description: ( + + ), + featured: true, + disabled: true + }, + { + name: 'LEGO WeDo 2.0', + extensionId: 'wedo2', + iconURL: wedoImage, + description: ( + + ), + featured: true, + disabled: true + }, + { + name: 'LEGO MINDSTORMS EV3', + extensionId: 'ev3', + iconURL: ev3Image, + description: ( + + ), + featured: true, + disabled: true + }, + { + name: 'LEGO Boost', + extensionId: 'boost', + iconURL: boostImage, + description: ( + + ), + featured: true, + disabled: true + } +]; From a91f6da1d0aabf83ec27ef03560b612c323e5d17 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 28 Jun 2018 14:32:57 -0400 Subject: [PATCH 0542/1971] Merge pull request #2513 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180628170948 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ac6d497f81..a948d0b7c5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,11 +96,11 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1530135682", "scratch-l10n": "3.0.20180611175036", - "scratch-paint": "0.2.0-prerelease.20180621213404", + "scratch-paint": "0.2.0-prerelease.20180628170948", "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1530135682", "scratch-l10n": "3.0.20180627134459", - "scratch-paint": "0.2.0-prerelease.20180626202149", + "scratch-paint": "0.2.0-prerelease.20180628170948", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", From 00f454e3c790e0108910611403925bd6697082d8 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 28 Jun 2018 15:57:35 -0400 Subject: [PATCH 0543/1971] Merge pull request #2468 from chrisgarrity/feature/update-translations ignore paint editor messages in gui --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a948d0b7c5..9df7c00fed 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -8,7 +8,7 @@ "clean": "rimraf ./build && mkdirp build && rimraf ./dist && mkdirp dist", "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", "prune": "./prune-gh-pages.sh", - "i18n:src": "babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/ ./translations/", + "i18n:src": "babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/src ./translations/", "start": "webpack-dev-server", "test": "npm run test:lint && npm run test:unit && npm run build && npm run test:integration", "test:integration": "jest --runInBand test[\\\\/]integration", From a767d58340976bfd13ac43314130581f9bf308f4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 28 Jun 2018 16:32:52 -0400 Subject: [PATCH 0544/1971] Merge pull request #2516 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180628183906 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9df7c00fed..4a44f1ec0c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,11 +96,11 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1530135682", "scratch-l10n": "3.0.20180611175036", - "scratch-paint": "0.2.0-prerelease.20180628170948", + "scratch-paint": "0.2.0-prerelease.20180628183906", "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1530135682", "scratch-l10n": "3.0.20180627134459", - "scratch-paint": "0.2.0-prerelease.20180628170948", + "scratch-paint": "0.2.0-prerelease.20180628183906", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", From da1c5d097c80d18bf90c90041b356739218ca40e Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 28 Jun 2018 16:56:15 -0400 Subject: [PATCH 0545/1971] Merge pull request #2517 from LLK/greenkeeper/eslint-5.0.1 chore(package): update eslint to version 5.0.1 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4a44f1ec0c..64886d145c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -48,7 +48,7 @@ "enzyme": "^3.1.0", "enzyme-adapter-react-16": "1.1.1", "es6-object-assign": "1.1.0", - "eslint": "^4.7.1", + "eslint": "^5.0.1", "eslint-config-scratch": "^5.0.0", "eslint-plugin-import": "^2.8.0", "eslint-plugin-react": "^7.5.1", From e34bcbb62fa2625fee4146efc4326215b6bf1097 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 29 Jun 2018 08:06:11 -0400 Subject: [PATCH 0546/1971] Merge pull request #2521 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180629115559 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 64886d145c..398da8af5f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,11 +96,11 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1530135682", "scratch-l10n": "3.0.20180611175036", - "scratch-paint": "0.2.0-prerelease.20180628183906", + "scratch-paint": "0.2.0-prerelease.20180629115559", "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1530135682", "scratch-l10n": "3.0.20180627134459", - "scratch-paint": "0.2.0-prerelease.20180628183906", + "scratch-paint": "0.2.0-prerelease.20180629115559", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", From 0fbcb6fe4a1c036e768d79749548447b7f7da582 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 29 Jun 2018 13:11:59 -0400 Subject: [PATCH 0547/1971] Merge pull request #2527 from LLK/smoke Shim array#includes and fix style assignment. Fixes IE11 and Safari10 --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 398da8af5f..24b2fabe53 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -44,6 +44,7 @@ "chromedriver": "2.40.0", "classnames": "2.2.6", "copy-webpack-plugin": "^4.5.1", + "core-js": "2.5.7", "css-loader": "^0.28.11", "enzyme": "^3.1.0", "enzyme-adapter-react-16": "1.1.1", From b74f17f85eece7544a9341d6cc0e8ee6da82d89b Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Tue, 3 Jul 2018 14:57:39 -0400 Subject: [PATCH 0548/1971] Merge pull request #2547 from LLK/greenkeeper/scratch-l10n-3.0.20180703181510 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 24b2fabe53..7d04e77f05 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,11 +96,11 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1530135682", - "scratch-l10n": "3.0.20180611175036", + "scratch-l10n": "3.0.20180703181510", "scratch-paint": "0.2.0-prerelease.20180629115559", "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1530135682", - "scratch-l10n": "3.0.20180627134459", + "scratch-l10n": "3.0.20180703181510", "scratch-paint": "0.2.0-prerelease.20180629115559", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", From 385b10c36c06742b983747ac579788b50e9eb43b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 5 Jul 2018 10:55:29 -0400 Subject: [PATCH 0549/1971] Merge pull request #2551 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180705130133 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7d04e77f05..1811a63589 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,11 +97,11 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1530135682", "scratch-l10n": "3.0.20180703181510", - "scratch-paint": "0.2.0-prerelease.20180629115559", + "scratch-paint": "0.2.0-prerelease.20180705130133", "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1530135682", "scratch-l10n": "3.0.20180703181510", - "scratch-paint": "0.2.0-prerelease.20180629115559", + "scratch-paint": "0.2.0-prerelease.20180705130133", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", From a0096aecb1d3d3bf3b1f1d223a2b8772e6f0bbb2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 6 Jul 2018 12:58:13 -0400 Subject: [PATCH 0550/1971] Merge pull request #2556 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180706154930 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1811a63589..dc305fa4a3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,11 +97,11 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1530135682", "scratch-l10n": "3.0.20180703181510", - "scratch-paint": "0.2.0-prerelease.20180705130133", + "scratch-paint": "0.2.0-prerelease.20180706154930", "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1530135682", "scratch-l10n": "3.0.20180703181510", - "scratch-paint": "0.2.0-prerelease.20180705130133", + "scratch-paint": "0.2.0-prerelease.20180706154930", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", From 04c8a0e73cae0d6f2320e5479d4f2947a47e304a Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 6 Jul 2018 15:08:05 -0400 Subject: [PATCH 0551/1971] Merge pull request #2560 from rschamp/dedupe-dev-dependencies Remove duplicate scratch-* dependencies --- packages/scratch-gui/package.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index dc305fa4a3..01184d2187 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,10 +98,6 @@ "scratch-blocks": "0.1.0-prerelease.1530135682", "scratch-l10n": "3.0.20180703181510", "scratch-paint": "0.2.0-prerelease.20180706154930", - "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1530135682", - "scratch-l10n": "3.0.20180703181510", - "scratch-paint": "0.2.0-prerelease.20180706154930", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", From a6abd78ce541e0609db8a4453c49603a6a3b5d7c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 9 Jul 2018 09:43:52 -0400 Subject: [PATCH 0552/1971] Merge pull request #2562 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180709132225 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 01184d2187..62c20f6d33 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1530135682", "scratch-l10n": "3.0.20180703181510", - "scratch-paint": "0.2.0-prerelease.20180706154930", + "scratch-paint": "0.2.0-prerelease.20180709132225", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", From 46338b42b215e69df52e8d42d25f85625f4a7813 Mon Sep 17 00:00:00 2001 From: kchadha Date: Mon, 9 Jul 2018 10:21:24 -0400 Subject: [PATCH 0553/1971] Merge pull request #2563 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1531144787 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.153… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 62c20f6d33..1f10cbe024 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -95,7 +95,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1530135682", + "scratch-blocks": "0.1.0-prerelease.1531144787", "scratch-l10n": "3.0.20180703181510", "scratch-paint": "0.2.0-prerelease.20180709132225", "scratch-render": "0.1.0-prerelease.20180618173030", From d1f00fc81ca7c9af256ebea407e285075b4baee8 Mon Sep 17 00:00:00 2001 From: kchadha Date: Mon, 9 Jul 2018 13:40:14 -0400 Subject: [PATCH 0554/1971] Merge pull request #2548 from kchadha/variable-modal-options Local Variables --- packages/scratch-gui/package.json | 2 +- packages/scratch-gui/src/containers/blocks.jsx | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1f10cbe024..a3ff0b80b1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -101,7 +101,7 @@ "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", - "scratch-vm": "0.1.0-prerelease.1529940524", + "scratch-vm": "0.1.0-prerelease.1530902855", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 1b411d6336..40ad9234ed 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -321,13 +321,19 @@ class Blocks extends React.Component { handlePromptStart (message, defaultValue, callback, optTitle, optVarType) { const p = {prompt: {callback, message, defaultValue}}; p.prompt.title = optTitle ? optTitle : - this.ScratchBlocks.VARIABLE_MODAL_TITLE; + this.ScratchBlocks.Msg.VARIABLE_MODAL_TITLE; + p.prompt.varType = typeof optVarType === 'string' ? + optVarType : this.ScratchBlocks.SCALAR_VARIABLE_TYPE; p.prompt.showMoreOptions = - optVarType !== this.ScratchBlocks.BROADCAST_MESSAGE_VARIABLE_TYPE; + optVarType !== this.ScratchBlocks.BROADCAST_MESSAGE_VARIABLE_TYPE && + p.prompt.title !== this.ScratchBlocks.Msg.RENAME_VARIABLE_MODAL_TITLE && + p.prompt.title !== this.ScratchBlocks.Msg.RENAME_LIST_MODAL_TITLE; this.setState(p); } - handlePromptCallback (data) { - this.state.prompt.callback(data); + handlePromptCallback (input, optionSelection) { + this.state.prompt.callback(input, optionSelection, + (optionSelection === 'local') ? [] : + this.props.vm.runtime.getAllVarNamesOfType(this.state.prompt.varType)); this.handlePromptClose(); } handlePromptClose () { @@ -366,6 +372,7 @@ class Blocks extends React.Component { /> {this.state.prompt ? ( Date: Wed, 11 Jul 2018 09:25:39 -0400 Subject: [PATCH 0555/1971] Merge pull request #2537 from LLK/feature/device-connection-modals Initial version of device connection modals --- .../scratch-gui/src/containers/blocks.jsx | 43 ++++++++++++++++++- .../src/lib/libraries/extensions/index.jsx | 16 ++++++- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 40ad9234ed..20c6d482ff 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -9,8 +9,10 @@ import VM from 'scratch-vm'; import analytics from '../lib/analytics'; import Prompt from './prompt.jsx'; +import ConnectionModal from './connection-modal.jsx'; import BlocksComponent from '../components/blocks/blocks.jsx'; import ExtensionLibrary from './extension-library.jsx'; +import extensionData from '../lib/libraries/extensions/index.jsx'; import CustomProcedures from './custom-procedures.jsx'; import errorBoundaryHOC from '../lib/error-boundary-hoc.jsx'; import {STAGE_DISPLAY_SIZES} from '../lib/layout-constants'; @@ -38,6 +40,9 @@ class Blocks extends React.Component { 'attachVM', 'detachVM', 'handleCategorySelected', + 'handleConnectionModalStart', + 'handleConnectionModalClose', + 'handleStatusButtonUpdate', 'handlePromptStart', 'handlePromptCallback', 'handlePromptClose', @@ -56,9 +61,11 @@ class Blocks extends React.Component { 'setLocale' ]); this.ScratchBlocks.prompt = this.handlePromptStart; + this.ScratchBlocks.statusButtonCallback = this.handleConnectionModalStart; this.state = { workspaceMetrics: {}, - prompt: null + prompt: null, + connectionModal: null }; this.onTargetsUpdate = debounce(this.onTargetsUpdate, 100); this.toolboxUpdateQueue = []; @@ -94,6 +101,7 @@ class Blocks extends React.Component { shouldComponentUpdate (nextProps, nextState) { return ( this.state.prompt !== nextState.prompt || + this.state.connectionModal !== nextState.connectionModal || this.props.isVisible !== nextProps.isVisible || this.props.toolboxXML !== nextProps.toolboxXML || this.props.extensionLibraryVisible !== nextProps.extensionLibraryVisible || @@ -311,6 +319,11 @@ class Blocks extends React.Component { this.handleExtensionAdded(blocksInfo); } handleCategorySelected (categoryId) { + const extension = extensionData.find(ext => ext.extensionId === categoryId); + if (extension && extension.launchDeviceConnectionFlow) { + this.handleConnectionModalStart(categoryId); + } + this.withToolboxUpdates(() => { this.workspace.toolbox_.setSelectedCategoryById(categoryId); }); @@ -330,6 +343,23 @@ class Blocks extends React.Component { p.prompt.title !== this.ScratchBlocks.Msg.RENAME_LIST_MODAL_TITLE; this.setState(p); } + handleConnectionModalStart (extensionId) { + const extension = extensionData.find(ext => ext.extensionId === extensionId); + if (extension) { + this.setState({connectionModal: { + extensionId: extensionId, + deviceImage: extension.deviceImage, + smallDeviceImage: extension.smallDeviceImage, + name: extension.name + }}); + } + } + handleConnectionModalClose () { + this.setState({connectionModal: null}); + } + handleStatusButtonUpdate (extensionId, status) { + this.ScratchBlocks.updateStatusButton(this.workspace, extensionId, status); + } handlePromptCallback (input, optionSelection) { this.state.prompt.callback(input, optionSelection, (optionSelection === 'local') ? [] : @@ -381,6 +411,17 @@ class Blocks extends React.Component { onOk={this.handlePromptCallback} /> ) : null} + {this.state.connectionModal ? ( + + ) : null} {extensionLibraryVisible ? ( ), featured: true, - disabled: true + disabled: true, + launchDeviceConnectionFlow: true, + deviceImage: microbitDeviceImage, + smallDeviceImage: microbitMenuImage }, { name: 'LEGO WeDo 2.0', @@ -148,7 +157,10 @@ export default [ /> ), featured: true, - disabled: true + disabled: true, + launchDeviceConnectionFlow: true, + deviceImage: ev3DeviceImage, + smallDeviceImage: ev3MenuImage }, { name: 'LEGO Boost', From 7e9aec00fdf4b4747ce84ede05a319f2bc1daec2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 11 Jul 2018 12:59:45 -0400 Subject: [PATCH 0556/1971] Merge pull request #2585 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180711151847 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a3ff0b80b1..246d5ddb21 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1531144787", "scratch-l10n": "3.0.20180703181510", - "scratch-paint": "0.2.0-prerelease.20180709132225", + "scratch-paint": "0.2.0-prerelease.20180711151847", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", From 27b7239764946e6bf856646f131ba5e559742994 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 11 Jul 2018 16:40:50 -0400 Subject: [PATCH 0557/1971] Merge pull request #2579 from paulkaplan/fix-device-status Fix device status indicator updating issues --- packages/scratch-gui/package.json | 2 +- packages/scratch-gui/src/containers/blocks.jsx | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 246d5ddb21..6959e770d6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -95,7 +95,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1531144787", + "scratch-blocks": "0.1.0-prerelease.1531338038", "scratch-l10n": "3.0.20180703181510", "scratch-paint": "0.2.0-prerelease.20180711151847", "scratch-render": "0.1.0-prerelease.20180618173030", diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 20c6d482ff..4c84983757 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -212,6 +212,8 @@ class Blocks extends React.Component { this.props.vm.addListener('targetsUpdate', this.onTargetsUpdate); this.props.vm.addListener('EXTENSION_ADDED', this.handleExtensionAdded); this.props.vm.addListener('BLOCKSINFO_UPDATE', this.handleBlocksInfoUpdate); + this.props.vm.addListener('PERIPHERAL_CONNECTED', this.handleStatusButtonUpdate); + this.props.vm.addListener('PERIPHERAL_ERROR', this.handleStatusButtonUpdate); } detachVM () { this.props.vm.removeListener('SCRIPT_GLOW_ON', this.onScriptGlowOn); @@ -223,6 +225,8 @@ class Blocks extends React.Component { this.props.vm.removeListener('targetsUpdate', this.onTargetsUpdate); this.props.vm.removeListener('EXTENSION_ADDED', this.handleExtensionAdded); this.props.vm.removeListener('BLOCKSINFO_UPDATE', this.handleBlocksInfoUpdate); + this.props.vm.removeListener('PERIPHERAL_CONNECTED', this.handleStatusButtonUpdate); + this.props.vm.removeListener('PERIPHERAL_ERROR', this.handleStatusButtonUpdate); } updateToolboxBlockValue (id, value) { @@ -357,8 +361,8 @@ class Blocks extends React.Component { handleConnectionModalClose () { this.setState({connectionModal: null}); } - handleStatusButtonUpdate (extensionId, status) { - this.ScratchBlocks.updateStatusButton(this.workspace, extensionId, status); + handleStatusButtonUpdate () { + this.ScratchBlocks.refreshStatusButtons(this.workspace); } handlePromptCallback (input, optionSelection) { this.state.prompt.callback(input, optionSelection, From 7a60bee411f25e821f7ea4c9258e6adf4a666a10 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 11 Jul 2018 16:41:45 -0400 Subject: [PATCH 0558/1971] Merge pull request #2587 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180711181626 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6959e770d6..faa1675d07 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1531338038", "scratch-l10n": "3.0.20180703181510", - "scratch-paint": "0.2.0-prerelease.20180711151847", + "scratch-paint": "0.2.0-prerelease.20180711181626", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", From e53dd6d914f0dd761b2c99cb1781c7167ce132a3 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 11 Jul 2018 16:44:53 -0400 Subject: [PATCH 0559/1971] Merge pull request #2591 from paulkaplan/use-bigger-icons Use a larger icon image for the device connection flow --- packages/scratch-gui/src/lib/libraries/extensions/index.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index 71b1f2fe9e..62da95ea5b 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -12,10 +12,10 @@ import boostImage from './boost.png'; import translateImage from './translate.png'; import ev3DeviceImage from './device-connection/ev3/ev3-hub-illustration.svg'; -import ev3MenuImage from './device-connection/ev3/ev3-menu-icon.svg'; +import ev3MenuImage from './device-connection/ev3/ev3-small.svg'; import microbitDeviceImage from './device-connection/microbit/microbit-illustration.svg'; -import microbitMenuImage from './device-connection/microbit/microbit-menu-icon.svg'; +import microbitMenuImage from './device-connection/microbit/microbit-small.svg'; export default [ { From d63237970ef4f8e56ec1ecead094cae7d8de9322 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 12 Jul 2018 08:10:40 -0400 Subject: [PATCH 0560/1971] Merge pull request #2593 from paulkaplan/update-scratch-vm-july-11 Update to newest scratch-vm --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index faa1675d07..3763149a2e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -101,7 +101,7 @@ "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", - "scratch-vm": "0.1.0-prerelease.1530902855", + "scratch-vm": "0.1.0-prerelease.1531340421", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From 361a3a7892bae43791bf59c9e96a903daaf418c6 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 12 Jul 2018 08:45:59 -0400 Subject: [PATCH 0561/1971] Merge pull request #2588 from chrisgarrity/issue/2538-firefox-select Handle mouseUp on language select for firefox --- .../src/components/menu-bar/menu-bar.jsx | 551 +++++++++--------- 1 file changed, 283 insertions(+), 268 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 164e9c78e7..f6399a9ac4 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -2,6 +2,7 @@ import classNames from 'classnames'; import {connect} from 'react-redux'; import {defineMessages, FormattedMessage, injectIntl, intlShape} from 'react-intl'; import PropTypes from 'prop-types'; +import bindAll from 'lodash.bindall'; import React from 'react'; import Box from '../box/box.jsx'; @@ -128,297 +129,311 @@ MenuBarMenu.propTypes = { open: PropTypes.bool, place: PropTypes.oneOf(['left', 'right']) }; - -const MenuBar = props => ( - -
-
-
- Scratch -
-
- -
- +class MenuBar extends React.Component { + constructor (props) { + super(props); + bindAll(this, [ + 'handleLanguageMouseUp' + ]); + } + handleLanguageMouseUp (e) { + if (!this.props.languageMenuOpen) { + this.props.onClickLanguage(e); + } + } + render () { + return ( + +
+
+
Scratch
- - - - - -
-
-
- -
- - - - - - - - - - - - - - - - - - - {(renderFileInput, loadProject, loadProps) => ( - +
- - {renderFileInput()} - - )} - {(saveProject, saveProps) => ( - - - - )} - - -
-
-
- +
+ + + + + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + {(renderFileInput, loadProject, loadProps) => ( + + + {renderFileInput()} + + )} + {(saveProject, saveProps) => ( + + + + )} + + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
- - - + +
+ + + +
+
+ + + +
+
+ {this.props.enableCommunity ? + : + + + + } +
-
- -
- - - -
-
- - - -
-
- {props.enableCommunity ? - : - - - } -
-
- -
-
- -
- -
- +
-
- -
- - - {'scratch-cat' /* @todo username */} - - +
+
+ +
+ +
+ +
+
+ +
+ + + {'scratch-cat' /* @todo username */} + + +
+
- -
- -); + + ); + } +} MenuBar.propTypes = { editMenuOpen: PropTypes.bool, From 77e37fe9f1650c2d35d9d2f7f77d9f935f17a8b6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 12 Jul 2018 11:10:37 -0400 Subject: [PATCH 0562/1971] Merge pull request #2598 from paulkaplan/update-how-to Change "how-to" messages to "tutorial" and update the icon to lightbulb --- .../src/components/menu-bar/menu-bar.jsx | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index f6399a9ac4..b843e05b45 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -31,6 +31,7 @@ import { import styles from './menu-bar.css'; +import helpIcon from '../../lib/assets/icon--tutorials.svg'; import mystuffIcon from './icon--mystuff.png'; import feedbackIcon from './icon--feedback.svg'; import profileIcon from './icon--profile.png'; @@ -40,18 +41,16 @@ import languageIcon from '../language-selector/language-icon.svg'; import scratchLogo from './scratch-logo.svg'; -import helpIcon from './icon--help.svg'; - const ariaMessages = defineMessages({ language: { id: 'gui.menuBar.LanguageSelector', defaultMessage: 'language selector', description: 'accessibility text for the language selection menu' }, - howTo: { - id: 'gui.menuBar.howToLibrary', - defaultMessage: 'How-to Library', - description: 'accessibility text for the how-to library button' + tutorials: { + id: 'gui.menuBar.tutorialsLibrary', + defaultMessage: 'Tutorials', + description: 'accessibility text for the tutorials button' } }); @@ -311,6 +310,18 @@ class MenuBar extends React.Component {
+
+ + +
+
-
- -
Date: Thu, 12 Jul 2018 11:40:21 -0400 Subject: [PATCH 0563/1971] Merge pull request #2592 from chrisgarrity/issue/2514-extension-lang-switch Issue/2514 extension lang switch --- packages/scratch-gui/src/containers/blocks.jsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 4c84983757..c81c5d21ad 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -156,11 +156,12 @@ class Blocks extends React.Component { setLocale () { this.workspace.getFlyout().setRecyclingEnabled(false); this.ScratchBlocks.ScratchMsgs.setLocale(this.props.locale); - this.props.vm.setLocale(this.props.locale, this.props.messages); - - this.workspace.updateToolbox(this.props.toolboxXML); - this.props.vm.refreshWorkspace(); - this.workspace.getFlyout().setRecyclingEnabled(true); + this.props.vm.setLocale(this.props.locale, this.props.messages) + .then(() => { + this.workspace.updateToolbox(this.props.toolboxXML); + this.props.vm.refreshWorkspace(); + this.workspace.getFlyout().setRecyclingEnabled(true); + }); } updateToolbox () { From 546181abc9b63a13fdfddb35a478d9fcc7f96077 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 12 Jul 2018 12:38:05 -0400 Subject: [PATCH 0564/1971] Merge pull request #2601 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180712144339 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3763149a2e..90dbffc67e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1531338038", "scratch-l10n": "3.0.20180703181510", - "scratch-paint": "0.2.0-prerelease.20180711181626", + "scratch-paint": "0.2.0-prerelease.20180712144339", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", From 6a9f2ad474f842c9d2c78b5f364e20cef491962d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 12 Jul 2018 14:30:25 -0400 Subject: [PATCH 0565/1971] Merge pull request #2603 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1531409796 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.153… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 90dbffc67e..4393537631 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -95,7 +95,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1531338038", + "scratch-blocks": "0.1.0-prerelease.1531409796", "scratch-l10n": "3.0.20180703181510", "scratch-paint": "0.2.0-prerelease.20180712144339", "scratch-render": "0.1.0-prerelease.20180618173030", From 4c2343085828b92fd10c671950210eebe8bdb110 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 12 Jul 2018 14:38:05 -0400 Subject: [PATCH 0566/1971] Merge pull request #2599 from LLK/fix-safari9 Add intl polyfill for Safari 9 --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4393537631..136a6bc41f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -59,6 +59,7 @@ "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", "html-webpack-plugin": "^3.2.0", "immutable": "3.8.2", + "intl": "1.2.5", "jest": "^21.0.0", "keymirror": "0.1.1", "lodash.bindall": "4.4.0", From 38bb234380cd084b2b0e55ecb6139d5d0c79c35d Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 12 Jul 2018 16:08:59 -0400 Subject: [PATCH 0567/1971] Merge pull request #2606 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1531424474 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 136a6bc41f..d5237498b2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1531409796", + "scratch-blocks": "0.1.0-prerelease.1531424474", "scratch-l10n": "3.0.20180703181510", "scratch-paint": "0.2.0-prerelease.20180712144339", "scratch-render": "0.1.0-prerelease.20180618173030", From fd583ab0628751c84a2ad5b1aaa22a3a64a83ce6 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 12 Jul 2018 18:42:17 -0400 Subject: [PATCH 0568/1971] Merge pull request #2609 from ericrosenbaum/bugfix/microbit-name-and-description Fix micro:bit extension name and description --- packages/scratch-gui/src/lib/libraries/extensions/index.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index 62da95ea5b..dfe1416464 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -115,13 +115,13 @@ export default [ disabled: true }, { - name: 'Micro:bit', + name: 'micro:bit', extensionId: 'microbit', iconURL: microbitImage, description: ( ), From 3ee617343e688786f409dd2cd0faa6b5cc8287b5 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 12 Jul 2018 19:14:03 -0400 Subject: [PATCH 0569/1971] Merge pull request #2575 from fsih/bitmapAdapter Use bitmapAdapter for costume upload --- packages/scratch-gui/package.json | 4 ++-- packages/scratch-gui/src/containers/stage.jsx | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d5237498b2..c9fece77f4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -101,8 +101,8 @@ "scratch-paint": "0.2.0-prerelease.20180712144339", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", - "scratch-svg-renderer": "0.2.0-prerelease.20180618172917", - "scratch-vm": "0.1.0-prerelease.1531340421", + "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", + "scratch-vm": "0.1.0-prerelease.1531436232", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 01fc3ec7c7..0ff48ef021 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -9,6 +9,7 @@ import {STAGE_DISPLAY_SIZES} from '../lib/layout-constants'; import {getEventXY} from '../lib/touch-utils'; import VideoProvider from '../lib/video/video-provider'; import {SVGRenderer as V2SVGAdapter} from 'scratch-svg-renderer'; +import {BitmapAdapter as V2BitmapAdapter} from 'scratch-svg-renderer'; import StageComponent from '../components/stage/stage.jsx'; @@ -60,6 +61,7 @@ class Stage extends React.Component { this.props.vm.attachRenderer(this.renderer); } this.props.vm.attachV2SVGAdapter(new V2SVGAdapter()); + this.props.vm.attachV2BitmapAdapter(new V2BitmapAdapter()); this.props.vm.setVideoProvider(new VideoProvider()); } componentDidMount () { From 7be5bfb8a8fd427ee6e233fb8b62f5daaac9fa26 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 13 Jul 2018 07:48:50 -0400 Subject: [PATCH 0570/1971] Merge pull request #2611 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1531439997 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c9fece77f4..261fb22fd3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1531424474", + "scratch-blocks": "0.1.0-prerelease.1531439997", "scratch-l10n": "3.0.20180703181510", "scratch-paint": "0.2.0-prerelease.20180712144339", "scratch-render": "0.1.0-prerelease.20180618173030", From 3ddfc1e5aed7c177166b21e324ffee9064ec5fb4 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Fri, 13 Jul 2018 08:14:50 -0400 Subject: [PATCH 0571/1971] Merge pull request #2608 from LLK/greenkeeper/scratch-l10n-3.0.20180712200642 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 261fb22fd3..8f44639755 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1531439997", - "scratch-l10n": "3.0.20180703181510", + "scratch-l10n": "3.0.20180712200642", "scratch-paint": "0.2.0-prerelease.20180712144339", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", From 4a6d07d0a1bb0989604061f0ddbf23ea9fd07c12 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 13 Jul 2018 08:15:44 -0400 Subject: [PATCH 0572/1971] Merge pull request #2607 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180712195436 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8f44639755..36a013ffa6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1531439997", "scratch-l10n": "3.0.20180712200642", - "scratch-paint": "0.2.0-prerelease.20180712144339", + "scratch-paint": "0.2.0-prerelease.20180712195436", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", From b049c52f300ea62a97c53c28259dd446e6f1b556 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 13 Jul 2018 08:17:53 -0400 Subject: [PATCH 0573/1971] Merge pull request #2610 from kchadha/rename-var-bugfix Fix rename variable bug --- packages/scratch-gui/src/containers/blocks.jsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index c81c5d21ad..1de665e334 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -366,9 +366,10 @@ class Blocks extends React.Component { this.ScratchBlocks.refreshStatusButtons(this.workspace); } handlePromptCallback (input, optionSelection) { - this.state.prompt.callback(input, optionSelection, - (optionSelection === 'local') ? [] : - this.props.vm.runtime.getAllVarNamesOfType(this.state.prompt.varType)); + this.state.prompt.callback( + input, + this.props.vm.runtime.getAllVarNamesOfType(this.state.prompt.varType), + optionSelection); this.handlePromptClose(); } handlePromptClose () { From 7399cb67c091b9ee34b1ede5b7c15f7056ac1784 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 13 Jul 2018 08:18:35 -0400 Subject: [PATCH 0574/1971] Merge pull request #2613 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1531482946 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 36a013ffa6..042f14655b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1531439997", + "scratch-blocks": "0.1.0-prerelease.1531482946", "scratch-l10n": "3.0.20180712200642", "scratch-paint": "0.2.0-prerelease.20180712195436", "scratch-render": "0.1.0-prerelease.20180618173030", From 79f68a9e297031abf93443a9c42d9fbeaa8729b4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 13 Jul 2018 08:59:10 -0400 Subject: [PATCH 0575/1971] Merge pull request #2614 from paulkaplan/update-vm-july-13 Update scratch vm to include promise reporter fixes --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 042f14655b..1b589d2284 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", - "scratch-vm": "0.1.0-prerelease.1531436232", + "scratch-vm": "0.1.0-prerelease.1531486395", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From cb2aec660069c05d11b52f9a3744fc5f00541ad1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 13 Jul 2018 13:34:39 -0400 Subject: [PATCH 0576/1971] Merge pull request #2620 from paulkaplan/update-vm-july-13-again Update vm again, bringing in variable updates --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1b589d2284..23701739cd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", - "scratch-vm": "0.1.0-prerelease.1531486395", + "scratch-vm": "0.1.0-prerelease.1531502959", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From 5e83dcab858d8d256893b8d8b6f8b7ec6c2a7dba Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 16 Jul 2018 11:40:16 -0400 Subject: [PATCH 0577/1971] Merge pull request #2639 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180713150122 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 23701739cd..79f217213b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1531482946", "scratch-l10n": "3.0.20180712200642", - "scratch-paint": "0.2.0-prerelease.20180712195436", + "scratch-paint": "0.2.0-prerelease.20180713150122", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", From 864d32c0e8ab93152ae2a0c7553cdcddcc3e1c48 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 17 Jul 2018 16:56:08 -0400 Subject: [PATCH 0578/1971] Merge pull request #2653 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180717182448 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 79f217213b..6f3f5d81a6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1531482946", "scratch-l10n": "3.0.20180712200642", - "scratch-paint": "0.2.0-prerelease.20180713150122", + "scratch-paint": "0.2.0-prerelease.20180717182448", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", From 586388deae2626ccc0c4c1f61bed7e9d8b46ba9d Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 17 Jul 2018 17:37:57 -0400 Subject: [PATCH 0579/1971] Update scratch paint, adds gradient fills --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6f3f5d81a6..294c6858e9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1531482946", "scratch-l10n": "3.0.20180712200642", - "scratch-paint": "0.2.0-prerelease.20180717182448", + "scratch-paint": "0.2.0-prerelease.20180717212631", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", From 7f0f2d4fd80e3347d7e237b03aa8f285de15ab06 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 18 Jul 2018 10:39:43 -0400 Subject: [PATCH 0580/1971] Merge pull request #2661 from thisandagain/bugfix/2621 Change 'video motion' to 'video sensing' and minor clean-up to extension library --- .../src/lib/libraries/extensions/index.jsx | 85 ++++++------------- 1 file changed, 24 insertions(+), 61 deletions(-) diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index dfe1416464..1ef3bd706d 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -4,18 +4,15 @@ import {FormattedMessage} from 'react-intl'; import musicImage from './music.png'; import penImage from './pen.png'; import videoImage from './video-sensing.png'; -import speechImage from './speech.png'; +import translateImage from './translate.png'; import microbitImage from './microbit.png'; -import wedoImage from './wedo.png'; import ev3Image from './ev3.png'; -import boostImage from './boost.png'; -import translateImage from './translate.png'; - -import ev3DeviceImage from './device-connection/ev3/ev3-hub-illustration.svg'; -import ev3MenuImage from './device-connection/ev3/ev3-small.svg'; +import wedoImage from './wedo.png'; import microbitDeviceImage from './device-connection/microbit/microbit-illustration.svg'; import microbitMenuImage from './device-connection/microbit/microbit-small.svg'; +import ev3DeviceImage from './device-connection/ev3/ev3-hub-illustration.svg'; +import ev3MenuImage from './device-connection/ev3/ev3-small.svg'; export default [ { @@ -59,37 +56,18 @@ export default [ { name: ( - ), - extensionId: 'translate', - iconURL: translateImage, - description: ( - - ), - featured: true - }, - { - name: ( - ), extensionId: 'videoSensing', iconURL: videoImage, description: ( ), featured: true @@ -97,22 +75,21 @@ export default [ { name: ( ), - extensionId: 'speech', - iconURL: speechImage, + extensionId: 'translate', + iconURL: translateImage, description: ( ), - featured: true, - disabled: true + featured: true }, { name: 'micro:bit', @@ -131,20 +108,6 @@ export default [ deviceImage: microbitDeviceImage, smallDeviceImage: microbitMenuImage }, - { - name: 'LEGO WeDo 2.0', - extensionId: 'wedo2', - iconURL: wedoImage, - description: ( - - ), - featured: true, - disabled: true - }, { name: 'LEGO MINDSTORMS EV3', extensionId: 'ev3', @@ -163,14 +126,14 @@ export default [ smallDeviceImage: ev3MenuImage }, { - name: 'LEGO Boost', - extensionId: 'boost', - iconURL: boostImage, + name: 'LEGO WeDo 2.0', + extensionId: 'wedo2', + iconURL: wedoImage, description: ( ), featured: true, From a7f47a440bf1a775b7ade98d3252a9e7fafc4333 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 18 Jul 2018 11:43:28 -0400 Subject: [PATCH 0581/1971] Merge pull request #2660 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1531868456 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.153… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 294c6858e9..7f52f94f8b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1531482946", + "scratch-blocks": "0.1.0-prerelease.1531868456", "scratch-l10n": "3.0.20180712200642", "scratch-paint": "0.2.0-prerelease.20180717212631", "scratch-render": "0.1.0-prerelease.20180618173030", From d7f6c02c8cc95f4fef3d7f099e4622c6976f0a13 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 18 Jul 2018 13:06:04 -0400 Subject: [PATCH 0582/1971] Merge pull request #2655 from chrisgarrity/feature/enable-language Enable the language menu --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index b843e05b45..08bd6b5561 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -159,8 +159,9 @@ class MenuBar extends React.Component { })} onMouseUp={this.handleLanguageMouseUp} > + {/* @TODO: remove coming soon tooltip wrapper https://github.com/LLK/scratch-gui/issues/2664 */} From e4f6e329690f0df7b1bb9ec3a483a2f049ac8867 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 18 Jul 2018 14:02:46 -0400 Subject: [PATCH 0583/1971] Merge pull request #2662 from paulkaplan/add-ev3-connection-message Add ev3 connection message and help links --- .../scratch-gui/src/containers/blocks.jsx | 6 +++++- .../src/lib/libraries/extensions/index.jsx | 20 +++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 1de665e334..56f2fa5d5f 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -355,7 +355,9 @@ class Blocks extends React.Component { extensionId: extensionId, deviceImage: extension.deviceImage, smallDeviceImage: extension.smallDeviceImage, - name: extension.name + name: extension.name, + connectingMessage: extension.connectingMessage, + helpLink: extension.helpLink }}); } } @@ -419,8 +421,10 @@ class Blocks extends React.Component { ) : null} {this.state.connectionModal ? ( + ), + helpLink: 'https://scratch.mit.edu/microbit' }, { name: 'LEGO MINDSTORMS EV3', @@ -123,7 +131,15 @@ export default [ disabled: true, launchDeviceConnectionFlow: true, deviceImage: ev3DeviceImage, - smallDeviceImage: ev3MenuImage + smallDeviceImage: ev3MenuImage, + connectingMessage: ( + + ), + helpLink: 'https://scratch.mit.edu/ev3' }, { name: 'LEGO WeDo 2.0', From 5b94efb4fbf470ea1f3d163931e8bba20612fb28 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 18 Jul 2018 14:09:23 -0400 Subject: [PATCH 0584/1971] Merge pull request #2668 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1531933918 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.153… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7f52f94f8b..ce5ccbb4eb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1531868456", + "scratch-blocks": "0.1.0-prerelease.1531933918", "scratch-l10n": "3.0.20180712200642", "scratch-paint": "0.2.0-prerelease.20180717212631", "scratch-render": "0.1.0-prerelease.20180618173030", From a811877b48676e1d62860e62633e14b194c3fec9 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 18 Jul 2018 14:22:17 -0400 Subject: [PATCH 0585/1971] Merge pull request #2667 from chrisgarrity/issue/2530-block-size-locale Issue/2530 block size locale --- packages/scratch-gui/package.json | 2 +- .../scratch-gui/src/containers/blocks.jsx | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ce5ccbb4eb..8118ea7f39 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", - "scratch-vm": "0.1.0-prerelease.1531502959", + "scratch-vm": "0.1.0-prerelease.1531927323", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 56f2fa5d5f..2ea34d2203 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -94,7 +94,11 @@ class Blocks extends React.Component { addFunctionListener(this.workspace, 'zoom', this.onWorkspaceMetricsChange); this.attachVM(); - this.setLocale(); + // Only update blocks/vm locale when visible to avoid sizing issues + // If locale changes while not visible it will get handled in didUpdate + if (this.props.isVisible) { + this.setLocale(); + } analytics.pageview('/editors/blocks'); } @@ -117,10 +121,6 @@ class Blocks extends React.Component { this.ScratchBlocks.hideChaff(); } - if (prevProps.locale !== this.props.locale) { - this.setLocale(); - } - if (prevProps.toolboxXML !== this.props.toolboxXML) { // rather than update the toolbox "sync" -- update it in the next frame clearTimeout(this.toolboxUpdateTimeout); @@ -139,7 +139,14 @@ class Blocks extends React.Component { // @todo hack to reload the workspace due to gui bug #413 if (this.props.isVisible) { // Scripts tab this.workspace.setVisible(true); - this.props.vm.refreshWorkspace(); + if (prevProps.locale !== this.props.locale || this.props.locale !== this.props.vm.getLocale()) { + // call setLocale if the locale has changed, or changed while the blocks were hidden. + // vm.getLocale() will be out of sync if locale was changed while not visible + this.setLocale(); + } else { + this.props.vm.refreshWorkspace(); + } + // Re-enable toolbox refreshes without causing one. See #updateToolbox for more info. this.workspace.toolboxRefreshEnabled_ = true; window.dispatchEvent(new Event('resize')); From 4b1ea1296e9b8719792ce2c23e0c7d3afd0d3b18 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 18 Jul 2018 14:40:36 -0400 Subject: [PATCH 0586/1971] Merge pull request #2669 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180718183615 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8118ea7f39..7a0a81d0ed 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1531933918", "scratch-l10n": "3.0.20180712200642", - "scratch-paint": "0.2.0-prerelease.20180717212631", + "scratch-paint": "0.2.0-prerelease.20180718183615", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", From 5c559eaa37fd853b55709884f56601fc807736a1 Mon Sep 17 00:00:00 2001 From: kchadha Date: Wed, 18 Jul 2018 17:08:33 -0400 Subject: [PATCH 0587/1971] Merge pull request #2674 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20180718204133 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7a0a81d0ed..ef6d5add60 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", - "scratch-vm": "0.1.0-prerelease.1531927323", + "scratch-vm": "0.2.0-prerelease.20180718204133", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From d2b11d9222403b9003752043529ccbb3d3e1b7ca Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 19 Jul 2018 10:55:55 -0400 Subject: [PATCH 0588/1971] Merge pull request #2679 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1532006613 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ef6d5add60..b96a0e7222 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1531933918", + "scratch-blocks": "0.1.0-prerelease.1532006613", "scratch-l10n": "3.0.20180712200642", "scratch-paint": "0.2.0-prerelease.20180718183615", "scratch-render": "0.1.0-prerelease.20180618173030", From 205f28d6e39c56328a8ca06ae70b0b138d49268d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 19 Jul 2018 11:36:19 -0400 Subject: [PATCH 0589/1971] Merge pull request #2681 from LLK/greenkeeper/scratch-l10n-3.0.20180719145856 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b96a0e7222..07d53905b6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1532006613", - "scratch-l10n": "3.0.20180712200642", + "scratch-l10n": "3.0.20180719145856", "scratch-paint": "0.2.0-prerelease.20180718183615", "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", From 832f15b084e6feeb0cd5b5948a6b0e8b0d5c08ff Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 19 Jul 2018 14:43:54 -0400 Subject: [PATCH 0590/1971] Merge pull request #2683 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1532024291 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 07d53905b6..213e65c5ed 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1532006613", + "scratch-blocks": "0.1.0-prerelease.1532024291", "scratch-l10n": "3.0.20180719145856", "scratch-paint": "0.2.0-prerelease.20180718183615", "scratch-render": "0.1.0-prerelease.20180618173030", From 74951b4fc99e65004b0ee5bf9176cc14eabca75a Mon Sep 17 00:00:00 2001 From: kchadha Date: Thu, 19 Jul 2018 15:52:50 -0400 Subject: [PATCH 0591/1971] Merge pull request #2684 from kchadha/update-toolbox-menus-after-rename Update Toolbox when blocks container updates and is visible. --- packages/scratch-gui/src/containers/blocks.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 2ea34d2203..cd3d4ff538 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -145,10 +145,9 @@ class Blocks extends React.Component { this.setLocale(); } else { this.props.vm.refreshWorkspace(); + this.updateToolbox(); } - // Re-enable toolbox refreshes without causing one. See #updateToolbox for more info. - this.workspace.toolboxRefreshEnabled_ = true; window.dispatchEvent(new Event('resize')); } else { this.workspace.setVisible(false); @@ -165,8 +164,8 @@ class Blocks extends React.Component { this.ScratchBlocks.ScratchMsgs.setLocale(this.props.locale); this.props.vm.setLocale(this.props.locale, this.props.messages) .then(() => { - this.workspace.updateToolbox(this.props.toolboxXML); this.props.vm.refreshWorkspace(); + this.updateToolbox(); this.workspace.getFlyout().setRecyclingEnabled(true); }); } From 2dbbd41306f23c187519fa5555b5080b1388a421 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 19 Jul 2018 15:53:38 -0400 Subject: [PATCH 0592/1971] Merge pull request #2680 from LLK/enable-hardware-extensions Enable EV3 and micro:bit extensions --- packages/scratch-gui/src/lib/libraries/extensions/index.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index 79de349873..316980efe1 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -103,7 +103,7 @@ export default [ /> ), featured: true, - disabled: true, + disabled: false, launchDeviceConnectionFlow: true, deviceImage: microbitDeviceImage, smallDeviceImage: microbitMenuImage, @@ -128,7 +128,7 @@ export default [ /> ), featured: true, - disabled: true, + disabled: false, launchDeviceConnectionFlow: true, deviceImage: ev3DeviceImage, smallDeviceImage: ev3MenuImage, From c0920ba90d089367179d845b6e6c3f19b0a577f2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 19 Jul 2018 17:17:02 -0400 Subject: [PATCH 0593/1971] Merge pull request #2686 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20180719205147 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 213e65c5ed..c21c222584 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", - "scratch-vm": "0.2.0-prerelease.20180718204133", + "scratch-vm": "0.2.0-prerelease.20180719205147", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From 7369f6f6af1563926ee67407bf11842dab5cb352 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 23 Jul 2018 14:06:20 -0400 Subject: [PATCH 0594/1971] Merge pull request #2701 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20180723145316 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c21c222584..0e754f6a2b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", - "scratch-vm": "0.2.0-prerelease.20180719205147", + "scratch-vm": "0.2.0-prerelease.20180723145316", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From bd5420f663b157ae2d44267628fe7daf10d91be4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Jul 2018 08:42:08 -0400 Subject: [PATCH 0595/1971] Merge pull request #2708 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20180723212614 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0e754f6a2b..18c2ab57e2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", - "scratch-vm": "0.2.0-prerelease.20180723145316", + "scratch-vm": "0.2.0-prerelease.20180723212614", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From d9b556de0d02f8ead73302a5dc56b9d364017854 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Jul 2018 08:43:01 -0400 Subject: [PATCH 0596/1971] Merge pull request #2705 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1532372094 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 18c2ab57e2..aa24616f3b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1532024291", + "scratch-blocks": "0.1.0-prerelease.1532372094", "scratch-l10n": "3.0.20180719145856", "scratch-paint": "0.2.0-prerelease.20180718183615", "scratch-render": "0.1.0-prerelease.20180618173030", From 04d055fbe34205580bca4162c9296b6a2dcb4e70 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Jul 2018 13:44:13 -0400 Subject: [PATCH 0597/1971] Merge pull request #2713 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20180724151553 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index aa24616f3b..db593f9d28 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180618173030", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", - "scratch-vm": "0.2.0-prerelease.20180723212614", + "scratch-vm": "0.2.0-prerelease.20180724151553", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From 994e4bfc90eb6b889dfaf63ccdf226fb7f969155 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Jul 2018 13:53:22 -0400 Subject: [PATCH 0598/1971] Merge pull request #2715 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1532446271 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index db593f9d28..c7b0e60796 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1532372094", + "scratch-blocks": "0.1.0-prerelease.1532446271", "scratch-l10n": "3.0.20180719145856", "scratch-paint": "0.2.0-prerelease.20180718183615", "scratch-render": "0.1.0-prerelease.20180618173030", From 481613e9f6edf5766fa5c21fb398f376f2e771ac Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Jul 2018 13:53:53 -0400 Subject: [PATCH 0599/1971] Merge pull request #2714 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20180724152606 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c7b0e60796..7dd15dad33 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -99,7 +99,7 @@ "scratch-blocks": "0.1.0-prerelease.1532446271", "scratch-l10n": "3.0.20180719145856", "scratch-paint": "0.2.0-prerelease.20180718183615", - "scratch-render": "0.1.0-prerelease.20180618173030", + "scratch-render": "0.1.0-prerelease.20180724152606", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", "scratch-vm": "0.2.0-prerelease.20180724151553", From 120a0f82b148a118bd1f4d13ed65c4c0a30e6493 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Jul 2018 13:54:08 -0400 Subject: [PATCH 0600/1971] Merge pull request #2712 from paulkaplan/bring-dragging-sprites-to-front Bring dragging sprites to front --- packages/scratch-gui/src/containers/stage.jsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 0ff48ef021..1ab84412ee 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -309,11 +309,13 @@ class Stage extends React.Component { const targetId = this.props.vm.getTargetIdForDrawableId(drawableId); if (targetId === null) return; - // Only start drags on non-draggable targets in editor drag mode - if (!this.props.useEditorDragStyle) { - const target = this.props.vm.runtime.getTargetById(targetId); - if (!target.draggable) return; - } + const target = this.props.vm.runtime.getTargetById(targetId); + + // Do not start drag unless in editor drag mode or target is draggable + if (!(this.props.useEditorDragStyle || target.draggable)) return; + + // Dragging always brings the target to the front + target.goToFront(); this.props.vm.startDrag(targetId); this.setState({ From 2f77130c1dcc1fd0f6b72bcce5b141fad72f8f9c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Jul 2018 15:09:47 -0400 Subject: [PATCH 0601/1971] Merge pull request #2718 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20180724180310 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7dd15dad33..646d302ba2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180724152606", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", - "scratch-vm": "0.2.0-prerelease.20180724151553", + "scratch-vm": "0.2.0-prerelease.20180724180310", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From 78df20a60376a4603fdaa503d6625a05470c13b4 Mon Sep 17 00:00:00 2001 From: kchadha Date: Tue, 24 Jul 2018 15:30:31 -0400 Subject: [PATCH 0602/1971] Merge pull request #2722 from kchadha/clear-workspace-undo Clear the workspace undo when the workspace updates. --- packages/scratch-gui/src/containers/blocks.jsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index cd3d4ff538..3862fa653f 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -310,6 +310,11 @@ class Blocks extends React.Component { this.workspace.scale = scale; this.workspace.resize(); } + + // Clear the undo state of the workspace since this is a + // fresh workspace and we don't want any changes made to another sprites + // workspace to be 'undone' here. + this.workspace.clearUndo(); } handleExtensionAdded (blocksInfo) { // select JSON from each block info object then reject the pseudo-blocks which don't have JSON, like separators From 482b2f28d3193fad09bc995e44de721a64915d44 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Jul 2018 16:22:12 -0400 Subject: [PATCH 0603/1971] Merge pull request #2723 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20180724192502 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 646d302ba2..2754f58c61 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180724152606", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", - "scratch-vm": "0.2.0-prerelease.20180724180310", + "scratch-vm": "0.2.0-prerelease.20180724192502", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From 84bba4696b4535c57018c8a8b2f612fae86059c4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Jul 2018 16:30:29 -0400 Subject: [PATCH 0604/1971] Merge pull request #2638 from LLK/greenkeeper/react-responsive-5.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-responsive to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2754f58c61..a8ceed5994 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -85,7 +85,7 @@ "react-modal": "3.4.4", "react-popover": "0.5.7", "react-redux": "5.0.7", - "react-responsive": "4.1.0", + "react-responsive": "5.0.0", "react-style-proptype": "3.2.1", "react-tabs": "2.2.2", "react-test-renderer": "16.2.0", From d9cdfefcc3cd0327eb46e58ed491b0e93d4d46bc Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Jul 2018 16:31:26 -0400 Subject: [PATCH 0605/1971] Merge pull request #2582 from LLK/greenkeeper/react-virtualized-9.20.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-virtualized to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a8ceed5994..a8af59f73e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -90,7 +90,7 @@ "react-tabs": "2.2.2", "react-test-renderer": "16.2.0", "react-tooltip": "3.6.1", - "react-virtualized": "9.20.0", + "react-virtualized": "9.20.1", "redux": "3.7.2", "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", From 4c5a3783efafc3705b74fc56f5f9254468c32779 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Jul 2018 16:32:33 -0400 Subject: [PATCH 0606/1971] Merge pull request #2559 from LLK/greenkeeper/css-loader-1.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update css-loader to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a8af59f73e..8697ec7ec4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -45,7 +45,7 @@ "classnames": "2.2.6", "copy-webpack-plugin": "^4.5.1", "core-js": "2.5.7", - "css-loader": "^0.28.11", + "css-loader": "^1.0.0", "enzyme": "^3.1.0", "enzyme-adapter-react-16": "1.1.1", "es6-object-assign": "1.1.0", From 5c8b0b29a23c88925e739481f0b42b9435590cd2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Jul 2018 16:32:53 -0400 Subject: [PATCH 0607/1971] Merge pull request #2549 from LLK/greenkeeper/react-modal-3.5.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-modal to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8697ec7ec4..771d9d2378 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -82,7 +82,7 @@ "react-draggable": "3.0.5", "react-ga": "2.5.3", "react-intl": "2.4.0", - "react-modal": "3.4.4", + "react-modal": "3.5.1", "react-popover": "0.5.7", "react-redux": "5.0.7", "react-responsive": "5.0.0", From ef21784fc933a2a91d42fcf399ea8e49c5b95713 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Jul 2018 16:33:26 -0400 Subject: [PATCH 0608/1971] Merge pull request #2531 from LLK/greenkeeper/bowser-1.9.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update bowser to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 771d9d2378..3295b9ec17 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -40,7 +40,7 @@ "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.22.0", "base64-loader": "1.0.0", - "bowser": "1.9.3", + "bowser": "1.9.4", "chromedriver": "2.40.0", "classnames": "2.2.6", "copy-webpack-plugin": "^4.5.1", From 59fe57e8d349f5e30a9990182cc200a8216f34c2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 25 Jul 2018 08:50:19 -0400 Subject: [PATCH 0609/1971] Merge pull request #2724 from LLK/greenkeeper/autoprefixer-9.0.1 chore(package): update autoprefixer to version 9.0.1 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3295b9ec17..c91d10ccc6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -30,7 +30,7 @@ }, "devDependencies": { "arraybuffer-loader": "^1.0.3", - "autoprefixer": "^8.1.0", + "autoprefixer": "^9.0.1", "babel-core": "^6.23.1", "babel-eslint": "^8.0.1", "babel-loader": "^7.1.0", From 501f413920e36332a89ea31280597e9dad74a1b9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 25 Jul 2018 09:58:59 -0400 Subject: [PATCH 0610/1971] Merge pull request #2699 from paulkaplan/set-username Add username setting to vm listener HOC --- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 3fd9e2096f..28f578b371 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -37,6 +37,12 @@ const vmListenerHOC = function (WrappedComponent) { document.addEventListener('keydown', this.handleKeyDown); document.addEventListener('keyup', this.handleKeyUp); } + this.props.vm.postIOData('userData', {username: this.props.username}); + } + componentWillReceiveProps (newProps) { + if (newProps.username !== this.props.username) { + this.props.vm.postIOData('userData', {username: newProps.username}); + } } componentWillUnmount () { if (this.props.attachKeyboardEvents) { @@ -72,6 +78,7 @@ const vmListenerHOC = function (WrappedComponent) { const { /* eslint-disable no-unused-vars */ attachKeyboardEvents, + username, onBlockDragUpdate, onKeyDown, onKeyUp, @@ -90,13 +97,16 @@ const vmListenerHOC = function (WrappedComponent) { onKeyUp: PropTypes.func, onMonitorsUpdate: PropTypes.func.isRequired, onTargetsUpdate: PropTypes.func.isRequired, + username: PropTypes.string, vm: PropTypes.instanceOf(VM).isRequired }; VMListener.defaultProps = { attachKeyboardEvents: true }; const mapStateToProps = state => ({ - vm: state.scratchGui.vm + vm: state.scratchGui.vm, + username: state.session && state.session.session ? + state.session.session.username : '' }); const mapDispatchToProps = dispatch => ({ onTargetsUpdate: data => { From a36afd21e7199c68233c69abaf2e03667daa178e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 25 Jul 2018 13:28:09 -0400 Subject: [PATCH 0611/1971] Merge pull request #2728 from LLK/greenkeeper/arraybuffer-loader-1.0.6 chore(package): update arraybuffer-loader to version 1.0.6 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c91d10ccc6..10c2e5667a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -29,7 +29,7 @@ "react-dom": "^16.0.0" }, "devDependencies": { - "arraybuffer-loader": "^1.0.3", + "arraybuffer-loader": "^1.0.6", "autoprefixer": "^9.0.1", "babel-core": "^6.23.1", "babel-eslint": "^8.0.1", From 5c6d15e66869abcf36e0fa5667d06fbc4202fe05 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 31 Jul 2018 07:46:52 -0400 Subject: [PATCH 0612/1971] Merge pull request #2733 from LLK/greenkeeper/react-contextmenu-2.9.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-contextmenu to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 10c2e5667a..40fe418be9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -77,7 +77,7 @@ "raf": "^3.4.0", "raw-loader": "^0.5.1", "react": "16.2.0", - "react-contextmenu": "2.9.2", + "react-contextmenu": "2.9.3", "react-dom": "16.2.0", "react-draggable": "3.0.5", "react-ga": "2.5.3", From 802a8ee645d1374e36f974d1bfe3f0fa0bdd5058 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 31 Jul 2018 13:43:03 -0400 Subject: [PATCH 0613/1971] Merge pull request #2746 from LLK/greenkeeper/chromedriver-2.41.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update chromedriver to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 40fe418be9..b4b134061f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -41,7 +41,7 @@ "babel-preset-react": "^6.22.0", "base64-loader": "1.0.0", "bowser": "1.9.4", - "chromedriver": "2.40.0", + "chromedriver": "2.41.0", "classnames": "2.2.6", "copy-webpack-plugin": "^4.5.1", "core-js": "2.5.7", From 3b007b9c08f7aaf771cb71c9945f3c88a15f2906 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 1 Aug 2018 20:19:36 -0400 Subject: [PATCH 0614/1971] Merge pull request #2761 from LLK/master Bring in hotfix to beta stuff to develop --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 08bd6b5561..edbc8a971b 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -376,7 +376,7 @@ class MenuBar extends React.Component {
From 2cdf362afa32cea77bb5a61e7ffc0d0cd52f79af Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 2 Aug 2018 12:47:35 -0400 Subject: [PATCH 0615/1971] Merge pull request #2769 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20180802142248 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b4b134061f..63f1ceaca4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180724152606", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", - "scratch-vm": "0.2.0-prerelease.20180724192502", + "scratch-vm": "0.2.0-prerelease.20180802142248", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From 47bf8b1c247dfa49032242b279e6abf125d0c6f9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 2 Aug 2018 13:15:50 -0400 Subject: [PATCH 0616/1971] Merge pull request #2767 from paulkaplan/turbo-events Turbo mode menu --- .../src/components/menu-bar/menu-bar.jsx | 25 +++++++++++++------ .../scratch-gui/src/lib/vm-listener-hoc.jsx | 15 ++++++++++- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index edbc8a971b..24117372b4 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -14,6 +14,7 @@ import ProjectLoader from '../../containers/project-loader.jsx'; import Menu from '../../containers/menu.jsx'; import {MenuItem, MenuSection} from '../menu/menu.jsx'; import ProjectSaver from '../../containers/project-saver.jsx'; +import TurboMode from '../../containers/turbo-mode.jsx'; import {openTipsLibrary} from '../../reducers/modals'; import {setPlayer} from '../../reducers/mode'; @@ -297,15 +298,23 @@ class MenuBar extends React.Component { - - - + {(toggleTurboMode, {turboMode}) => ( + + {turboMode ? ( + + ) : ( + + )} - + )}
diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 28f578b371..b2256b82a4 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -8,6 +8,7 @@ import {connect} from 'react-redux'; import {updateTargets} from '../reducers/targets'; import {updateBlockDrag} from '../reducers/block-drag'; import {updateMonitors} from '../reducers/monitors'; +import {setRunningState, setTurboState} from '../reducers/vm-status'; /* * Higher Order Component to manage events emitted by the VM @@ -31,6 +32,10 @@ const vmListenerHOC = function (WrappedComponent) { this.props.vm.on('targetsUpdate', this.props.onTargetsUpdate); this.props.vm.on('MONITORS_UPDATE', this.props.onMonitorsUpdate); this.props.vm.on('BLOCK_DRAG_UPDATE', this.props.onBlockDragUpdate); + this.props.vm.on('TURBO_MODE_ON', this.props.onTurboModeOn); + this.props.vm.on('TURBO_MODE_OFF', this.props.onTurboModeOff); + this.props.vm.on('PROJECT_RUN_START', this.props.onProjectRunStart); + this.props.vm.on('PROJECT_RUN_STOP', this.props.onProjectRunStop); } componentDidMount () { if (this.props.attachKeyboardEvents) { @@ -96,7 +101,11 @@ const vmListenerHOC = function (WrappedComponent) { onKeyDown: PropTypes.func, onKeyUp: PropTypes.func, onMonitorsUpdate: PropTypes.func.isRequired, + onProjectRunStart: PropTypes.func.isRequired, + onProjectRunStop: PropTypes.func.isRequired, onTargetsUpdate: PropTypes.func.isRequired, + onTurboModeOff: PropTypes.func.isRequired, + onTurboModeOn: PropTypes.func.isRequired, username: PropTypes.string, vm: PropTypes.instanceOf(VM).isRequired }; @@ -117,7 +126,11 @@ const vmListenerHOC = function (WrappedComponent) { }, onBlockDragUpdate: areBlocksOverGui => { dispatch(updateBlockDrag(areBlocksOverGui)); - } + }, + onProjectRunStart: () => dispatch(setRunningState(true)), + onProjectRunStop: () => dispatch(setRunningState(false)), + onTurboModeOn: () => dispatch(setTurboState(true)), + onTurboModeOff: () => dispatch(setTurboState(false)) }); return connect( mapStateToProps, From 852c4a5b75802a3832c7ead2807968e2a001bff0 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 3 Aug 2018 08:42:35 -0400 Subject: [PATCH 0617/1971] Merge pull request #2771 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180802204251 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 63f1ceaca4..4bb36e4016 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1532446271", "scratch-l10n": "3.0.20180719145856", - "scratch-paint": "0.2.0-prerelease.20180718183615", + "scratch-paint": "0.2.0-prerelease.20180802204251", "scratch-render": "0.1.0-prerelease.20180724152606", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", From 27f21fcab7d20e649a7fa6e1d066331e2d51623f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 3 Aug 2018 12:21:31 -0400 Subject: [PATCH 0618/1971] Merge pull request #2768 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1533219167 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4bb36e4016..91ea7f69dc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1532446271", + "scratch-blocks": "0.1.0-prerelease.1533219167", "scratch-l10n": "3.0.20180719145856", "scratch-paint": "0.2.0-prerelease.20180802204251", "scratch-render": "0.1.0-prerelease.20180724152606", From 5b10997a940a0339d95ed2999a3b982f19ddccb0 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Fri, 3 Aug 2018 15:56:23 -0400 Subject: [PATCH 0619/1971] Merge pull request #2785 from chrisgarrity/smoke-update-translations [DEVELOP] Update dependencies for translations --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 91ea7f69dc..70d47dc260 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,8 +96,8 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1533219167", - "scratch-l10n": "3.0.20180719145856", + "scratch-blocks": "0.1.0-prerelease.1533301486", + "scratch-l10n": "3.0.20180803171042", "scratch-paint": "0.2.0-prerelease.20180802204251", "scratch-render": "0.1.0-prerelease.20180724152606", "scratch-storage": "0.5.1", From 5543755b3c34a5004143046f3882ff14265e9cbd Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Fri, 3 Aug 2018 15:57:09 -0400 Subject: [PATCH 0620/1971] Merge pull request #2760 from chrisgarrity/feature/1579-rtl fixes #1579 --- packages/scratch-gui/src/containers/blocks.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 3862fa653f..44d52664e3 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -77,7 +77,7 @@ class Blocks extends React.Component { const workspaceConfig = defaultsDeep({}, Blocks.defaultOptions, this.props.options, - {toolbox: this.props.toolboxXML} + {rtl: this.props.isRtl, toolbox: this.props.toolboxXML} ); this.workspace = this.ScratchBlocks.inject(this.blocks, workspaceConfig); @@ -403,6 +403,7 @@ class Blocks extends React.Component { options, stageSize, vm, + isRtl, isVisible, onActivateColorPicker, updateToolboxState, @@ -467,6 +468,7 @@ Blocks.propTypes = { anyModalVisible: PropTypes.bool, customProceduresVisible: PropTypes.bool, extensionLibraryVisible: PropTypes.bool, + isRtl: PropTypes.bool, isVisible: PropTypes.bool, locale: PropTypes.string, messages: PropTypes.objectOf(PropTypes.string), @@ -541,6 +543,7 @@ const mapStateToProps = state => ({ state.scratchGui.mode.isFullScreen ), extensionLibraryVisible: state.scratchGui.modals.extensionLibrary, + isRtl: state.locales.isRtl, locale: state.locales.locale, messages: state.locales.messages, toolboxXML: state.scratchGui.toolbox.toolboxXML, From 98a3dacbbcdb57d6fc3464f8b4d087f962770623 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 6 Aug 2018 09:07:21 -0400 Subject: [PATCH 0621/1971] Merge pull request #2783 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180803171604 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 70d47dc260..6f7469c366 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1533301486", "scratch-l10n": "3.0.20180803171042", - "scratch-paint": "0.2.0-prerelease.20180802204251", + "scratch-paint": "0.2.0-prerelease.20180803171604", "scratch-render": "0.1.0-prerelease.20180724152606", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", From a720b6dc3d2e478948bdf706b2d225675b6c119c Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 8 Aug 2018 19:07:02 -0400 Subject: [PATCH 0622/1971] Merge pull request #2825 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180808223211 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6f7469c366..a97e7c4731 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1533301486", "scratch-l10n": "3.0.20180803171042", - "scratch-paint": "0.2.0-prerelease.20180803171604", + "scratch-paint": "0.2.0-prerelease.20180808223211", "scratch-render": "0.1.0-prerelease.20180724152606", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", From 1a6afda67d3bc73165671ffd55d3ff697dbd4d90 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 9 Aug 2018 10:33:19 -0400 Subject: [PATCH 0623/1971] Merge pull request #2827 from LLK/bump-render-vm bump render and vm together --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a97e7c4731..17e9de98c7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -99,10 +99,10 @@ "scratch-blocks": "0.1.0-prerelease.1533301486", "scratch-l10n": "3.0.20180803171042", "scratch-paint": "0.2.0-prerelease.20180808223211", - "scratch-render": "0.1.0-prerelease.20180724152606", + "scratch-render": "0.1.0-prerelease.20180808184135", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", - "scratch-vm": "0.2.0-prerelease.20180802142248", + "scratch-vm": "^0.2.0-prerelease.20180808205317", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.21.0", From 81b655e6c629cd179f54399d4f720bf88594da59 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 9 Aug 2018 11:08:38 -0400 Subject: [PATCH 0624/1971] Merge pull request #2794 from LLK/greenkeeper/postcss-import-12.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update postcss-import to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 17e9de98c7..88267be6a7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -70,7 +70,7 @@ "lodash.pick": "4.4.0", "minilog": "3.1.0", "mkdirp": "^0.5.1", - "postcss-import": "^11.0.0", + "postcss-import": "^12.0.0", "postcss-loader": "^2.1.4", "postcss-simple-vars": "^4.0.0", "prop-types": "^15.5.10", From 01dd1510add37afd02a80fb009a94af788c3c9d0 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 9 Aug 2018 11:10:28 -0400 Subject: [PATCH 0625/1971] Merge pull request #2804 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1533592374 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 88267be6a7..e20d96e330 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1533301486", + "scratch-blocks": "0.1.0-prerelease.1533592374", "scratch-l10n": "3.0.20180803171042", "scratch-paint": "0.2.0-prerelease.20180808223211", "scratch-render": "0.1.0-prerelease.20180808184135", From 5eb919c9f8c0194f99185389cc68ee12ad11d420 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 9 Aug 2018 11:46:10 -0400 Subject: [PATCH 0626/1971] Merge pull request #2830 from LLK/greenkeeper/style-loader-0.22.1 chore(package): update style-loader to version 0.22.1 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e20d96e330..060218605d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "scratch-vm": "^0.2.0-prerelease.20180808205317", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", - "style-loader": "^0.21.0", + "style-loader": "^0.22.1", "svg-to-image": "1.1.3", "text-encoding": "0.6.4", "to-style": "1.3.3", From 8202a39d7d1ca17db6e7eda51dde2ef822ecb02b Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 9 Aug 2018 11:46:27 -0400 Subject: [PATCH 0627/1971] Merge pull request #2829 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180809150043 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 060218605d..cff8ed2f15 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1533592374", "scratch-l10n": "3.0.20180803171042", - "scratch-paint": "0.2.0-prerelease.20180808223211", + "scratch-paint": "0.2.0-prerelease.20180809150043", "scratch-render": "0.1.0-prerelease.20180808184135", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", From 0d16e4b9c9b0ed85c34dfdf6f21951af390dcc24 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 9 Aug 2018 11:47:08 -0400 Subject: [PATCH 0628/1971] Merge pull request #2821 from LLK/greenkeeper/postcss-loader-3.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update postcss-loader to the latest version 🚀 --- packages/scratch-gui/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cff8ed2f15..8f483a4972 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -70,8 +70,9 @@ "lodash.pick": "4.4.0", "minilog": "3.1.0", "mkdirp": "^0.5.1", + "postcss-import": "^11.0.0", "postcss-import": "^12.0.0", - "postcss-loader": "^2.1.4", + "postcss-loader": "^3.0.0", "postcss-simple-vars": "^4.0.0", "prop-types": "^15.5.10", "raf": "^3.4.0", From 8489591b24d0f599c7ee1a5274204faa6433db7f Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 9 Aug 2018 13:48:13 -0400 Subject: [PATCH 0629/1971] Merge pull request #2833 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1533835159 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8f483a4972..7485a7a3af 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1533592374", + "scratch-blocks": "0.1.0-prerelease.1533835159", "scratch-l10n": "3.0.20180803171042", "scratch-paint": "0.2.0-prerelease.20180809150043", "scratch-render": "0.1.0-prerelease.20180808184135", From ac7289d05b3b9dc215d5aa559d3b70fb64cc7166 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 9 Aug 2018 15:37:03 -0400 Subject: [PATCH 0630/1971] Merge pull request #2836 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180809185222 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7485a7a3af..f06fed8d8e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -99,7 +99,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1533835159", "scratch-l10n": "3.0.20180803171042", - "scratch-paint": "0.2.0-prerelease.20180809150043", + "scratch-paint": "0.2.0-prerelease.20180809185222", "scratch-render": "0.1.0-prerelease.20180808184135", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", From f69efa55c7192b9c4cf2da5193e201d0642f9f05 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 9 Aug 2018 16:41:40 -0400 Subject: [PATCH 0631/1971] Merge pull request #2772 from ericrosenbaum/feature/wedo-steps Add device connection steps for Lego WeDo 2.0 --- .../scratch-gui/src/containers/blocks.jsx | 9 +++----- .../src/lib/libraries/extensions/index.jsx | 21 ++++++++++++++++++- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 44d52664e3..8a6bec5fd7 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -364,8 +364,10 @@ class Blocks extends React.Component { if (extension) { this.setState({connectionModal: { extensionId: extensionId, + useAutoScan: extension.useAutoScan, deviceImage: extension.deviceImage, smallDeviceImage: extension.smallDeviceImage, + deviceButtonImage: extension.deviceButtonImage, name: extension.name, connectingMessage: extension.connectingMessage, helpLink: extension.helpLink @@ -433,12 +435,7 @@ class Blocks extends React.Component { ) : null} {this.state.connectionModal ? ( ), featured: true, - disabled: true + disabled: true, + launchDeviceConnectionFlow: true, + useAutoScan: true, + deviceImage: wedoDeviceImage, + smallDeviceImage: wedoMenuImage, + deviceButtonImage: wedoButtonImage, + connectingMessage: ( + + ), + helpLink: 'https://scratch.mit.edu/wedo' + } ]; From 7de9d2cf7fd8f85f5783edd8ea6609269a2c6f96 Mon Sep 17 00:00:00 2001 From: kchadha Date: Thu, 9 Aug 2018 17:30:25 -0400 Subject: [PATCH 0632/1971] Merge pull request #2824 from kchadha/restore-sprite Restore deleted sprite. --- packages/scratch-gui/package.json | 2 +- .../src/components/menu-bar/menu-bar.jsx | 45 +++++++++++-------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f06fed8d8e..0d94daebd9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -103,7 +103,7 @@ "scratch-render": "0.1.0-prerelease.20180808184135", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", - "scratch-vm": "^0.2.0-prerelease.20180808205317", + "scratch-vm": "0.2.0-prerelease.20180809193416", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.22.1", diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 24117372b4..8355f8dfd4 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -14,6 +14,7 @@ import ProjectLoader from '../../containers/project-loader.jsx'; import Menu from '../../containers/menu.jsx'; import {MenuItem, MenuSection} from '../menu/menu.jsx'; import ProjectSaver from '../../containers/project-saver.jsx'; +import DeletionRestorer from '../../containers/deletion-restorer.jsx'; import TurboMode from '../../containers/turbo-mode.jsx'; import {openTipsLibrary} from '../../reducers/modals'; @@ -133,7 +134,8 @@ class MenuBar extends React.Component { constructor (props) { super(props); bindAll(this, [ - 'handleLanguageMouseUp' + 'handleLanguageMouseUp', + 'handleRestoreOption' ]); } handleLanguageMouseUp (e) { @@ -141,6 +143,12 @@ class MenuBar extends React.Component { this.props.onClickLanguage(e); } } + handleRestoreOption (restoreFun) { + return () => { + restoreFun(); + this.props.onRequestCloseEdit(); + }; + } render () { return ( @@ -279,24 +287,25 @@ class MenuBar extends React.Component { open={this.props.editMenuOpen} onRequestClose={this.props.onRequestCloseEdit} > - - - - - - - - + {(handleRestore, {restorable, deletedItem}) => ( + + {deletedItem === 'Sprite' ? + : + + } - + )} {(toggleTurboMode, {turboMode}) => ( From e43b61cb2cc426d9a55567c73ada33f8c67f6782 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Fri, 10 Aug 2018 09:56:33 -0400 Subject: [PATCH 0633/1971] Merge pull request #2839 from LLK/greenkeeper/scratch-l10n-3.0.20180810134928 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0d94daebd9..49272ebf8d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1533835159", - "scratch-l10n": "3.0.20180803171042", + "scratch-l10n": "3.0.20180810134928", "scratch-paint": "0.2.0-prerelease.20180809185222", "scratch-render": "0.1.0-prerelease.20180808184135", "scratch-storage": "0.5.1", From d28a33319e76c2b5c7730c20e3882f1a6f322219 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Fri, 10 Aug 2018 10:30:30 -0400 Subject: [PATCH 0634/1971] Merge pull request #2841 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1533910749 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.153… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 49272ebf8d..45068c4f5c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1533835159", + "scratch-blocks": "0.1.0-prerelease.1533910749", "scratch-l10n": "3.0.20180810134928", "scratch-paint": "0.2.0-prerelease.20180809185222", "scratch-render": "0.1.0-prerelease.20180808184135", From edd9d7d488998597f3c5ae48b25208d697cbe055 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 13 Aug 2018 11:32:04 -0400 Subject: [PATCH 0635/1971] Merge pull request #2855 from fsih/removeDupeDep Remove duplicate dependency --- packages/scratch-gui/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 45068c4f5c..9791904c4b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -70,7 +70,6 @@ "lodash.pick": "4.4.0", "minilog": "3.1.0", "mkdirp": "^0.5.1", - "postcss-import": "^11.0.0", "postcss-import": "^12.0.0", "postcss-loader": "^3.0.0", "postcss-simple-vars": "^4.0.0", From f63fd0f1e3d66151abfe42f4df744224df8a7ab3 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 13 Aug 2018 13:27:39 -0400 Subject: [PATCH 0636/1971] Merge pull request #2856 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180813162447 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9791904c4b..e9f160fbeb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1533910749", "scratch-l10n": "3.0.20180810134928", - "scratch-paint": "0.2.0-prerelease.20180809185222", + "scratch-paint": "0.2.0-prerelease.20180813162447", "scratch-render": "0.1.0-prerelease.20180808184135", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", From 988bdd2e579067af59bdec0d06e755fe232423c7 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 13 Aug 2018 16:52:44 -0400 Subject: [PATCH 0637/1971] Merge pull request #2859 from LLK/smoke [Develop] Release for August 10 --- packages/scratch-gui/package.json | 2 +- .../src/components/menu-bar/menu-bar.jsx | 44 ++++++++++--------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e9f160fbeb..1afe4a7a0d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -99,7 +99,7 @@ "scratch-blocks": "0.1.0-prerelease.1533910749", "scratch-l10n": "3.0.20180810134928", "scratch-paint": "0.2.0-prerelease.20180813162447", - "scratch-render": "0.1.0-prerelease.20180808184135", + "scratch-render": "0.1.0-prerelease.20180811013828", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", "scratch-vm": "0.2.0-prerelease.20180809193416", diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 8355f8dfd4..fcb2b51de0 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -143,9 +143,9 @@ class MenuBar extends React.Component { this.props.onClickLanguage(e); } } - handleRestoreOption (restoreFun) { + handleRestoreOption (restoreFun /* eslint-disable-line no-unused-vars */) { return () => { - restoreFun(); + // restoreFun(); TODO re-enable this when validation issues are fixed this.props.onRequestCloseEdit(); }; } @@ -287,25 +287,27 @@ class MenuBar extends React.Component { open={this.props.editMenuOpen} onRequestClose={this.props.onRequestCloseEdit} > - {(handleRestore, {restorable, deletedItem}) => ( - - {deletedItem === 'Sprite' ? - : - - } - - )} + + {(handleRestore, {restorable /* eslint-disable-line no-unused-vars, max-len */, deletedItem}) => ( + + {deletedItem === 'Sprite' ? + : + + } + + )} + {(toggleTurboMode, {turboMode}) => ( From be8dbc34225c9fd02817a213b9de3bce214a6202 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 14 Aug 2018 15:39:21 -0400 Subject: [PATCH 0638/1971] Merge pull request #2857 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1534184042 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1afe4a7a0d..f10dd1a97d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1533910749", + "scratch-blocks": "0.1.0-prerelease.1534184042", "scratch-l10n": "3.0.20180810134928", "scratch-paint": "0.2.0-prerelease.20180813162447", "scratch-render": "0.1.0-prerelease.20180811013828", From a6c8fcf5fa7595ab1ea79c5b30a3ca841cce0982 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Tue, 14 Aug 2018 17:22:22 -0400 Subject: [PATCH 0639/1971] Merge pull request #2843 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20180810161034 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f10dd1a97d..5d346a4c97 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180811013828", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", - "scratch-vm": "0.2.0-prerelease.20180809193416", + "scratch-vm": "0.2.0-prerelease.20180810161034", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.22.1", From 139ed52e987236962b7276d14ac1ee7d3d35dd1a Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 16 Aug 2018 09:52:06 -0400 Subject: [PATCH 0640/1971] Merge pull request #2876 from LLK/hotfix/new-feedback-link [Develop] Update Give Feedback link --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index fcb2b51de0..28530131fb 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -396,7 +396,7 @@ class MenuBar extends React.Component {
From 8c2f1c65cc46096afd4193401f50f656d055eae0 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 16 Aug 2018 13:30:13 -0400 Subject: [PATCH 0641/1971] Merge pull request #2869 from chrisgarrity/feature/2757-menubar Handle RTL in menubar --- .../src/components/menu-bar/menu-bar.jsx | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 28530131fb..0be11d730e 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -91,10 +91,11 @@ MenuBarItemTooltip.propTypes = { place: PropTypes.oneOf(['top', 'bottom', 'left', 'right']) }; -const MenuItemTooltip = ({id, children, className}) => ( +const MenuItemTooltip = ({id, isRtl, children, className}) => ( @@ -105,7 +106,8 @@ const MenuItemTooltip = ({id, children, className}) => ( MenuItemTooltip.propTypes = { children: PropTypes.node, className: PropTypes.string, - id: PropTypes.string + id: PropTypes.string, + isRtl: PropTypes.bool }; const MenuBarMenu = ({ @@ -189,6 +191,7 @@ class MenuBar extends React.Component {
@@ -211,9 +214,13 @@ class MenuBar extends React.Component {
- + - + - + - + {(handleRestore, {restorable /* eslint-disable-line no-unused-vars, max-len */, deletedItem}) => (
({ fileMenuOpen: fileMenuOpen(state), editMenuOpen: editMenuOpen(state), + isRtl: state.locales.isRtl, languageMenuOpen: languageMenuOpen(state) }); From eccaaa6f4bcda0164d002859df8603906ffec161 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 16 Aug 2018 16:51:16 -0400 Subject: [PATCH 0642/1971] Merge pull request #2890 from kchadha/revert-disable-restore-sprite Revert "Merge pull request #2845 from kchadha/disable-sprite-restore" --- .../src/components/menu-bar/menu-bar.jsx | 47 +++++++++---------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 0be11d730e..1156241108 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -145,9 +145,9 @@ class MenuBar extends React.Component { this.props.onClickLanguage(e); } } - handleRestoreOption (restoreFun /* eslint-disable-line no-unused-vars */) { + handleRestoreOption (restoreFun) { return () => { - // restoreFun(); TODO re-enable this when validation issues are fixed + restoreFun(); this.props.onRequestCloseEdit(); }; } @@ -301,30 +301,25 @@ class MenuBar extends React.Component { place={this.props.isRtl ? 'left' : 'right'} onRequestClose={this.props.onRequestCloseEdit} > - - {(handleRestore, {restorable /* eslint-disable-line no-unused-vars, max-len */, deletedItem}) => ( - - {deletedItem === 'Sprite' ? - : - - } - - )} - + {(handleRestore, {restorable, deletedItem}) => ( + + {deletedItem === 'Sprite' ? + : + + } + + )} {(toggleTurboMode, {turboMode}) => ( From 7fb98edb89625b3a17c2e9b2d29b4965d86702e7 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 17 Aug 2018 08:40:51 -0400 Subject: [PATCH 0643/1971] Merge pull request #2886 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1534442955 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5d346a4c97..b45f3dde19 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1534184042", + "scratch-blocks": "0.1.0-prerelease.1534442955", "scratch-l10n": "3.0.20180810134928", "scratch-paint": "0.2.0-prerelease.20180813162447", "scratch-render": "0.1.0-prerelease.20180811013828", From b0674ee266bac903fb8ef1d74a58254ce8baaf3b Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 17 Aug 2018 08:41:17 -0400 Subject: [PATCH 0644/1971] Merge pull request #2893 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20180816213529 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2018081… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b45f3dde19..2a6bb7b348 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180811013828", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", - "scratch-vm": "0.2.0-prerelease.20180810161034", + "scratch-vm": "0.2.0-prerelease.20180816213529", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.22.1", From ae84863cc6b704a64efedce1d8c9bca849b83c98 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 17 Aug 2018 08:41:25 -0400 Subject: [PATCH 0645/1971] Merge pull request #2894 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180816205442 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2a6bb7b348..dc814bbda3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1534442955", "scratch-l10n": "3.0.20180810134928", - "scratch-paint": "0.2.0-prerelease.20180813162447", + "scratch-paint": "0.2.0-prerelease.20180816205442", "scratch-render": "0.1.0-prerelease.20180811013828", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", From 9233c9877d83ff6b846c98ab51732c6370720356 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 17 Aug 2018 08:44:28 -0400 Subject: [PATCH 0646/1971] Merge pull request #2892 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20180817005452 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index dc814bbda3..6f8635b759 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -101,7 +101,7 @@ "scratch-paint": "0.2.0-prerelease.20180816205442", "scratch-render": "0.1.0-prerelease.20180811013828", "scratch-storage": "0.5.1", - "scratch-svg-renderer": "0.2.0-prerelease.20180712223402", + "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", "scratch-vm": "0.2.0-prerelease.20180816213529", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From acd3296676c69e44de65747f7d200758fa94db99 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 17 Aug 2018 10:28:06 -0400 Subject: [PATCH 0647/1971] Merge pull request #2897 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1534513944 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.153… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6f8635b759..5804280392 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1534442955", + "scratch-blocks": "0.1.0-prerelease.1534513944", "scratch-l10n": "3.0.20180810134928", "scratch-paint": "0.2.0-prerelease.20180816205442", "scratch-render": "0.1.0-prerelease.20180811013828", From c2fdfb8d028e687011aba6946968cf4834ac6f8d Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 17 Aug 2018 10:29:51 -0400 Subject: [PATCH 0648/1971] Merge pull request #2896 from LLK/greenkeeper/scratch-l10n-3.0.20180817135616 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5804280392..f1ddf3c3ef 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1534513944", - "scratch-l10n": "3.0.20180810134928", + "scratch-l10n": "3.0.20180817135616", "scratch-paint": "0.2.0-prerelease.20180816205442", "scratch-render": "0.1.0-prerelease.20180811013828", "scratch-storage": "0.5.1", From 0dca9b2d860ebcb84d11ce648773979e1a0d8d76 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 17 Aug 2018 10:36:49 -0400 Subject: [PATCH 0649/1971] Merge pull request #2898 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20180817143311 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f1ddf3c3ef..c18f84fc94 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180811013828", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", - "scratch-vm": "0.2.0-prerelease.20180816213529", + "scratch-vm": "0.2.0-prerelease.20180817143311", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.22.1", From baf4c0c4dc7935fa0b433a72a60e805acfb074ac Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 20 Aug 2018 10:39:07 -0400 Subject: [PATCH 0650/1971] Merge pull request #2920 from LLK/revert-vm-setxy [Develop] Roll back x/y rounding --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c18f84fc94..f1ddf3c3ef 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180811013828", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", - "scratch-vm": "0.2.0-prerelease.20180817143311", + "scratch-vm": "0.2.0-prerelease.20180816213529", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.22.1", From 55db8db446ec745cf8f6566f3a27c9b4a7d7c5e4 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 20 Aug 2018 11:15:03 -0400 Subject: [PATCH 0651/1971] Merge pull request #2922 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20180820150913 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2018082… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f1ddf3c3ef..d4a31020bb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180811013828", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", - "scratch-vm": "0.2.0-prerelease.20180816213529", + "scratch-vm": "0.2.0-prerelease.20180820150913", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.22.1", From 2652c8089c236ae81e3676ae441d83b0cd018d1e Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Mon, 20 Aug 2018 17:18:49 -0400 Subject: [PATCH 0652/1971] Merge pull request #2879 from chrisgarrity/issue/2658-lang-select Update language selection menu --- .../src/components/menu-bar/menu-bar.jsx | 44 +++++-------------- 1 file changed, 12 insertions(+), 32 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 1156241108..67e026edda 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -165,39 +165,19 @@ class MenuBar extends React.Component { />
- {/* @TODO: remove coming soon tooltip wrapper https://github.com/LLK/scratch-gui/issues/2664 */} - -
- - -
- - - - -
+
+ + +
+
Date: Tue, 21 Aug 2018 15:29:09 -0400 Subject: [PATCH 0653/1971] Merge pull request #2935 from kchadha/restore-sound Restore the last deleted sound. --- .../src/components/menu-bar/menu-bar.jsx | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 67e026edda..6edc06043f 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -137,7 +137,8 @@ class MenuBar extends React.Component { super(props); bindAll(this, [ 'handleLanguageMouseUp', - 'handleRestoreOption' + 'handleRestoreOption', + 'restoreOptionMessage' ]); } handleLanguageMouseUp (e) { @@ -151,6 +152,30 @@ class MenuBar extends React.Component { this.props.onRequestCloseEdit(); }; } + restoreOptionMessage (deletedItem) { + switch (deletedItem) { + case 'Sprite': + return (); + case 'Sound': + return (); + case 'Costume': + default: { + return (); + } + } + } render () { return ( @@ -286,18 +311,7 @@ class MenuBar extends React.Component { className={classNames({[styles.disabled]: !restorable})} onClick={this.handleRestoreOption(handleRestore)} > - {deletedItem === 'Sprite' ? - : - - } + {this.restoreOptionMessage(deletedItem)} )} From 0b3b007a6ab553d04d666215f58a0f16f383a562 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 22 Aug 2018 10:28:58 -0400 Subject: [PATCH 0654/1971] Merge pull request #2950 from kchadha/restore-costume Restore deleted costume --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 6edc06043f..5eb4ad3312 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -167,6 +167,11 @@ class MenuBar extends React.Component { id="gui.menuBar.restoreSound" />); case 'Costume': + return (); default: { return ( Date: Thu, 23 Aug 2018 11:29:47 -0400 Subject: [PATCH 0655/1971] Merge pull request #2963 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20180822215310 chore(package): update scratch-vm to version 0.2.0-prerelease.20180822215310 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d4a31020bb..ca19402c43 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180811013828", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", - "scratch-vm": "0.2.0-prerelease.20180820150913", + "scratch-vm": "0.2.0-prerelease.20180822215310", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.22.1", From a2c225b1f9855ed5fde11106bf6e9f8b7614dae8 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 23 Aug 2018 11:30:27 -0400 Subject: [PATCH 0656/1971] Merge pull request #2928 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20180820154012 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ca19402c43..744ecd5173 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -99,7 +99,7 @@ "scratch-blocks": "0.1.0-prerelease.1534513944", "scratch-l10n": "3.0.20180817135616", "scratch-paint": "0.2.0-prerelease.20180816205442", - "scratch-render": "0.1.0-prerelease.20180811013828", + "scratch-render": "0.1.0-prerelease.20180820154012", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", "scratch-vm": "0.2.0-prerelease.20180822215310", From cbad41ba9b32047961d38106f6991f13ee1966dc Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 23 Aug 2018 11:52:41 -0400 Subject: [PATCH 0657/1971] Merge pull request #2965 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1534979688 chore(package): update scratch-blocks to version 0.1.0-prerelease.1534979688 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 744ecd5173..42bbf8da95 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1534513944", + "scratch-blocks": "0.1.0-prerelease.1534979688", "scratch-l10n": "3.0.20180817135616", "scratch-paint": "0.2.0-prerelease.20180816205442", "scratch-render": "0.1.0-prerelease.20180820154012", From f20389706e423eaf6aa5a1c64c3391ed1ea122d6 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 23 Aug 2018 13:57:08 -0400 Subject: [PATCH 0658/1971] Merge pull request #2962 from chrisgarrity/issue/fix-warning Fix warnings --- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index b2256b82a4..67e70f9001 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -89,6 +89,10 @@ const vmListenerHOC = function (WrappedComponent) { onKeyUp, onMonitorsUpdate, onTargetsUpdate, + onProjectRunStart, + onProjectRunStop, + onTurboModeOff, + onTurboModeOn, /* eslint-enable no-unused-vars */ ...props } = this.props; From 7b7ef736a817c53fee08d8b96498dabefb6a84e4 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 23 Aug 2018 14:06:08 -0400 Subject: [PATCH 0659/1971] Merge pull request #2966 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180822171824 chore(package): update scratch-paint to version 0.2.0-prerelease.20180822171824 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 42bbf8da95..ee9e186f12 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1534979688", "scratch-l10n": "3.0.20180817135616", - "scratch-paint": "0.2.0-prerelease.20180816205442", + "scratch-paint": "0.2.0-prerelease.20180822171824", "scratch-render": "0.1.0-prerelease.20180820154012", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", From faf7f6cacf44c51d6e8200b27382f2de8f9614d2 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 23 Aug 2018 15:44:00 -0400 Subject: [PATCH 0660/1971] Merge pull request #2964 from LLK/greenkeeper/scratch-l10n-3.0.20180823152851 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ee9e186f12..24345d73f2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1534979688", - "scratch-l10n": "3.0.20180817135616", + "scratch-l10n": "3.0.20180823152851", "scratch-paint": "0.2.0-prerelease.20180822171824", "scratch-render": "0.1.0-prerelease.20180820154012", "scratch-storage": "0.5.1", From 778c861a952967ec5aafff55a6db7285b1e71a54 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 23 Aug 2018 17:04:15 -0400 Subject: [PATCH 0661/1971] Merge pull request #2969 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20180823181308 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 24345d73f2..62e7cc57fd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180820154012", "scratch-storage": "0.5.1", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", - "scratch-vm": "0.2.0-prerelease.20180822215310", + "scratch-vm": "0.2.0-prerelease.20180823181308", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.22.1", From 7dc3cba895174425ae1975ced65f75410a31473f Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 24 Aug 2018 09:13:46 -0400 Subject: [PATCH 0662/1971] Merge pull request #2974 from LLK/smoke-wedo Enable WeDo extension --- packages/scratch-gui/src/lib/libraries/extensions/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index 0bd3b36ee9..edd4fa512b 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -158,7 +158,7 @@ export default [ /> ), featured: true, - disabled: true, + disabled: false, launchDeviceConnectionFlow: true, useAutoScan: true, deviceImage: wedoDeviceImage, From b8ea58aa70f03e35b7a3c9411ec354185179a00f Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 24 Aug 2018 09:21:32 -0400 Subject: [PATCH 0663/1971] Merge pull request #2972 from LLK/greenkeeper/scratch-storage-1.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-storage to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 62e7cc57fd..c13ceff061 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -100,7 +100,7 @@ "scratch-l10n": "3.0.20180823152851", "scratch-paint": "0.2.0-prerelease.20180822171824", "scratch-render": "0.1.0-prerelease.20180820154012", - "scratch-storage": "0.5.1", + "scratch-storage": "1.0.0", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", "scratch-vm": "0.2.0-prerelease.20180823181308", "selenium-webdriver": "3.6.0", From 0168e8ad7ba027557da13c93e2f2cbd6d6a348e1 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 24 Aug 2018 09:21:51 -0400 Subject: [PATCH 0664/1971] Merge pull request #2973 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180823231354 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c13ceff061..bd091d46bb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1534979688", "scratch-l10n": "3.0.20180823152851", - "scratch-paint": "0.2.0-prerelease.20180822171824", + "scratch-paint": "0.2.0-prerelease.20180823231354", "scratch-render": "0.1.0-prerelease.20180820154012", "scratch-storage": "1.0.0", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", From 1379afbaefa7c3c0ae4fcebb31faf02b34ec18fc Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 24 Aug 2018 09:42:19 -0400 Subject: [PATCH 0665/1971] Merge pull request #2975 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1535116879 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index bd091d46bb..efba2b92b3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1534979688", + "scratch-blocks": "0.1.0-prerelease.1535116879", "scratch-l10n": "3.0.20180823152851", "scratch-paint": "0.2.0-prerelease.20180823231354", "scratch-render": "0.1.0-prerelease.20180820154012", From 4e949e54ec8144921c89822eeb4e07cd1b7c923b Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 24 Aug 2018 09:47:09 -0400 Subject: [PATCH 0666/1971] Merge pull request #2976 from LLK/greenkeeper/scratch-l10n-3.0.20180824134256 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index efba2b92b3..ac0f158e50 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1535116879", - "scratch-l10n": "3.0.20180823152851", + "scratch-l10n": "3.0.20180824134256", "scratch-paint": "0.2.0-prerelease.20180823231354", "scratch-render": "0.1.0-prerelease.20180820154012", "scratch-storage": "1.0.0", From 81f69a7569fce8ada9b5c2c89741f639ab7075ab Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 24 Aug 2018 09:59:19 -0400 Subject: [PATCH 0667/1971] Merge pull request #2977 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20180824135031 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ac0f158e50..c60f586c0e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180820154012", "scratch-storage": "1.0.0", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", - "scratch-vm": "0.2.0-prerelease.20180823181308", + "scratch-vm": "0.2.0-prerelease.20180824135031", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.22.1", From a899c0bbf1dbc5d75a25072ea9b50b805a4d949a Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 24 Aug 2018 10:25:52 -0400 Subject: [PATCH 0668/1971] Merge pull request #2978 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20180824141819 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c60f586c0e..c43fb30c03 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -99,7 +99,7 @@ "scratch-blocks": "0.1.0-prerelease.1535116879", "scratch-l10n": "3.0.20180824134256", "scratch-paint": "0.2.0-prerelease.20180823231354", - "scratch-render": "0.1.0-prerelease.20180820154012", + "scratch-render": "0.1.0-prerelease.20180824141819", "scratch-storage": "1.0.0", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", "scratch-vm": "0.2.0-prerelease.20180824135031", From a3b22df1e32a0671ec99581c87d982c92ecb2afe Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 28 Aug 2018 15:52:50 -0400 Subject: [PATCH 0669/1971] Merge pull request #2941 from apple502j/patch-5 Make Scratch logo a link --- .../src/components/menu-bar/menu-bar.jsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 5eb4ad3312..5a9ca128b6 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -187,12 +187,18 @@ class MenuBar extends React.Component {
Date: Wed, 29 Aug 2018 16:16:54 -0400 Subject: [PATCH 0670/1971] chore(package): update scratch-paint to version 0.2.0-prerelease.20180829193410 (#3027) --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c43fb30c03..befae1b6fe 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1535116879", "scratch-l10n": "3.0.20180824134256", - "scratch-paint": "0.2.0-prerelease.20180823231354", + "scratch-paint": "0.2.0-prerelease.20180829193410", "scratch-render": "0.1.0-prerelease.20180824141819", "scratch-storage": "1.0.0", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", From a1e1059d5ea93cedf9c3835c8c815f5cbfabb1c6 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 29 Aug 2018 21:28:02 -0400 Subject: [PATCH 0671/1971] Merge pull request #3021 from rschamp/save-now Save now --- packages/scratch-gui/package.json | 2 +- .../src/components/menu-bar/menu-bar.jsx | 66 ++++++++++++++----- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index befae1b6fe..9ac8bc5957 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -100,7 +100,7 @@ "scratch-l10n": "3.0.20180824134256", "scratch-paint": "0.2.0-prerelease.20180829193410", "scratch-render": "0.1.0-prerelease.20180824141819", - "scratch-storage": "1.0.0", + "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", "scratch-vm": "0.2.0-prerelease.20180824135031", "selenium-webdriver": "3.6.0", diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 5a9ca128b6..b1ccd29412 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -138,8 +138,10 @@ class MenuBar extends React.Component { bindAll(this, [ 'handleLanguageMouseUp', 'handleRestoreOption', + 'handleCloseFileMenuAndThen', 'restoreOptionMessage' ]); + this.state = {projectSaveInProgress: false}; } handleLanguageMouseUp (e) { if (!this.props.languageMenuOpen) { @@ -152,6 +154,24 @@ class MenuBar extends React.Component { this.props.onRequestCloseEdit(); }; } + handleUpdateProject (updateFun) { + return () => { + this.props.onRequestCloseFile(); + this.setState({projectSaveInProgress: true}, + () => { + updateFun().then(() => { + this.setState({projectSaveInProgress: false}); + }); + } + ); + }; + } + handleCloseFileMenuAndThen (fn) { + return () => { + this.props.onRequestCloseFile(); + fn(); + }; + } restoreOptionMessage (deletedItem) { switch (deletedItem) { case 'Sprite': @@ -182,8 +202,19 @@ class MenuBar extends React.Component { } } render () { + const saveNowMessage = ( + + ); return ( - +
@@ -246,18 +277,20 @@ class MenuBar extends React.Component { - - - - - + {(saveProject, updateProject) => ( + this.props.canUpdateProject ? ( + + {saveNowMessage} + + ) : ( + + {saveNowMessage} + + ) + )} )} - {(saveProject, saveProps) => ( + {saveProject => ( ({ + canUpdateProject: typeof (state.session && state.session.session && state.session.session.user) !== 'undefined', fileMenuOpen: fileMenuOpen(state), editMenuOpen: editMenuOpen(state), isRtl: state.locales.isRtl, From 61057f105e63279ac2b7cee6f7b60c66d91ba374 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 30 Aug 2018 10:22:42 -0400 Subject: [PATCH 0672/1971] Merge pull request #3037 from thisandagain/gk/20180830 Update enzyme, paint, vm, style-loader, and webpack-cli packages --- packages/scratch-gui/package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9ac8bc5957..a5d5bf5d79 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -46,8 +46,8 @@ "copy-webpack-plugin": "^4.5.1", "core-js": "2.5.7", "css-loader": "^1.0.0", - "enzyme": "^3.1.0", - "enzyme-adapter-react-16": "1.1.1", + "enzyme": "^3.5.0", + "enzyme-adapter-react-16": "1.3.0", "es6-object-assign": "1.1.0", "eslint": "^5.0.1", "eslint-config-scratch": "^5.0.0", @@ -98,14 +98,14 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1535116879", "scratch-l10n": "3.0.20180824134256", - "scratch-paint": "0.2.0-prerelease.20180829193410", + "scratch-paint": "0.2.0-prerelease.20180829211422", "scratch-render": "0.1.0-prerelease.20180824141819", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", - "scratch-vm": "0.2.0-prerelease.20180824135031", + "scratch-vm": "0.2.0-prerelease.20180829214449", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", - "style-loader": "^0.22.1", + "style-loader": "^0.23.0", "svg-to-image": "1.1.3", "text-encoding": "0.6.4", "to-style": "1.3.3", @@ -113,7 +113,7 @@ "wav-encoder": "1.3.0", "web-audio-test-api": "^0.5.2", "webpack": "^4.6.0", - "webpack-cli": "^2.0.15", + "webpack-cli": "^3.1.0", "webpack-dev-server": "^3.1.3", "xhr": "2.5.0" }, From dc59d25f3d36319eda10abc7007ae229ac85df90 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 30 Aug 2018 15:59:12 -0400 Subject: [PATCH 0673/1971] Merge pull request #3039 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20180830155110 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a5d5bf5d79..40e697187a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180824141819", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", - "scratch-vm": "0.2.0-prerelease.20180829214449", + "scratch-vm": "0.2.0-prerelease.20180830155110", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 542d3c4769310be54d456b806f7fe3ed6ded0ce7 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 30 Aug 2018 16:00:53 -0400 Subject: [PATCH 0674/1971] Merge pull request #3043 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1535652659 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 40e697187a..0d3ea5d34c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1535116879", + "scratch-blocks": "0.1.0-prerelease.1535652659", "scratch-l10n": "3.0.20180824134256", "scratch-paint": "0.2.0-prerelease.20180829211422", "scratch-render": "0.1.0-prerelease.20180824141819", From 6e6ebe1804de6901774d9ed9ba990030ba28377a Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 30 Aug 2018 18:23:09 -0400 Subject: [PATCH 0675/1971] chore(package): update scratch-paint to version 0.2.0-prerelease.20180830215546 (#3050) Closes #3044 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0d3ea5d34c..8b95ace44f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1535652659", "scratch-l10n": "3.0.20180824134256", - "scratch-paint": "0.2.0-prerelease.20180829211422", + "scratch-paint": "0.2.0-prerelease.20180830215546", "scratch-render": "0.1.0-prerelease.20180824141819", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", From 17617c5c1e536c6d3b8192467ea556b8aee2edc2 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Fri, 31 Aug 2018 06:27:50 -0400 Subject: [PATCH 0676/1971] Merge pull request #3047 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1535662135 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8b95ace44f..cb5424d208 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1535652659", + "scratch-blocks": "0.1.0-prerelease.1535662135", "scratch-l10n": "3.0.20180824134256", "scratch-paint": "0.2.0-prerelease.20180830215546", "scratch-render": "0.1.0-prerelease.20180824141819", From 3c35ae8878caffd00a48698b23e9ddd5f5bd7427 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Fri, 31 Aug 2018 06:29:48 -0400 Subject: [PATCH 0677/1971] Merge pull request #3048 from LLK/greenkeeper/scratch-l10n-3.0.20180830210150 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cb5424d208..5a568650d5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1535662135", - "scratch-l10n": "3.0.20180824134256", + "scratch-l10n": "3.0.20180830210150", "scratch-paint": "0.2.0-prerelease.20180830215546", "scratch-render": "0.1.0-prerelease.20180824141819", "scratch-storage": "1.0.2", From 75491b7d186422480375a38a00b2db6b3f3e9163 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 31 Aug 2018 12:56:11 -0400 Subject: [PATCH 0678/1971] Add zoom level (#3054) --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5a568650d5..ea76ae1fb2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1535662135", "scratch-l10n": "3.0.20180830210150", - "scratch-paint": "0.2.0-prerelease.20180830215546", + "scratch-paint": "0.2.0-prerelease.20180831161157", "scratch-render": "0.1.0-prerelease.20180824141819", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", From d45051530c045621ac8347e727fca2d95dc7ff4f Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 31 Aug 2018 13:08:55 -0400 Subject: [PATCH 0679/1971] Update to version that removes keyboard shortcuts (#3055) --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ea76ae1fb2..3f09a6f9bd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1535662135", "scratch-l10n": "3.0.20180830210150", - "scratch-paint": "0.2.0-prerelease.20180831161157", + "scratch-paint": "0.2.0-prerelease.20180831163506", "scratch-render": "0.1.0-prerelease.20180824141819", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", From 38845c709c14384b8976e87608c1d10b5a939edf Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 31 Aug 2018 14:01:05 -0400 Subject: [PATCH 0680/1971] Update paint (#3056) --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3f09a6f9bd..82d9dc575e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1535662135", "scratch-l10n": "3.0.20180830210150", - "scratch-paint": "0.2.0-prerelease.20180831163506", + "scratch-paint": "0.2.0-prerelease.20180831175055", "scratch-render": "0.1.0-prerelease.20180824141819", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", From 9e60822a5a9d0d9303b1fe18ff10eb8d75ff90cd Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 5 Sep 2018 21:29:18 -0400 Subject: [PATCH 0681/1971] chore(package): update scratch-paint to version 0.2.0-prerelease.20180905220723 (#3075) Closes #3071 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 82d9dc575e..c19a507f3d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1535662135", "scratch-l10n": "3.0.20180830210150", - "scratch-paint": "0.2.0-prerelease.20180831175055", + "scratch-paint": "0.2.0-prerelease.20180905220723", "scratch-render": "0.1.0-prerelease.20180824141819", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", From 3b23c4f68c41df4b5eceddfb6565dd17fe318300 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 6 Sep 2018 09:23:36 -0400 Subject: [PATCH 0682/1971] Merge pull request #3045 from LLK/greenkeeper/react-tabs-2.3.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-tabs to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c19a507f3d..51591dcdc9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -87,7 +87,7 @@ "react-redux": "5.0.7", "react-responsive": "5.0.0", "react-style-proptype": "3.2.1", - "react-tabs": "2.2.2", + "react-tabs": "2.3.0", "react-test-renderer": "16.2.0", "react-tooltip": "3.6.1", "react-virtualized": "9.20.1", From a02baed0077e784d0b5055b5d45327859a2b2a6d Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 6 Sep 2018 09:47:30 -0400 Subject: [PATCH 0683/1971] Merge pull request #3078 from LLK/greenkeeper/react-tooltip-3.8.0 chore(package): update react-tooltip to version 3.8.0 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 51591dcdc9..ecb1015248 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -89,7 +89,7 @@ "react-style-proptype": "3.2.1", "react-tabs": "2.3.0", "react-test-renderer": "16.2.0", - "react-tooltip": "3.6.1", + "react-tooltip": "3.8.0", "react-virtualized": "9.20.1", "redux": "3.7.2", "redux-mock-store": "^1.2.3", From 5f1df74b15ecc943aedc4b108201889a4bff3c41 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 6 Sep 2018 11:03:21 -0400 Subject: [PATCH 0684/1971] Merge pull request #3080 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20180906133035 chore(package): update scratch-vm to version 0.2.0-prerelease.20180906133035 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ecb1015248..d3585914f7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180824141819", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", - "scratch-vm": "0.2.0-prerelease.20180830155110", + "scratch-vm": "0.2.0-prerelease.20180906133035", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From a48016834109a92631bbeff4be7b0df795b98736 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Fri, 7 Sep 2018 10:57:16 -0400 Subject: [PATCH 0685/1971] chore(package): update scratch-svg-renderer to version 0.2.0-prerelease.20180907141232 (#3091) --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d3585914f7..28986970e7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -101,7 +101,7 @@ "scratch-paint": "0.2.0-prerelease.20180905220723", "scratch-render": "0.1.0-prerelease.20180824141819", "scratch-storage": "1.0.2", - "scratch-svg-renderer": "0.2.0-prerelease.20180817005452", + "scratch-svg-renderer": "0.2.0-prerelease.20180907141232", "scratch-vm": "0.2.0-prerelease.20180906133035", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 00ec3f320fb15c66e53a9dbc0a397f32e0df19cd Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Fri, 7 Sep 2018 12:34:51 -0400 Subject: [PATCH 0686/1971] chore(package): update scratch-paint to version 0.2.0-prerelease.20180907144642 (#3092) --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 28986970e7..d6e5eeff0d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1535662135", "scratch-l10n": "3.0.20180830210150", - "scratch-paint": "0.2.0-prerelease.20180905220723", + "scratch-paint": "0.2.0-prerelease.20180907144642", "scratch-render": "0.1.0-prerelease.20180824141819", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180907141232", From a3199d0541fd3e80e385ba286e31804c85045d95 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Fri, 7 Sep 2018 16:02:33 -0400 Subject: [PATCH 0687/1971] Merge pull request #3016 from benjiwheeler/rename_title make project title editable, both in standalone and www modes --- .../src/components/menu-bar/menu-bar.jsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index b1ccd29412..82b37b0b05 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -13,6 +13,7 @@ import LanguageSelector from '../../containers/language-selector.jsx'; import ProjectLoader from '../../containers/project-loader.jsx'; import Menu from '../../containers/menu.jsx'; import {MenuItem, MenuSection} from '../menu/menu.jsx'; +import ProjectTitleInput from './project-title-input.jsx'; import ProjectSaver from '../../containers/project-saver.jsx'; import DeletionRestorer from '../../containers/deletion-restorer.jsx'; import TurboMode from '../../containers/turbo-mode.jsx'; @@ -393,11 +394,12 @@ class MenuBar extends React.Component {
- - +
@@ -521,7 +523,8 @@ MenuBar.propTypes = { onRequestCloseEdit: PropTypes.func, onRequestCloseFile: PropTypes.func, onRequestCloseLanguage: PropTypes.func, - onSeeCommunity: PropTypes.func + onSeeCommunity: PropTypes.func, + onUpdateProjectTitle: PropTypes.func }; const mapStateToProps = state => ({ From 39348271bfd2189bd29d84e7922d988b9389a016 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Fri, 7 Sep 2018 16:04:53 -0400 Subject: [PATCH 0688/1971] chore(package): update scratch-render to version 0.1.0-prerelease.20180907144714 (#3093) --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d6e5eeff0d..145e10372c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -99,7 +99,7 @@ "scratch-blocks": "0.1.0-prerelease.1535662135", "scratch-l10n": "3.0.20180830210150", "scratch-paint": "0.2.0-prerelease.20180907144642", - "scratch-render": "0.1.0-prerelease.20180824141819", + "scratch-render": "0.1.0-prerelease.20180907144714", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180907141232", "scratch-vm": "0.2.0-prerelease.20180906133035", From 67dfc7f8fc073eb1d6cecfcf9cca6339f83fdc10 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 11 Sep 2018 11:54:51 -0400 Subject: [PATCH 0689/1971] Merge pull request #3072 from evhan55/refactor/hardware-extensions Refactor for hardware extensions --- packages/scratch-gui/package.json | 2 +- .../scratch-gui/src/containers/blocks.jsx | 8 ++--- .../src/lib/libraries/extensions/index.jsx | 34 +++++++++---------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 145e10372c..27d80a6410 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180907144714", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180907141232", - "scratch-vm": "0.2.0-prerelease.20180906133035", + "scratch-vm": "0.2.0-prerelease.20180907210751", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 8a6bec5fd7..499d776361 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -336,7 +336,7 @@ class Blocks extends React.Component { } handleCategorySelected (categoryId) { const extension = extensionData.find(ext => ext.extensionId === categoryId); - if (extension && extension.launchDeviceConnectionFlow) { + if (extension && extension.launchPeripheralConnectionFlow) { this.handleConnectionModalStart(categoryId); } @@ -365,9 +365,9 @@ class Blocks extends React.Component { this.setState({connectionModal: { extensionId: extensionId, useAutoScan: extension.useAutoScan, - deviceImage: extension.deviceImage, - smallDeviceImage: extension.smallDeviceImage, - deviceButtonImage: extension.deviceButtonImage, + peripheralImage: extension.peripheralImage, + smallPeripheralImage: extension.smallPeripheralImage, + peripheralButtonImage: extension.peripheralButtonImage, name: extension.name, connectingMessage: extension.connectingMessage, helpLink: extension.helpLink diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index edd4fa512b..1be61b7346 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -9,13 +9,13 @@ import microbitImage from './microbit.png'; import ev3Image from './ev3.png'; import wedoImage from './wedo.png'; -import microbitDeviceImage from './device-connection/microbit/microbit-illustration.svg'; -import microbitMenuImage from './device-connection/microbit/microbit-small.svg'; -import ev3DeviceImage from './device-connection/ev3/ev3-hub-illustration.svg'; -import ev3MenuImage from './device-connection/ev3/ev3-small.svg'; -import wedoDeviceImage from './device-connection/wedo/wedo-illustration.svg'; -import wedoMenuImage from './device-connection/wedo/wedo-small.svg'; -import wedoButtonImage from './device-connection/wedo/wedo-button-illustration.svg'; +import microbitPeripheralImage from './peripheral-connection/microbit/microbit-illustration.svg'; +import microbitMenuImage from './peripheral-connection/microbit/microbit-small.svg'; +import ev3PeripheralImage from './peripheral-connection/ev3/ev3-hub-illustration.svg'; +import ev3MenuImage from './peripheral-connection/ev3/ev3-small.svg'; +import wedoPeripheralImage from './peripheral-connection/wedo/wedo-illustration.svg'; +import wedoMenuImage from './peripheral-connection/wedo/wedo-small.svg'; +import wedoButtonImage from './peripheral-connection/wedo/wedo-button-illustration.svg'; export default [ { @@ -107,10 +107,10 @@ export default [ ), featured: true, disabled: false, - launchDeviceConnectionFlow: true, + launchPeripheralConnectionFlow: true, useAutoScan: false, - deviceImage: microbitDeviceImage, - smallDeviceImage: microbitMenuImage, + peripheralImage: microbitPeripheralImage, + smallPeripheralImage: microbitMenuImage, connectingMessage: ( Date: Tue, 11 Sep 2018 14:28:27 -0400 Subject: [PATCH 0690/1971] chore(package): update scratch-paint to version 0.2.0-prerelease.20180911144011 (#3107) --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 27d80a6410..ec8802927c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1535662135", "scratch-l10n": "3.0.20180830210150", - "scratch-paint": "0.2.0-prerelease.20180907144642", + "scratch-paint": "0.2.0-prerelease.20180911144011", "scratch-render": "0.1.0-prerelease.20180907144714", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180907141232", From 111b430ecb8bdb8de9cc4f420c5ca9af1f8233e3 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 12 Sep 2018 11:59:15 -0400 Subject: [PATCH 0691/1971] Merge pull request #3109 from ericrosenbaum/feature/remove-translate-branding Remove branding from translate extension --- .../scratch-gui/src/lib/libraries/extensions/index.jsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index 1be61b7346..4b5a4db07c 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -78,9 +78,9 @@ export default [ { name: ( ), extensionId: 'translate', @@ -88,8 +88,8 @@ export default [ description: ( ), featured: true From e5998b850ef5354bf3865dd18a393c19d04bb785 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 12 Sep 2018 17:40:16 -0400 Subject: [PATCH 0692/1971] Merge pull request #3057 from LLK/greenkeeper/react-style-proptype-3.2.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-style-proptype to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ec8802927c..4cfb2d06c3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -86,7 +86,7 @@ "react-popover": "0.5.7", "react-redux": "5.0.7", "react-responsive": "5.0.0", - "react-style-proptype": "3.2.1", + "react-style-proptype": "3.2.2", "react-tabs": "2.3.0", "react-test-renderer": "16.2.0", "react-tooltip": "3.8.0", From 7d9b1c6956335f772ec1871adec37792e7fde17a Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 12 Sep 2018 17:40:29 -0400 Subject: [PATCH 0693/1971] Merge pull request #3117 from LLK/greenkeeper/postcss-simple-vars-5.0.1 chore(package): update postcss-simple-vars to version 5.0.1 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4cfb2d06c3..b2aa5ac09c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -72,7 +72,7 @@ "mkdirp": "^0.5.1", "postcss-import": "^12.0.0", "postcss-loader": "^3.0.0", - "postcss-simple-vars": "^4.0.0", + "postcss-simple-vars": "^5.0.1", "prop-types": "^15.5.10", "raf": "^3.4.0", "raw-loader": "^0.5.1", From 09cee947e9716fa081f7f43905734c5fda6ecda7 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 12 Sep 2018 17:52:14 -0400 Subject: [PATCH 0694/1971] Merge pull request #3110 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20180911192651 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b2aa5ac09c..c3fa78f0b0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180907144714", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180907141232", - "scratch-vm": "0.2.0-prerelease.20180907210751", + "scratch-vm": "0.2.0-prerelease.20180912162301", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 1b7b4ec9fe6d8336121324f3921c825712986e49 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 12 Sep 2018 18:05:13 -0400 Subject: [PATCH 0695/1971] Merge pull request #3118 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180912173039 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c3fa78f0b0..0b4f5d6bda 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1535662135", "scratch-l10n": "3.0.20180830210150", - "scratch-paint": "0.2.0-prerelease.20180911144011", + "scratch-paint": "0.2.0-prerelease.20180912173039", "scratch-render": "0.1.0-prerelease.20180907144714", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180907141232", From a05f292c8bd748e46407710185a2c81fc7018d26 Mon Sep 17 00:00:00 2001 From: Linda Date: Wed, 12 Sep 2018 20:05:54 -0400 Subject: [PATCH 0696/1971] Merge pull request #3106 from LiFaytheGoblin/2641/make-title-field-width-dynamic Make title field shrink for smaller screens --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 82b37b0b05..897fedc3f3 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -393,12 +393,13 @@ class MenuBar extends React.Component {
-
+
From d8d4b132ced638806d3e4906d57d5485da469a01 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 12 Sep 2018 23:23:09 -0400 Subject: [PATCH 0697/1971] chore(package): update scratch-paint to version 0.2.0-prerelease.20180912222409 (#3123) Closes #3122 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0b4f5d6bda..58afa2ecce 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1535662135", "scratch-l10n": "3.0.20180830210150", - "scratch-paint": "0.2.0-prerelease.20180912173039", + "scratch-paint": "0.2.0-prerelease.20180912222409", "scratch-render": "0.1.0-prerelease.20180907144714", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180907141232", From 7c43e46a7e9ebb845faba7d486a475eff3bd0a3b Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 12 Sep 2018 23:46:43 -0400 Subject: [PATCH 0698/1971] Merge pull request #3099 from cwillisf/extension-reporters Attempt to get monitor labels from VM --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 58afa2ecce..7764fd51be 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180907144714", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180907141232", - "scratch-vm": "0.2.0-prerelease.20180912162301", + "scratch-vm": "0.2.0-prerelease.20180912222010", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From d5e2624cd390b4400091ce857d01e8849fadddb5 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 14 Sep 2018 16:59:04 -0400 Subject: [PATCH 0699/1971] Update Paint to fix crash --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7764fd51be..5b9e6cdc01 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1535662135", "scratch-l10n": "3.0.20180830210150", - "scratch-paint": "0.2.0-prerelease.20180912222409", + "scratch-paint": "0.2.0-prerelease.20180914201930", "scratch-render": "0.1.0-prerelease.20180907144714", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180907141232", From 5bdd635444f656fc8b5a8b94928ad834e3076959 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 19 Sep 2018 16:28:56 -0400 Subject: [PATCH 0700/1971] Merge pull request #3169 from rschamp/fix-canvas-hack Use new APIs for renderer and canvas --- packages/scratch-gui/package.json | 4 ++-- packages/scratch-gui/src/containers/stage.jsx | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5b9e6cdc01..29814a7040 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -99,10 +99,10 @@ "scratch-blocks": "0.1.0-prerelease.1535662135", "scratch-l10n": "3.0.20180830210150", "scratch-paint": "0.2.0-prerelease.20180914201930", - "scratch-render": "0.1.0-prerelease.20180907144714", + "scratch-render": "0.1.0-prerelease.20180918201144", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180907141232", - "scratch-vm": "0.2.0-prerelease.20180912222010", + "scratch-vm": "0.2.0-prerelease.20180918201814", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 1ab84412ee..0aa3f2a4b6 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -52,9 +52,9 @@ class Stage extends React.Component { colorInfo: null, question: null }; - if (this.props.vm.runtime.renderer) { - this.renderer = this.props.vm.runtime.renderer; - this.canvas = this.props.vm.runtime.renderer._gl.canvas; + if (this.props.vm.renderer) { + this.renderer = this.props.vm.renderer; + this.canvas = this.renderer.canvas; } else { this.canvas = document.createElement('canvas'); this.renderer = new Renderer(this.canvas); From 01b66a2a157a5f78cd753c2b2b0a9a006897ee85 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 20 Sep 2018 05:10:10 -0400 Subject: [PATCH 0701/1971] Merge pull request #3178 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1537303399 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 29814a7040..f722a4ee4a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1535662135", + "scratch-blocks": "0.1.0-prerelease.1537303399", "scratch-l10n": "3.0.20180830210150", "scratch-paint": "0.2.0-prerelease.20180914201930", "scratch-render": "0.1.0-prerelease.20180918201144", From d0be67e1f6dcc7d622ae7da95f9c893ca9598941 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 20 Sep 2018 05:15:30 -0400 Subject: [PATCH 0702/1971] Merge pull request #3187 from LLK/greenkeeper/chromedriver-2.42.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update chromedriver to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f722a4ee4a..3a93862263 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -41,7 +41,7 @@ "babel-preset-react": "^6.22.0", "base64-loader": "1.0.0", "bowser": "1.9.4", - "chromedriver": "2.41.0", + "chromedriver": "2.42.0", "classnames": "2.2.6", "copy-webpack-plugin": "^4.5.1", "core-js": "2.5.7", From 7e9cff5cee309cfc48d5894b8ca0168e57cb4d00 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 20 Sep 2018 05:34:03 -0400 Subject: [PATCH 0703/1971] Merge pull request #3179 from LLK/greenkeeper/scratch-l10n-3.0.20180918211645 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3a93862263..d11a95fbc3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1537303399", - "scratch-l10n": "3.0.20180830210150", + "scratch-l10n": "3.0.20180918211645", "scratch-paint": "0.2.0-prerelease.20180914201930", "scratch-render": "0.1.0-prerelease.20180918201144", "scratch-storage": "1.0.2", From 3a3ba75e943cb5ed05b2c8811e2ffe47ba02cfa5 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 20 Sep 2018 05:34:59 -0400 Subject: [PATCH 0704/1971] Merge pull request #3190 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20180918203812 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d11a95fbc3..5cc35fb64e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,9 +96,12 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", + "scratch-blocks": "0.1.0-prerelease.1535662135", + "scratch-l10n": "3.0.20180830210150", + "scratch-paint": "0.2.0-prerelease.20180918203812", "scratch-blocks": "0.1.0-prerelease.1537303399", "scratch-l10n": "3.0.20180918211645", - "scratch-paint": "0.2.0-prerelease.20180914201930", + "scratch-paint": "0.2.0-prerelease.20180918203812", "scratch-render": "0.1.0-prerelease.20180918201144", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180907141232", From a352142d21ed01e4182d9457bde43c4285299b5d Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 20 Sep 2018 10:42:54 -0400 Subject: [PATCH 0705/1971] Merge pull request #3193 from fsih/removeDupeDeps Remove duplicate deps --- packages/scratch-gui/package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5cc35fb64e..00718bd23e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,9 +96,6 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1535662135", - "scratch-l10n": "3.0.20180830210150", - "scratch-paint": "0.2.0-prerelease.20180918203812", "scratch-blocks": "0.1.0-prerelease.1537303399", "scratch-l10n": "3.0.20180918211645", "scratch-paint": "0.2.0-prerelease.20180918203812", From 2912a43507512feafc110a152aed3b0e09ea74d7 Mon Sep 17 00:00:00 2001 From: Evelyn Eastmond Date: Thu, 20 Sep 2018 11:20:51 -0400 Subject: [PATCH 0706/1971] Merge pull request #3143 from evhan55/alerts-test Testing a new gui component for alerts --- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 67e70f9001..cf2307726c 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -9,6 +9,7 @@ import {updateTargets} from '../reducers/targets'; import {updateBlockDrag} from '../reducers/block-drag'; import {updateMonitors} from '../reducers/monitors'; import {setRunningState, setTurboState} from '../reducers/vm-status'; +import {showAlert} from '../reducers/alerts'; /* * Higher Order Component to manage events emitted by the VM @@ -36,6 +37,7 @@ const vmListenerHOC = function (WrappedComponent) { this.props.vm.on('TURBO_MODE_OFF', this.props.onTurboModeOff); this.props.vm.on('PROJECT_RUN_START', this.props.onProjectRunStart); this.props.vm.on('PROJECT_RUN_STOP', this.props.onProjectRunStop); + this.props.vm.on('PERIPHERAL_ERROR', this.props.onShowAlert); } componentDidMount () { if (this.props.attachKeyboardEvents) { @@ -93,6 +95,7 @@ const vmListenerHOC = function (WrappedComponent) { onProjectRunStop, onTurboModeOff, onTurboModeOn, + onShowAlert, /* eslint-enable no-unused-vars */ ...props } = this.props; @@ -107,6 +110,7 @@ const vmListenerHOC = function (WrappedComponent) { onMonitorsUpdate: PropTypes.func.isRequired, onProjectRunStart: PropTypes.func.isRequired, onProjectRunStop: PropTypes.func.isRequired, + onShowAlert: PropTypes.func.isRequired, onTargetsUpdate: PropTypes.func.isRequired, onTurboModeOff: PropTypes.func.isRequired, onTurboModeOn: PropTypes.func.isRequired, @@ -134,7 +138,10 @@ const vmListenerHOC = function (WrappedComponent) { onProjectRunStart: () => dispatch(setRunningState(true)), onProjectRunStop: () => dispatch(setRunningState(false)), onTurboModeOn: () => dispatch(setTurboState(true)), - onTurboModeOff: () => dispatch(setTurboState(false)) + onTurboModeOff: () => dispatch(setTurboState(false)), + onShowAlert: () => { + dispatch(showAlert('Scratch has lost connection to peripheral.')); + } }); return connect( mapStateToProps, From 263d2acbb0dadb79feacfe0fbc49a559afa04879 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Mon, 24 Sep 2018 11:07:14 -0400 Subject: [PATCH 0707/1971] adds support for account navigation, registration, login, delivered from www (#3070) * added account navigation menu, copied and adapted from www * fixed css * support for login menu, registration menu from www * Dropdown replaced with MenuBarMenu, override www css in menus * render and style login dropdown via function from www * moved menu styling from menu itself to menu bar * refactoring menu components and containers * refactor and restyle account nav menu * cleaned up cruft and css per rschamp's review of PR 3070 --- .../src/components/menu-bar/menu-bar.jsx | 237 ++++++++++++------ .../scratch-gui/src/lib/vm-listener-hoc.jsx | 4 +- 2 files changed, 165 insertions(+), 76 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 897fedc3f3..5b710fd351 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -11,9 +11,11 @@ import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx'; import Divider from '../divider/divider.jsx'; import LanguageSelector from '../../containers/language-selector.jsx'; import ProjectLoader from '../../containers/project-loader.jsx'; -import Menu from '../../containers/menu.jsx'; +import MenuBarMenu from './menu-bar-menu.jsx'; import {MenuItem, MenuSection} from '../menu/menu.jsx'; import ProjectTitleInput from './project-title-input.jsx'; +import AccountNav from '../../containers/account-nav.jsx'; +import LoginDropdown from './login-dropdown.jsx'; import ProjectSaver from '../../containers/project-saver.jsx'; import DeletionRestorer from '../../containers/deletion-restorer.jsx'; import TurboMode from '../../containers/turbo-mode.jsx'; @@ -21,6 +23,9 @@ import TurboMode from '../../containers/turbo-mode.jsx'; import {openTipsLibrary} from '../../reducers/modals'; import {setPlayer} from '../../reducers/mode'; import { + openAccountMenu, + closeAccountMenu, + accountMenuOpen, openFileMenu, closeFileMenu, fileMenuOpen, @@ -29,7 +34,10 @@ import { editMenuOpen, openLanguageMenu, closeLanguageMenu, - languageMenuOpen + languageMenuOpen, + openLoginMenu, + closeLoginMenu, + loginMenuOpen } from '../../reducers/menus'; import styles from './menu-bar.css'; @@ -39,7 +47,7 @@ import mystuffIcon from './icon--mystuff.png'; import feedbackIcon from './icon--feedback.svg'; import profileIcon from './icon--profile.png'; import communityIcon from './icon--see-community.svg'; -import dropdownCaret from '../language-selector/dropdown-caret.svg'; +import dropdownCaret from './dropdown-caret.svg'; import languageIcon from '../language-selector/language-icon.svg'; import scratchLogo from './scratch-logo.svg'; @@ -111,28 +119,6 @@ MenuItemTooltip.propTypes = { isRtl: PropTypes.bool }; -const MenuBarMenu = ({ - children, - onRequestClose, - open, - place = 'right' -}) => ( - - {children} - -); - -MenuBarMenu.propTypes = { - children: PropTypes.node, - onRequestClose: PropTypes.func, - open: PropTypes.bool, - place: PropTypes.oneOf(['left', 'right']) -}; class MenuBar extends React.Component { constructor (props) { super(props); @@ -253,14 +239,13 @@ class MenuBar extends React.Component { })} onMouseUp={this.props.onClickFile} > -
- -
+ + /> + @@ -346,6 +332,7 @@ class MenuBar extends React.Component { />
-
- -
- -
-
- -
- - - {'scratch-cat' /* @todo username */} - - -
-
+ + {/* show the proper UI in the account menu, given whether the user is + logged in, and whether a session is available to log in with */} +
+ {this.props.sessionExists ? ( + this.props.username ? ( + // ************ user is logged in ************ + + +
+ +
+
+ +
+ ) : ( + // ********* user not logged in, but a session exists + // ********* so they can choose to log in + +
+ +
+
+ + +
+
+ ) + ) : ( + // ******** no login session is available, so don't show login stuff + + +
+ +
+
+ +
+ + + {'scratch-cat'} + + +
+
+
+ )}
); @@ -510,6 +578,7 @@ class MenuBar extends React.Component { } MenuBar.propTypes = { + accountMenuOpen: PropTypes.bool, canUpdateProject: PropTypes.bool, editMenuOpen: PropTypes.bool, enableCommunity: PropTypes.bool, @@ -517,33 +586,53 @@ MenuBar.propTypes = { intl: intlShape, isRtl: PropTypes.bool, languageMenuOpen: PropTypes.bool, + loginMenuOpen: PropTypes.bool, + onClickAccount: PropTypes.func, onClickEdit: PropTypes.func, onClickFile: PropTypes.func, onClickLanguage: PropTypes.func, + onClickLogin: PropTypes.func, + onLogOut: PropTypes.func, + onOpenRegistration: PropTypes.func, onOpenTipLibrary: PropTypes.func, + onRequestCloseAccount: PropTypes.func, onRequestCloseEdit: PropTypes.func, onRequestCloseFile: PropTypes.func, onRequestCloseLanguage: PropTypes.func, + onRequestCloseLogin: PropTypes.func, onSeeCommunity: PropTypes.func, - onUpdateProjectTitle: PropTypes.func + onToggleLoginOpen: PropTypes.func, + onUpdateProjectTitle: PropTypes.func, + renderLogin: PropTypes.func, + sessionExists: PropTypes.bool, + username: PropTypes.string }; const mapStateToProps = state => ({ canUpdateProject: typeof (state.session && state.session.session && state.session.session.user) !== 'undefined', + accountMenuOpen: accountMenuOpen(state), fileMenuOpen: fileMenuOpen(state), editMenuOpen: editMenuOpen(state), isRtl: state.locales.isRtl, - languageMenuOpen: languageMenuOpen(state) + languageMenuOpen: languageMenuOpen(state), + loginMenuOpen: loginMenuOpen(state), + sessionExists: state.session && typeof state.session.session !== 'undefined', + username: state.session && state.session.session && state.session.session.user ? + state.session.session.user.username : null }); const mapDispatchToProps = dispatch => ({ onOpenTipLibrary: () => dispatch(openTipsLibrary()), + onClickAccount: () => dispatch(openAccountMenu()), + onRequestCloseAccount: () => dispatch(closeAccountMenu()), onClickFile: () => dispatch(openFileMenu()), onRequestCloseFile: () => dispatch(closeFileMenu()), onClickEdit: () => dispatch(openEditMenu()), onRequestCloseEdit: () => dispatch(closeEditMenu()), onClickLanguage: () => dispatch(openLanguageMenu()), onRequestCloseLanguage: () => dispatch(closeLanguageMenu()), + onClickLogin: () => dispatch(openLoginMenu()), + onRequestCloseLogin: () => dispatch(closeLoginMenu()), onSeeCommunity: () => dispatch(setPlayer(true)) }); diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index cf2307726c..16bb5781f1 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -122,8 +122,8 @@ const vmListenerHOC = function (WrappedComponent) { }; const mapStateToProps = state => ({ vm: state.scratchGui.vm, - username: state.session && state.session.session ? - state.session.session.username : '' + username: state.session && state.session.session && state.session.session.user ? + state.session.session.user.username : '' }); const mapDispatchToProps = dispatch => ({ onTargetsUpdate: data => { From 038fa28fbee32edd4d18788f6cf97183dc4054b6 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 26 Sep 2018 11:30:26 -0400 Subject: [PATCH 0708/1971] Update scratch svg renderer (#3228) --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 00718bd23e..2c57472f72 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -101,7 +101,7 @@ "scratch-paint": "0.2.0-prerelease.20180918203812", "scratch-render": "0.1.0-prerelease.20180918201144", "scratch-storage": "1.0.2", - "scratch-svg-renderer": "0.2.0-prerelease.20180907141232", + "scratch-svg-renderer": "0.2.0-prerelease.20180926143036", "scratch-vm": "0.2.0-prerelease.20180918201814", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From d69ef68480d6234bcb282a57474e80ac350df3ba Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 26 Sep 2018 13:03:20 -0400 Subject: [PATCH 0709/1971] Merge pull request #3209 from evhan55/multiple-alerts Show multiple alerts in a stack and allow for custom alert icons. --- packages/scratch-gui/package.json | 2 +- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2c57472f72..43b2800e37 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180918201144", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180926143036", - "scratch-vm": "0.2.0-prerelease.20180918201814", + "scratch-vm": "0.2.0-prerelease.20180925190229", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 16bb5781f1..68b2753ab4 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -139,8 +139,8 @@ const vmListenerHOC = function (WrappedComponent) { onProjectRunStop: () => dispatch(setRunningState(false)), onTurboModeOn: () => dispatch(setTurboState(true)), onTurboModeOff: () => dispatch(setTurboState(false)), - onShowAlert: () => { - dispatch(showAlert('Scratch has lost connection to peripheral.')); + onShowAlert: data => { + dispatch(showAlert(data)); } }); return connect( From b8302e0ff60ca805e7f0fea97c550230d282382f Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Wed, 26 Sep 2018 17:04:38 -0400 Subject: [PATCH 0710/1971] chore(package): update scratch-render to version 0.1.0-prerelease.20180926153819 (#3233) --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 43b2800e37..3f756aa42a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -99,7 +99,7 @@ "scratch-blocks": "0.1.0-prerelease.1537303399", "scratch-l10n": "3.0.20180918211645", "scratch-paint": "0.2.0-prerelease.20180918203812", - "scratch-render": "0.1.0-prerelease.20180918201144", + "scratch-render": "0.1.0-prerelease.20180926153819", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180926143036", "scratch-vm": "0.2.0-prerelease.20180925190229", From dc4f2b848e12842aeb91ba9ea8813792bed84d23 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 26 Sep 2018 17:09:02 -0400 Subject: [PATCH 0711/1971] chore(package): update scratch-paint to version 0.2.0-prerelease.20180926191006 (#3236) Closes #3222 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3f756aa42a..19caa8a8c4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1537303399", "scratch-l10n": "3.0.20180918211645", - "scratch-paint": "0.2.0-prerelease.20180918203812", + "scratch-paint": "0.2.0-prerelease.20180926191006", "scratch-render": "0.1.0-prerelease.20180926153819", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180926143036", From b0214b092916268aa2dc00557288113b7d624b29 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 26 Sep 2018 17:54:34 -0400 Subject: [PATCH 0712/1971] Merge pull request #3212 from LLK/greenkeeper/react-popover-0.5.10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-popover to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 19caa8a8c4..a49330a797 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -83,7 +83,7 @@ "react-ga": "2.5.3", "react-intl": "2.4.0", "react-modal": "3.5.1", - "react-popover": "0.5.7", + "react-popover": "0.5.10", "react-redux": "5.0.7", "react-responsive": "5.0.0", "react-style-proptype": "3.2.2", From 02842827d8471a3551f6097be4bad7bc713e9369 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 26 Sep 2018 17:55:57 -0400 Subject: [PATCH 0713/1971] Merge pull request #3226 from LLK/greenkeeper/react-modal-3.6.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-modal to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a49330a797..a9068c23fc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -82,7 +82,7 @@ "react-draggable": "3.0.5", "react-ga": "2.5.3", "react-intl": "2.4.0", - "react-modal": "3.5.1", + "react-modal": "3.6.1", "react-popover": "0.5.10", "react-redux": "5.0.7", "react-responsive": "5.0.0", From 5ad434105dac466720648e435edc82ecb7ed15ee Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 26 Sep 2018 17:56:53 -0400 Subject: [PATCH 0714/1971] Merge pull request #3240 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1537975589 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.153… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a9068c23fc..9f26cbe704 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1537303399", + "scratch-blocks": "0.1.0-prerelease.1537975589", "scratch-l10n": "3.0.20180918211645", "scratch-paint": "0.2.0-prerelease.20180926191006", "scratch-render": "0.1.0-prerelease.20180926153819", From 6047f233edd6e8e8546920eea881bceeca62c38d Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 26 Sep 2018 17:57:35 -0400 Subject: [PATCH 0715/1971] Merge pull request #3234 from LLK/greenkeeper/scratch-l10n-3.0.20180926203705 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9f26cbe704..7cadf34d5d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1537975589", - "scratch-l10n": "3.0.20180918211645", + "scratch-l10n": "3.0.20180926203705", "scratch-paint": "0.2.0-prerelease.20180926191006", "scratch-render": "0.1.0-prerelease.20180926153819", "scratch-storage": "1.0.2", From 615a14f9c316cb0538a288a2d8d408f3984345c2 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 26 Sep 2018 17:59:30 -0400 Subject: [PATCH 0716/1971] Add an indicator to show that the microphone is listening (#3205) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add mic indicator * Fix mic indicator position and margin * Don’t always show indicator, don’t sneaky-enable extension * Position mic indicator in RTL * Actually, don’t move indicator in RTL * Update event name for MIC_LISTENING * Move mic indicator state into redux * Move stageSizeToTransform into screen-utils * Position mic indicator and question at bottom of stage * Fix pointer events * JSDOC for stageSizeToTransform * Pass micIndicator into StageComponent via …props --- packages/scratch-gui/src/containers/stage.jsx | 4 +++- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 0aa3f2a4b6..dc2b870439 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -75,7 +75,8 @@ class Stage extends React.Component { this.props.isColorPicking !== nextProps.isColorPicking || this.state.colorInfo !== nextState.colorInfo || this.props.isFullScreen !== nextProps.isFullScreen || - this.state.question !== nextState.question; + this.state.question !== nextState.question || + this.props.micIndicator !== nextProps.micIndicator; } componentDidUpdate (prevProps) { if (this.props.isColorPicking && !prevProps.isColorPicking) { @@ -402,6 +403,7 @@ Stage.defaultProps = { const mapStateToProps = state => ({ isColorPicking: state.scratchGui.colorPicker.active, isFullScreen: state.scratchGui.mode.isFullScreen, + micIndicator: state.scratchGui.micIndicator, // Do not use editor drag style in fullscreen or player mode. useEditorDragStyle: !(state.scratchGui.mode.isFullScreen || state.scratchGui.mode.isPlayerOnly) }); diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 68b2753ab4..6b8d177779 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -10,6 +10,7 @@ import {updateBlockDrag} from '../reducers/block-drag'; import {updateMonitors} from '../reducers/monitors'; import {setRunningState, setTurboState} from '../reducers/vm-status'; import {showAlert} from '../reducers/alerts'; +import {updateMicIndicator} from '../reducers/mic-indicator'; /* * Higher Order Component to manage events emitted by the VM @@ -38,6 +39,8 @@ const vmListenerHOC = function (WrappedComponent) { this.props.vm.on('PROJECT_RUN_START', this.props.onProjectRunStart); this.props.vm.on('PROJECT_RUN_STOP', this.props.onProjectRunStop); this.props.vm.on('PERIPHERAL_ERROR', this.props.onShowAlert); + this.props.vm.on('MIC_LISTENING', this.props.onMicListeningUpdate); + } componentDidMount () { if (this.props.attachKeyboardEvents) { @@ -89,6 +92,7 @@ const vmListenerHOC = function (WrappedComponent) { onBlockDragUpdate, onKeyDown, onKeyUp, + onMicListeningUpdate, onMonitorsUpdate, onTargetsUpdate, onProjectRunStart, @@ -107,6 +111,7 @@ const vmListenerHOC = function (WrappedComponent) { onBlockDragUpdate: PropTypes.func.isRequired, onKeyDown: PropTypes.func, onKeyUp: PropTypes.func, + onMicListeningUpdate: PropTypes.func.isRequired, onMonitorsUpdate: PropTypes.func.isRequired, onProjectRunStart: PropTypes.func.isRequired, onProjectRunStop: PropTypes.func.isRequired, @@ -141,6 +146,9 @@ const vmListenerHOC = function (WrappedComponent) { onTurboModeOff: () => dispatch(setTurboState(false)), onShowAlert: data => { dispatch(showAlert(data)); + }, + onMicListeningUpdate: listening => { + dispatch(updateMicIndicator(listening)); } }); return connect( From b4fa671460486e963f70922d2c9da429d852f4b4 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 26 Sep 2018 18:00:23 -0400 Subject: [PATCH 0717/1971] Merge pull request #3144 from mzgoddard/allow-workspace-loading-error log workspace update errors --- packages/scratch-gui/src/containers/blocks.jsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 499d776361..bf759bb1b8 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -8,6 +8,7 @@ import VMScratchBlocks from '../lib/blocks'; import VM from 'scratch-vm'; import analytics from '../lib/analytics'; +import log from '../lib/log.js'; import Prompt from './prompt.jsx'; import ConnectionModal from './connection-modal.jsx'; import BlocksComponent from '../components/blocks/blocks.jsx'; @@ -300,7 +301,21 @@ class Blocks extends React.Component { // Remove and reattach the workspace listener (but allow flyout events) this.workspace.removeChangeListener(this.props.vm.blockListener); const dom = this.ScratchBlocks.Xml.textToDom(data.xml); - this.ScratchBlocks.Xml.clearWorkspaceAndLoadFromXml(dom, this.workspace); + try { + this.ScratchBlocks.Xml.clearWorkspaceAndLoadFromXml(dom, this.workspace); + } catch (error) { + // The workspace is likely incomplete. What did update should be + // functional. + // + // Instead of throwing the error, by logging it and continuing as + // normal lets the other workspace update processes complete in the + // gui and vm, which lets the vm run even if the workspace is + // incomplete. Throwing the error would keep things like setting the + // correct editing target from happening which can interfere with + // some blocks and processes in the vm. + error.message = `Workspace Update Error: ${error.message}`; + log.error(error); + } this.workspace.addChangeListener(this.props.vm.blockListener); if (this.props.vm.editingTarget && this.state.workspaceMetrics[this.props.vm.editingTarget.id]) { From d87c0a8d28cb0ee8f3c4c6d199f5af7654ef9a4d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 26 Sep 2018 21:11:39 -0400 Subject: [PATCH 0718/1971] Merge pull request #3239 from rschamp/conditional-feedback Don't show the "Give Feedback" button if there is a session --- .../src/components/menu-bar/menu-bar.jsx | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 5b710fd351..0e14f69151 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -432,25 +432,6 @@ class MenuBar extends React.Component { }
- {/* show the proper UI in the account menu, given whether the user is logged in, and whether a session is available to log in with */} @@ -531,6 +512,25 @@ class MenuBar extends React.Component { ) : ( // ******** no login session is available, so don't show login stuff +
Date: Wed, 26 Sep 2018 21:11:57 -0400 Subject: [PATCH 0719/1971] Merge pull request #3238 from rschamp/share-toggle Add onShare prop to enable the share button --- .../src/components/menu-bar/menu-bar.jsx | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 0e14f69151..7d321ede9e 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -196,6 +196,18 @@ class MenuBar extends React.Component { id="gui.menuBar.saveNow" /> ); + const shareButton = ( + + ); return (
- - - + {this.props.onShare ? shareButton : ( + + {shareButton} + + )}
{this.props.enableCommunity ? From 1809acd6da9f494908bdfa8b31b339752c305c3a Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 1 Oct 2018 17:47:46 -0400 Subject: [PATCH 0720/1971] Merge pull request #3246 from LLK/greenkeeper/text-encoding-0.7.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update text-encoding to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7cadf34d5d..01c3330586 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", "svg-to-image": "1.1.3", - "text-encoding": "0.6.4", + "text-encoding": "0.7.0", "to-style": "1.3.3", "uglifyjs-webpack-plugin": "^1.2.5", "wav-encoder": "1.3.0", From 11d8666d7fe7560160a187b9452e3ea5925d23d1 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 1 Oct 2018 17:56:47 -0400 Subject: [PATCH 0721/1971] Merge pull request #3275 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181001212728 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2018100… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 01c3330586..86fc3adc73 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180926153819", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180926143036", - "scratch-vm": "0.2.0-prerelease.20180925190229", + "scratch-vm": "0.2.0-prerelease.20181001212728", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 5fa02bd738bcb98fd3a42ce0bfd07a62f0ffa35a Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 4 Oct 2018 10:00:01 -0400 Subject: [PATCH 0722/1971] Merge pull request #3276 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181001223744 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 86fc3adc73..767f99a2e1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20180926153819", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180926143036", - "scratch-vm": "0.2.0-prerelease.20181001212728", + "scratch-vm": "0.2.0-prerelease.20181001223744", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From a20da9d5fe92354a5f5334c840c03a90b56ded13 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 4 Oct 2018 10:00:13 -0400 Subject: [PATCH 0723/1971] Merge pull request #3291 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20181002192350 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 767f99a2e1..06535f2571 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -99,7 +99,7 @@ "scratch-blocks": "0.1.0-prerelease.1537975589", "scratch-l10n": "3.0.20180926203705", "scratch-paint": "0.2.0-prerelease.20180926191006", - "scratch-render": "0.1.0-prerelease.20180926153819", + "scratch-render": "0.1.0-prerelease.20181002192350", "scratch-storage": "1.0.2", "scratch-svg-renderer": "0.2.0-prerelease.20180926143036", "scratch-vm": "0.2.0-prerelease.20181001223744", From d01763202590c78a227929ea9291a59c869bb57c Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 4 Oct 2018 10:00:55 -0400 Subject: [PATCH 0724/1971] Merge pull request #3296 from LLK/greenkeeper/scratch-storage-1.0.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-storage to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 06535f2571..65001e9618 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -100,7 +100,7 @@ "scratch-l10n": "3.0.20180926203705", "scratch-paint": "0.2.0-prerelease.20180926191006", "scratch-render": "0.1.0-prerelease.20181002192350", - "scratch-storage": "1.0.2", + "scratch-storage": "1.0.3", "scratch-svg-renderer": "0.2.0-prerelease.20180926143036", "scratch-vm": "0.2.0-prerelease.20181001223744", "selenium-webdriver": "3.6.0", From 84864df64804d43b93e018c001e7ca7c64ce98fc Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 4 Oct 2018 10:02:57 -0400 Subject: [PATCH 0725/1971] Merge pull request #3299 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1538658492 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 65001e9618..061ae256ec 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1537975589", + "scratch-blocks": "0.1.0-prerelease.1538658492", "scratch-l10n": "3.0.20180926203705", "scratch-paint": "0.2.0-prerelease.20180926191006", "scratch-render": "0.1.0-prerelease.20181002192350", From c67efe4862a11e7ae0b338fa1ef70391c6531a40 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 4 Oct 2018 11:03:31 -0400 Subject: [PATCH 0726/1971] Merge pull request #3300 from LLK/greenkeeper/scratch-l10n-3.0.20181004141631 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 061ae256ec..620455b8ea 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1538658492", - "scratch-l10n": "3.0.20180926203705", + "scratch-l10n": "3.0.20181004141631", "scratch-paint": "0.2.0-prerelease.20180926191006", "scratch-render": "0.1.0-prerelease.20181002192350", "scratch-storage": "1.0.3", From a316688cb6a512e06b5de7678a709062c3e012e4 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Fri, 5 Oct 2018 08:28:37 -0400 Subject: [PATCH 0727/1971] Merge pull request #3310 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181004184717 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2018100… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 620455b8ea..fbe2e9fc26 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20181002192350", "scratch-storage": "1.0.3", "scratch-svg-renderer": "0.2.0-prerelease.20180926143036", - "scratch-vm": "0.2.0-prerelease.20181001223744", + "scratch-vm": "0.2.0-prerelease.20181004184717", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 635d3e68520b27f25ce43df3b7e34817ab325cf1 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Fri, 5 Oct 2018 08:29:16 -0400 Subject: [PATCH 0728/1971] Merge pull request #3305 from benjiwheeler/mic added micIndicator prop validation --- packages/scratch-gui/src/containers/stage.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index dc2b870439..888dfa1c6f 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -389,6 +389,7 @@ class Stage extends React.Component { Stage.propTypes = { isColorPicking: PropTypes.bool, isFullScreen: PropTypes.bool.isRequired, + micIndicator: PropTypes.bool, onActivateColorPicker: PropTypes.func, onDeactivateColorPicker: PropTypes.func, stageSize: PropTypes.oneOf(Object.keys(STAGE_DISPLAY_SIZES)).isRequired, From 247922bfd43c6e131d6cb4a9c26aee3c6d46fcf6 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Tue, 9 Oct 2018 09:55:45 -0400 Subject: [PATCH 0729/1971] Merge pull request #3313 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181005173109 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index fbe2e9fc26..f143220199 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20181002192350", "scratch-storage": "1.0.3", "scratch-svg-renderer": "0.2.0-prerelease.20180926143036", - "scratch-vm": "0.2.0-prerelease.20181004184717", + "scratch-vm": "0.2.0-prerelease.20181005173109", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 6b366ce8c9db43e3783f138f0890760b984372b1 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Tue, 9 Oct 2018 10:32:02 -0400 Subject: [PATCH 0730/1971] Merge pull request #3318 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1539092006 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.153… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f143220199..9858b524fa 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -96,7 +96,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20180625202813", - "scratch-blocks": "0.1.0-prerelease.1538658492", + "scratch-blocks": "0.1.0-prerelease.1539092006", "scratch-l10n": "3.0.20181004141631", "scratch-paint": "0.2.0-prerelease.20180926191006", "scratch-render": "0.1.0-prerelease.20181002192350", From 26c980c7c14fd9265bc7ccdc5fe9bdf4dc80be83 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 10 Oct 2018 16:43:53 -0400 Subject: [PATCH 0731/1971] Merge pull request #3314 from LLK/greenkeeper/file-loader-2.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update file-loader to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9858b524fa..f1c88048c6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -53,7 +53,7 @@ "eslint-config-scratch": "^5.0.0", "eslint-plugin-import": "^2.8.0", "eslint-plugin-react": "^7.5.1", - "file-loader": "1.1.11", + "file-loader": "2.0.0", "get-float-time-domain-data": "0.1.0", "get-user-media-promise": "1.1.4", "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", From a9cab88904a228169bce85d26eaee3c3ed68ec19 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 10 Oct 2018 16:44:25 -0400 Subject: [PATCH 0732/1971] Merge pull request #3321 from LLK/greenkeeper/react-contextmenu-2.9.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-contextmenu to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f1c88048c6..b0cf1c3705 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -77,7 +77,7 @@ "raf": "^3.4.0", "raw-loader": "^0.5.1", "react": "16.2.0", - "react-contextmenu": "2.9.3", + "react-contextmenu": "2.9.4", "react-dom": "16.2.0", "react-draggable": "3.0.5", "react-ga": "2.5.3", From 366b3d9241986d1a8e7fba6081e434c3b1456765 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 10 Oct 2018 16:44:55 -0400 Subject: [PATCH 0733/1971] Merge pull request #3323 from LLK/greenkeeper/scratch-storage-1.0.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-storage to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b0cf1c3705..6e2b0a2dfa 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -100,7 +100,7 @@ "scratch-l10n": "3.0.20181004141631", "scratch-paint": "0.2.0-prerelease.20180926191006", "scratch-render": "0.1.0-prerelease.20181002192350", - "scratch-storage": "1.0.3", + "scratch-storage": "1.0.4", "scratch-svg-renderer": "0.2.0-prerelease.20180926143036", "scratch-vm": "0.2.0-prerelease.20181005173109", "selenium-webdriver": "3.6.0", From daea7d9a4950d09c1b30ec8c7667c5e5e06ddda4 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 10 Oct 2018 16:46:58 -0400 Subject: [PATCH 0734/1971] Merge pull request #3331 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181010193639 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6e2b0a2dfa..859f53dc5a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-render": "0.1.0-prerelease.20181002192350", "scratch-storage": "1.0.4", "scratch-svg-renderer": "0.2.0-prerelease.20180926143036", - "scratch-vm": "0.2.0-prerelease.20181005173109", + "scratch-vm": "0.2.0-prerelease.20181010193639", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 7e1d597473086b8f7221b1cbd9ad119cbb68e817 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 10 Oct 2018 16:48:39 -0400 Subject: [PATCH 0735/1971] Merge pull request #3337 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20181010194950 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 859f53dc5a..233c56ebed 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "scratch-audio": "0.1.0-prerelease.20180625202813", "scratch-blocks": "0.1.0-prerelease.1539092006", "scratch-l10n": "3.0.20181004141631", - "scratch-paint": "0.2.0-prerelease.20180926191006", + "scratch-paint": "0.2.0-prerelease.20181010194950", "scratch-render": "0.1.0-prerelease.20181002192350", "scratch-storage": "1.0.4", "scratch-svg-renderer": "0.2.0-prerelease.20180926143036", From 7b7f6d21d42eca81b8f3b9e0899705b8cee6c992 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Thu, 11 Oct 2018 11:00:09 -0400 Subject: [PATCH 0736/1971] Merge pull request #3303 from benjiwheeler/new-project User can load new project in 3.0 --- .../src/components/menu-bar/menu-bar.jsx | 195 +++++++++++------- 1 file changed, 119 insertions(+), 76 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 7d321ede9e..78723b405b 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -10,18 +10,24 @@ import Button from '../button/button.jsx'; import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx'; import Divider from '../divider/divider.jsx'; import LanguageSelector from '../../containers/language-selector.jsx'; -import ProjectLoader from '../../containers/project-loader.jsx'; +import SBFileUploader from '../../containers/sb-file-uploader.jsx'; import MenuBarMenu from './menu-bar-menu.jsx'; import {MenuItem, MenuSection} from '../menu/menu.jsx'; import ProjectTitleInput from './project-title-input.jsx'; import AccountNav from '../../containers/account-nav.jsx'; import LoginDropdown from './login-dropdown.jsx'; -import ProjectSaver from '../../containers/project-saver.jsx'; +import SB3Downloader from '../../containers/sb3-downloader.jsx'; import DeletionRestorer from '../../containers/deletion-restorer.jsx'; import TurboMode from '../../containers/turbo-mode.jsx'; import {openTipsLibrary} from '../../reducers/modals'; import {setPlayer} from '../../reducers/mode'; +import { + getIsUpdating, + getIsShowingProject, + requestNewProject, + saveProject +} from '../../reducers/project-state'; import { openAccountMenu, closeAccountMenu, @@ -123,42 +129,50 @@ class MenuBar extends React.Component { constructor (props) { super(props); bindAll(this, [ + 'handleClickNew', + 'handleClickSave', + 'handleCloseFileMenuAndThen', 'handleLanguageMouseUp', 'handleRestoreOption', - 'handleCloseFileMenuAndThen', 'restoreOptionMessage' ]); - this.state = {projectSaveInProgress: false}; } - handleLanguageMouseUp (e) { - if (!this.props.languageMenuOpen) { - this.props.onClickLanguage(e); + componentDidUpdate (prevProps) { + // if we're no longer showing the project (loading, or whatever), close menus + if (this.props.isShowingProject && !prevProps.isShowingProject) { + this.props.onRequestCloseFile(); + this.props.onRequestCloseEdit(); + } + } + handleClickNew () { + const canSave = this.props.canUpdateProject; // logged in + // if canSave===true, it's safe to replace current project, since we will auto-save first + const readyToReplaceProject = + canSave || confirm('Replace contents of the current project?'); // eslint-disable-line no-alert + if (readyToReplaceProject) { + this.props.onClickNew(canSave); } } + handleClickSave () { + this.props.onClickSave(); + } handleRestoreOption (restoreFun) { return () => { restoreFun(); this.props.onRequestCloseEdit(); }; } - handleUpdateProject (updateFun) { - return () => { - this.props.onRequestCloseFile(); - this.setState({projectSaveInProgress: true}, - () => { - updateFun().then(() => { - this.setState({projectSaveInProgress: false}); - }); - } - ); - }; - } handleCloseFileMenuAndThen (fn) { return () => { this.props.onRequestCloseFile(); fn(); }; } + handleLanguageMouseUp (e) { + if (!this.props.languageMenuOpen) { + this.props.onClickLanguage(e); + } + } restoreOptionMessage (deletedItem) { switch (deletedItem) { case 'Sprite': @@ -196,6 +210,13 @@ class MenuBar extends React.Component { id="gui.menuBar.saveNow" /> ); + const newProjectMessage = ( + + ); const shareButton = ( : + + ) : (this.props.showComingSoon ? ( - } + ) : [])}
@@ -572,44 +575,48 @@ class MenuBar extends React.Component {
- -
- -
-
- -
- - - {'scratch-cat'} - - -
-
+ {this.props.showComingSoon ? ( + + +
+ +
+
+ +
+ + + {'scratch-cat'} + + +
+
+
+ ) : []} )}
@@ -656,6 +663,7 @@ MenuBar.propTypes = { onUpdateProjectTitle: PropTypes.func, renderLogin: PropTypes.func, sessionExists: PropTypes.bool, + showComingSoon: PropTypes.bool, username: PropTypes.string }; From b725759be364ad3af4ee8fb4920ac580b21c91a9 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 23 Oct 2018 16:27:27 -0400 Subject: [PATCH 0764/1971] Move connection modal into redux (#3459) * Connection Modal loads its own data * Connection does not update flyout status buttons * WIP moving connection modal into redux * Lint fix and remove unused prop * Close modal even if exception on disconnect --- .../scratch-gui/src/containers/blocks.jsx | 39 +++++-------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 9df68e65a4..52d3376665 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -10,7 +10,6 @@ import VM from 'scratch-vm'; import analytics from '../lib/analytics'; import log from '../lib/log.js'; import Prompt from './prompt.jsx'; -import ConnectionModal from './connection-modal.jsx'; import BlocksComponent from '../components/blocks/blocks.jsx'; import ExtensionLibrary from './extension-library.jsx'; import extensionData from '../lib/libraries/extensions/index.jsx'; @@ -21,8 +20,9 @@ import {STAGE_DISPLAY_SIZES} from '../lib/layout-constants'; import {connect} from 'react-redux'; import {updateToolbox} from '../reducers/toolbox'; import {activateColorPicker} from '../reducers/color-picker'; -import {closeExtensionLibrary, openSoundRecorder} from '../reducers/modals'; +import {closeExtensionLibrary, openSoundRecorder, openConnectionModal} from '../reducers/modals'; import {activateCustomProcedures, deactivateCustomProcedures} from '../reducers/custom-procedures'; +import {setConnectionModalExtensionId} from '../reducers/connection-modal'; import { activateTab, @@ -47,7 +47,6 @@ class Blocks extends React.Component { 'detachVM', 'handleCategorySelected', 'handleConnectionModalStart', - 'handleConnectionModalClose', 'handleStatusButtonUpdate', 'handleOpenSoundRecorder', 'handlePromptStart', @@ -73,8 +72,7 @@ class Blocks extends React.Component { this.state = { workspaceMetrics: {}, - prompt: null, - connectionModal: null + prompt: null }; this.onTargetsUpdate = debounce(this.onTargetsUpdate, 100); this.toolboxUpdateQueue = []; @@ -114,7 +112,6 @@ class Blocks extends React.Component { shouldComponentUpdate (nextProps, nextState) { return ( this.state.prompt !== nextState.prompt || - this.state.connectionModal !== nextState.connectionModal || this.props.isVisible !== nextProps.isVisible || this.props.toolboxXML !== nextProps.toolboxXML || this.props.extensionLibraryVisible !== nextProps.extensionLibraryVisible || @@ -383,22 +380,7 @@ class Blocks extends React.Component { this.setState(p); } handleConnectionModalStart (extensionId) { - const extension = extensionData.find(ext => ext.extensionId === extensionId); - if (extension) { - this.setState({connectionModal: { - extensionId: extensionId, - useAutoScan: extension.useAutoScan, - peripheralImage: extension.peripheralImage, - smallPeripheralImage: extension.smallPeripheralImage, - peripheralButtonImage: extension.peripheralButtonImage, - name: extension.name, - connectingMessage: extension.connectingMessage, - helpLink: extension.helpLink - }}); - } - } - handleConnectionModalClose () { - this.setState({connectionModal: null}); + this.props.onOpenConnectionModal(extensionId); } handleStatusButtonUpdate () { this.ScratchBlocks.refreshStatusButtons(this.workspace); @@ -434,6 +416,7 @@ class Blocks extends React.Component { isRtl, isVisible, onActivateColorPicker, + onOpenConnectionModal, onOpenSoundRecorder, updateToolboxState, onActivateCustomProcedures, @@ -460,14 +443,6 @@ class Blocks extends React.Component { onOk={this.handlePromptCallback} /> ) : null} - {this.state.connectionModal ? ( - - ) : null} {extensionLibraryVisible ? ( ({ const mapDispatchToProps = dispatch => ({ onActivateColorPicker: callback => dispatch(activateColorPicker(callback)), onActivateCustomProcedures: (data, callback) => dispatch(activateCustomProcedures(data, callback)), + onOpenConnectionModal: id => { + dispatch(setConnectionModalExtensionId(id)); + dispatch(openConnectionModal()); + }, onOpenSoundRecorder: () => { dispatch(activateTab(SOUNDS_TAB_INDEX)); dispatch(openSoundRecorder()); From fee03e9bf8273b8c085eccb9273d45a6bb5950a8 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Tue, 23 Oct 2018 16:49:45 -0400 Subject: [PATCH 0765/1971] Merge pull request #3477 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20181023192935 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9a98f2cd4e..748da821a3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -101,7 +101,7 @@ "scratch-blocks": "0.1.0-prerelease.1539267627", "scratch-l10n": "3.0.20181010220115", "scratch-paint": "0.2.0-prerelease.20181017200201", - "scratch-render": "0.1.0-prerelease.20181017195657", + "scratch-render": "0.1.0-prerelease.20181023192935", "scratch-storage": "1.0.5", "scratch-svg-renderer": "0.2.0-prerelease.20181017193458", "scratch-vm": "0.2.0-prerelease.20181019092856", From b80b8f94c92f567549d2d78672e00c4f5166ea75 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Tue, 23 Oct 2018 16:50:02 -0400 Subject: [PATCH 0766/1971] Merge pull request #3476 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181023192708 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 748da821a3..ed15052e7b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "scratch-render": "0.1.0-prerelease.20181023192935", "scratch-storage": "1.0.5", "scratch-svg-renderer": "0.2.0-prerelease.20181017193458", - "scratch-vm": "0.2.0-prerelease.20181019092856", + "scratch-vm": "0.2.0-prerelease.20181023192708", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From bf61805b0b9f21d5595cfe080ecf6ddb32b5f89c Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Tue, 23 Oct 2018 17:15:41 -0400 Subject: [PATCH 0767/1971] Merge pull request #3480 from LLK/greenkeeper/scratch-audio-0.1.0-prerelease.20181023202904 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-audio to version 0.1.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ed15052e7b..684dcbd616 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -97,7 +97,7 @@ "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "0.1.0-prerelease.20180625202813", + "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1539267627", "scratch-l10n": "3.0.20181010220115", "scratch-paint": "0.2.0-prerelease.20181017200201", From 60ec443f7ef218d31760ef45142e20c16e22844a Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 24 Oct 2018 13:06:26 +0100 Subject: [PATCH 0768/1971] Merge pull request #3488 from LLK/greenkeeper/scratch-storage-1.1.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-storage to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 684dcbd616..5e48cbbf40 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "scratch-l10n": "3.0.20181010220115", "scratch-paint": "0.2.0-prerelease.20181017200201", "scratch-render": "0.1.0-prerelease.20181023192935", - "scratch-storage": "1.0.5", + "scratch-storage": "1.1.0", "scratch-svg-renderer": "0.2.0-prerelease.20181017193458", "scratch-vm": "0.2.0-prerelease.20181023192708", "selenium-webdriver": "3.6.0", From 5ad437b71e09a2df3c3934140b69088fb4a8dd06 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Wed, 24 Oct 2018 11:16:49 -0400 Subject: [PATCH 0769/1971] Merge pull request #3490 from benjiwheeler/connection-modal-quick-fix added missing onOpenConnectionModal --- packages/scratch-gui/src/containers/blocks.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 52d3376665..73a265be9d 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -473,6 +473,7 @@ Blocks.propTypes = { messages: PropTypes.objectOf(PropTypes.string), onActivateColorPicker: PropTypes.func, onActivateCustomProcedures: PropTypes.func, + onOpenConnectionModal: PropTypes.func, onOpenSoundRecorder: PropTypes.func, onRequestCloseCustomProcedures: PropTypes.func, onRequestCloseExtensionLibrary: PropTypes.func, From e2d0e452d772ccc0e06119c024a1e84127f35af1 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 24 Oct 2018 12:58:11 -0400 Subject: [PATCH 0770/1971] Merge pull request #3492 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1540395268 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.154… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5e48cbbf40..48ab3a9ae3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", - "scratch-blocks": "0.1.0-prerelease.1539267627", + "scratch-blocks": "0.1.0-prerelease.1540395268", "scratch-l10n": "3.0.20181010220115", "scratch-paint": "0.2.0-prerelease.20181017200201", "scratch-render": "0.1.0-prerelease.20181023192935", From 1eb3f93acf990266d12bc4bcce0b8a94fe67b1f8 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Wed, 24 Oct 2018 13:24:25 -0400 Subject: [PATCH 0771/1971] Merge pull request #3461 from benjiwheeler/remix can remix, can save as copy; project state names are more consistent --- .../src/components/menu-bar/menu-bar.jsx | 72 ++++++++++++------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 462131b629..a67c97cebb 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -26,7 +26,9 @@ import { getIsUpdating, getIsShowingProject, requestNewProject, - saveProject + remixProject, + updateProject, + saveProjectAsCopy } from '../../reducers/project-state'; import { openAccountMenu, @@ -130,7 +132,9 @@ class MenuBar extends React.Component { super(props); bindAll(this, [ 'handleClickNew', + 'handleClickRemix', 'handleClickSave', + 'handleClickSaveAsCopy', 'handleCloseFileMenuAndThen', 'handleLanguageMouseUp', 'handleRestoreOption', @@ -145,16 +149,23 @@ class MenuBar extends React.Component { } } handleClickNew () { - // if canSave===true, it's safe to replace current project, since we will auto-save first - const readyToReplaceProject = - this.props.canSave || confirm('Replace contents of the current project?'); // eslint-disable-line no-alert + // if canCreateNew===true, it's safe to replace current project, since we will auto-save first. + // else confirm first. + const readyToReplaceProject = this.props.canCreateNew || + confirm('Replace contents of the current project?'); // eslint-disable-line no-alert if (readyToReplaceProject) { - this.props.onClickNew(this.props.canSave); + this.props.onClickNew(this.props.canCreateNew); } } + handleClickRemix () { + this.props.onClickRemix(); + } handleClickSave () { this.props.onClickSave(); } + handleClickSaveAsCopy () { + this.props.onClickSaveAsCopy(); + } handleRestoreOption (restoreFun) { return () => { restoreFun(); @@ -209,13 +220,20 @@ class MenuBar extends React.Component { id="gui.menuBar.saveNow" /> ); - const saveAsCopyMessage = ( + const createCopyMessage = ( ); + const remixMessage = ( + + ); const newProjectMessage = ( - {this.props.canCreateNew ? ( - - {newProjectMessage} - - ) : (this.props.showComingSoon ? ( - - {newProjectMessage} - - ) : [])} + + {newProjectMessage} + {this.props.canSave ? ( @@ -319,18 +328,23 @@ class MenuBar extends React.Component { {saveNowMessage} ) : [])} - {this.props.canSaveAsCopy ? ( + {this.props.canCreateCopy ? ( - {saveAsCopyMessage} + {createCopyMessage} ) : (this.props.showComingSoon ? ( - {saveAsCopyMessage} + {createCopyMessage} ) : [])} + {this.props.canRemix ? ( + + {remixMessage} + + ) : []} @@ -627,10 +641,10 @@ class MenuBar extends React.Component { MenuBar.propTypes = { accountMenuOpen: PropTypes.bool, + canCreateCopy: PropTypes.bool, canCreateNew: PropTypes.bool, canRemix: PropTypes.bool, canSave: PropTypes.bool, - canSaveAsCopy: PropTypes.bool, canShare: PropTypes.bool, className: PropTypes.string, editMenuOpen: PropTypes.bool, @@ -648,7 +662,9 @@ MenuBar.propTypes = { onClickLanguage: PropTypes.func, onClickLogin: PropTypes.func, onClickNew: PropTypes.func, + onClickRemix: PropTypes.func, onClickSave: PropTypes.func, + onClickSaveAsCopy: PropTypes.func, onLogOut: PropTypes.func, onOpenRegistration: PropTypes.func, onOpenTipLibrary: PropTypes.func, @@ -700,8 +716,10 @@ const mapDispatchToProps = dispatch => ({ onRequestCloseLanguage: () => dispatch(closeLanguageMenu()), onClickLogin: () => dispatch(openLoginMenu()), onRequestCloseLogin: () => dispatch(closeLoginMenu()), - onClickNew: canSave => dispatch(requestNewProject(canSave)), - onClickSave: () => dispatch(saveProject()), + onClickNew: canCreateNew => dispatch(requestNewProject(canCreateNew)), + onClickRemix: () => dispatch(remixProject()), + onClickSave: () => dispatch(updateProject()), + onClickSaveAsCopy: () => dispatch(saveProjectAsCopy()), onSeeCommunity: () => dispatch(setPlayer(true)) }); From 9e63fc24cb3e80a9647f2ca918f18660af93fa6d Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 24 Oct 2018 15:47:58 -0400 Subject: [PATCH 0772/1971] Merge pull request #3497 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1540403722 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.154… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 48ab3a9ae3..7dc2237ac0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -98,7 +98,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", - "scratch-blocks": "0.1.0-prerelease.1540395268", + "scratch-blocks": "0.1.0-prerelease.1540403722", "scratch-l10n": "3.0.20181010220115", "scratch-paint": "0.2.0-prerelease.20181017200201", "scratch-render": "0.1.0-prerelease.20181023192935", From 29ff50fbceae0f23ff0859fa8ae056827318925b Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Wed, 24 Oct 2018 17:52:06 -0400 Subject: [PATCH 0773/1971] chore(package): update scratch-svg-renderer to version 0.2.0-prerelease.20181024192149 (#3496) --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7dc2237ac0..633bdcf628 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -103,7 +103,7 @@ "scratch-paint": "0.2.0-prerelease.20181017200201", "scratch-render": "0.1.0-prerelease.20181023192935", "scratch-storage": "1.1.0", - "scratch-svg-renderer": "0.2.0-prerelease.20181017193458", + "scratch-svg-renderer": "0.2.0-prerelease.20181024192149", "scratch-vm": "0.2.0-prerelease.20181023192708", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From af2219c8e088548e56794be53bb55b8f2d123aa6 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 24 Oct 2018 18:10:00 -0400 Subject: [PATCH 0774/1971] chore(package): update scratch-paint to version 0.2.0-prerelease.20181024214759 (#3503) Closes #3498 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 633bdcf628..36ede7e86f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -100,7 +100,7 @@ "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1540403722", "scratch-l10n": "3.0.20181010220115", - "scratch-paint": "0.2.0-prerelease.20181017200201", + "scratch-paint": "0.2.0-prerelease.20181024214759", "scratch-render": "0.1.0-prerelease.20181023192935", "scratch-storage": "1.1.0", "scratch-svg-renderer": "0.2.0-prerelease.20181024192149", From 09b6232643ff2eb11a088ce5b40b09fd07b8e5b2 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 24 Oct 2018 22:30:02 -0400 Subject: [PATCH 0775/1971] chore(package): update scratch-render to version 0.1.0-prerelease.20181024220305 (#3507) Closes #3504 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 36ede7e86f..c5c77a1d05 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -101,7 +101,7 @@ "scratch-blocks": "0.1.0-prerelease.1540403722", "scratch-l10n": "3.0.20181010220115", "scratch-paint": "0.2.0-prerelease.20181024214759", - "scratch-render": "0.1.0-prerelease.20181023192935", + "scratch-render": "0.1.0-prerelease.20181024220305", "scratch-storage": "1.1.0", "scratch-svg-renderer": "0.2.0-prerelease.20181024192149", "scratch-vm": "0.2.0-prerelease.20181023192708", From 3e5b6c4fab6c10049e3672af1792b60b2407e6b3 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 25 Oct 2018 10:20:22 +0100 Subject: [PATCH 0776/1971] Merge pull request #3505 from chrisgarrity/issue/fix-translation-issues Add @babel/cli for generating translation strings --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c5c77a1d05..fa1e798a41 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -29,6 +29,7 @@ "react-dom": "^16.0.0" }, "devDependencies": { + "@babel/cli": "^7.1.2", "@babel/core": "^7.1.2", "@babel/plugin-proposal-object-rest-spread": "^7.0.0", "@babel/plugin-syntax-dynamic-import": "^7.0.0", From cb58fbfa72da5ac9ba1de4b7ec52821ab983bd75 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 25 Oct 2018 11:13:41 +0100 Subject: [PATCH 0777/1971] Merge pull request #3491 from rschamp/storage-no-cache Use assets stored in the vm instead of storage --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index fa1e798a41..64c945fde8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "scratch-render": "0.1.0-prerelease.20181024220305", "scratch-storage": "1.1.0", "scratch-svg-renderer": "0.2.0-prerelease.20181024192149", - "scratch-vm": "0.2.0-prerelease.20181023192708", + "scratch-vm": "0.2.0-prerelease.20181025092837", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 4830786fa677cef5398fc0dfe944eb112f50c71a Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 25 Oct 2018 09:35:08 -0400 Subject: [PATCH 0778/1971] Merge pull request #3513 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1540417094 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.154… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 64c945fde8..7e5a3559cd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -99,7 +99,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", - "scratch-blocks": "0.1.0-prerelease.1540403722", + "scratch-blocks": "0.1.0-prerelease.1540417094", "scratch-l10n": "3.0.20181010220115", "scratch-paint": "0.2.0-prerelease.20181024214759", "scratch-render": "0.1.0-prerelease.20181024220305", From d1c4b711e8d482f79b3c521fdfacfeb57ef30b47 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 25 Oct 2018 09:42:02 -0400 Subject: [PATCH 0779/1971] Merge pull request #3506 from LLK/greenkeeper/scratch-l10n-3.0.20181024221158 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7e5a3559cd..699d9a263b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -100,7 +100,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1540417094", - "scratch-l10n": "3.0.20181010220115", + "scratch-l10n": "3.0.20181024221158", "scratch-paint": "0.2.0-prerelease.20181024214759", "scratch-render": "0.1.0-prerelease.20181024220305", "scratch-storage": "1.1.0", From e918514487c405b9e1c4e1161332c843abb051a2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 26 Oct 2018 13:12:14 -0400 Subject: [PATCH 0780/1971] Merge pull request #3525 from paulkaplan/prevent-targets-dispatch Only dispatch targetsUpdate events if the full editor is visible --- .../scratch-gui/src/lib/vm-listener-hoc.jsx | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index ad6a9f9126..5e18cdaf1e 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -23,7 +23,8 @@ const vmListenerHOC = function (WrappedComponent) { super(props); bindAll(this, [ 'handleKeyDown', - 'handleKeyUp' + 'handleKeyUp', + 'handleTargetsUpdate' ]); // We have to start listening to the vm here rather than in // componentDidMount because the HOC mounts the wrapped component, @@ -31,7 +32,7 @@ const vmListenerHOC = function (WrappedComponent) { // mounts. // If the wrapped component uses the vm in componentDidMount, then // we need to start listening before mounting the wrapped component. - this.props.vm.on('targetsUpdate', this.props.onTargetsUpdate); + this.props.vm.on('targetsUpdate', this.handleTargetsUpdate); this.props.vm.on('MONITORS_UPDATE', this.props.onMonitorsUpdate); this.props.vm.on('BLOCK_DRAG_UPDATE', this.props.onBlockDragUpdate); this.props.vm.on('TURBO_MODE_ON', this.props.onTurboModeOn); @@ -49,9 +50,15 @@ const vmListenerHOC = function (WrappedComponent) { } this.props.vm.postIOData('userData', {username: this.props.username}); } - componentWillReceiveProps (newProps) { - if (newProps.username !== this.props.username) { - this.props.vm.postIOData('userData', {username: newProps.username}); + componentDidUpdate (prevProps) { + if (prevProps.username !== this.props.username) { + this.props.vm.postIOData('userData', {username: this.props.username}); + } + + // Re-request a targets update when the shouldEmitTargetsUpdate state changes to true + // i.e. when the editor transitions out of fullscreen/player only modes + if (this.props.shouldEmitTargetsUpdate && !prevProps.shouldEmitTargetsUpdate) { + this.props.vm.emitTargetsUpdate(); } } componentWillUnmount () { @@ -61,6 +68,11 @@ const vmListenerHOC = function (WrappedComponent) { document.removeEventListener('keyup', this.handleKeyUp); } } + handleTargetsUpdate (data) { + if (this.props.shouldEmitTargetsUpdate) { + this.props.onTargetsUpdate(data); + } + } handleKeyDown (e) { // Don't capture keys intended for Blockly inputs. if (e.target !== document && e.target !== document.body) return; @@ -89,6 +101,7 @@ const vmListenerHOC = function (WrappedComponent) { const { /* eslint-disable no-unused-vars */ attachKeyboardEvents, + shouldEmitTargetsUpdate, username, onBlockDragUpdate, onKeyDown, @@ -120,6 +133,7 @@ const vmListenerHOC = function (WrappedComponent) { onTargetsUpdate: PropTypes.func.isRequired, onTurboModeOff: PropTypes.func.isRequired, onTurboModeOn: PropTypes.func.isRequired, + shouldEmitTargetsUpdate: PropTypes.bool, username: PropTypes.string, vm: PropTypes.instanceOf(VM).isRequired }; @@ -127,6 +141,8 @@ const vmListenerHOC = function (WrappedComponent) { attachKeyboardEvents: true }; const mapStateToProps = state => ({ + // Do not emit target updates in fullscreen or player only mode + shouldEmitTargetsUpdate: !state.scratchGui.mode.isFullScreen && !state.scratchGui.mode.isPlayerOnly, vm: state.scratchGui.vm, username: state.session && state.session.session && state.session.session.user ? state.session.session.user.username : '' From f64eddedfebf1600aea36eacf97ce40c7244537a Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Fri, 26 Oct 2018 17:58:49 -0400 Subject: [PATCH 0781/1971] Merge pull request #3508 from benjiwheeler/share-remix-buttons shared and remix buttons --- .../src/components/menu-bar/menu-bar.jsx | 52 ++++++++++++++++--- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index a67c97cebb..f7af33ec0b 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -55,6 +55,7 @@ import mystuffIcon from './icon--mystuff.png'; import feedbackIcon from './icon--feedback.svg'; import profileIcon from './icon--profile.png'; import communityIcon from './icon--see-community.svg'; +import remixIcon from './icon--remix.svg'; import dropdownCaret from './dropdown-caret.svg'; import languageIcon from '../language-selector/language-icon.svg'; @@ -241,16 +242,43 @@ class MenuBar extends React.Component { id="gui.menuBar.new" /> ); + const shareMessage = ( + + ); + const isSharedMessage = ( + + ); const shareButton = ( + ); + const remixButton = ( + ); return ( @@ -458,11 +486,15 @@ class MenuBar extends React.Component { ) : [] )} + {this.props.canRemix ? remixButton : []}
{this.props.enableCommunity ? ( - ); const remixButton = (
- {this.props.canShare ? shareButton : ( + {this.props.canShare ? ( + (this.props.isShowingProject || this.props.isUpdating) && ( + + { + setRequesting => ( + { + this.handleClickShare(setRequesting); + }} + /* eslint-enable react/jsx-no-bind */ + /> + ) + } + + ) + ) : ( this.props.showComingSoon ? ( - {shareButton} + ) : [] )} @@ -478,37 +491,27 @@ class MenuBar extends React.Component {
{this.props.enableCommunity ? ( - + (this.props.isShowingProject || this.props.isUpdating) && ( + + { + setRequesting => ( + { + this.handleClickSeeCommunity(setRequesting); + }} + /* eslint-enable react/jsx-no-bind */ + /> + ) + } + + ) ) : (this.props.showComingSoon ? ( - + ) : [])}
From 53870adb69f47373f2c3eb6d9d616a1200b620c9 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Wed, 14 Nov 2018 06:41:03 -0500 Subject: [PATCH 0815/1971] Merge pull request #3681 from benjiwheeler/show-creator added author-info, user-avatar components --- .../src/components/menu-bar/menu-bar.jsx | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index c864106ff0..12fe0c2737 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -17,6 +17,7 @@ import ProjectWatcher from '../../containers/project-watcher.jsx'; import MenuBarMenu from './menu-bar-menu.jsx'; import {MenuItem, MenuSection} from '../menu/menu.jsx'; import ProjectTitleInput from './project-title-input.jsx'; +import AuthorInfo from './author-info.jsx'; import AccountNav from '../../containers/account-nav.jsx'; import LoginDropdown from './login-dropdown.jsx'; import SB3Downloader from '../../containers/sb3-downloader.jsx'; @@ -448,17 +449,27 @@ class MenuBar extends React.Component {
-
- - - -
+ {this.props.canEditTitle ? ( +
+ + + +
+ ) : ((this.props.authorUsername && this.props.authorUsername !== this.props.username) ? ( + + ) : null)}
{this.props.canShare ? ( (this.props.isShowingProject || this.props.isUpdating) && ( @@ -667,8 +678,12 @@ class MenuBar extends React.Component { MenuBar.propTypes = { accountMenuOpen: PropTypes.bool, + authorId: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), + authorThumbnailUrl: PropTypes.string, + authorUsername: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), canCreateCopy: PropTypes.bool, canCreateNew: PropTypes.bool, + canEditTitle: PropTypes.bool, canRemix: PropTypes.bool, canSave: PropTypes.bool, canShare: PropTypes.bool, @@ -704,6 +719,7 @@ MenuBar.propTypes = { onShare: PropTypes.func, onToggleLoginOpen: PropTypes.func, onUpdateProjectTitle: PropTypes.func, + projectTitle: PropTypes.string, renderLogin: PropTypes.func, sessionExists: PropTypes.bool, showComingSoon: PropTypes.bool, @@ -726,6 +742,7 @@ const mapStateToProps = state => { isShowingProject: getIsShowingProject(loadingState), languageMenuOpen: languageMenuOpen(state), loginMenuOpen: loginMenuOpen(state), + projectTitle: state.scratchGui.projectTitle, sessionExists: state.session && typeof state.session.session !== 'undefined', username: user ? user.username : null }; From 92771794d3cf86919481057ce0c3e6857a9cc8e1 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 14 Nov 2018 15:02:02 -0500 Subject: [PATCH 0816/1971] Merge pull request #3713 from kchadha/monitor-save-load Monitor save load --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0b5f8d2880..32c30f2431 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-render": "0.1.0-prerelease.20181102130522", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181101210634", - "scratch-vm": "0.2.0-prerelease.20181113174211", + "scratch-vm": "0.2.0-prerelease.20181114192419", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 0a2978627e971116944485461141a179a6b0c7da Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 14 Nov 2018 15:52:16 -0500 Subject: [PATCH 0817/1971] Merge pull request #3772 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20181114155723 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 32c30f2431..e5e37b79be 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -103,7 +103,7 @@ "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1542120810", "scratch-l10n": "3.0.20181031234255", - "scratch-paint": "0.2.0-prerelease.20181109181214", + "scratch-paint": "0.2.0-prerelease.20181114155723", "scratch-render": "0.1.0-prerelease.20181102130522", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181101210634", From 989eca44554d3603ddef731bf0144a506ae72920 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 15 Nov 2018 11:35:09 +0000 Subject: [PATCH 0818/1971] Merge pull request #3724 from rschamp/feature/incremental-saves Save 3.0 project JSON to the project server and assets to the assets server --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e5e37b79be..842ac20784 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-render": "0.1.0-prerelease.20181102130522", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181101210634", - "scratch-vm": "0.2.0-prerelease.20181114192419", + "scratch-vm": "0.2.0-prerelease.20181115103725", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 93403e9624ad8df62a3f906204079bda215dd5d5 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 15 Nov 2018 08:39:01 -0500 Subject: [PATCH 0819/1971] Merge pull request #3774 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1542223417 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 842ac20784..54be00981c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -101,7 +101,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", - "scratch-blocks": "0.1.0-prerelease.1542120810", + "scratch-blocks": "0.1.0-prerelease.1542223417", "scratch-l10n": "3.0.20181031234255", "scratch-paint": "0.2.0-prerelease.20181114155723", "scratch-render": "0.1.0-prerelease.20181102130522", From c56dd4a62cb2abdf706e79ff4ae29ba16cf883e7 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 15 Nov 2018 09:17:32 -0500 Subject: [PATCH 0820/1971] Merge pull request #3749 from kchadha/create-cloud-var Create cloud vars --- packages/scratch-gui/src/containers/blocks.jsx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 9a8e5c9b6b..e038790e47 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -380,7 +380,7 @@ class Blocks extends React.Component { this.ScratchBlocks.Msg.VARIABLE_MODAL_TITLE; p.prompt.varType = typeof optVarType === 'string' ? optVarType : this.ScratchBlocks.SCALAR_VARIABLE_TYPE; - p.prompt.showMoreOptions = + p.prompt.showVariableOptions = // This flag means that we should show variable/list options about scope optVarType !== this.ScratchBlocks.BROADCAST_MESSAGE_VARIABLE_TYPE && p.prompt.title !== this.ScratchBlocks.Msg.RENAME_VARIABLE_MODAL_TITLE && p.prompt.title !== this.ScratchBlocks.Msg.RENAME_LIST_MODAL_TITLE; @@ -395,11 +395,17 @@ class Blocks extends React.Component { handleOpenSoundRecorder () { this.props.onOpenSoundRecorder(); } - handlePromptCallback (input, optionSelection) { + + /* + * Pass along information about proposed name and variable options (scope and isCloud) + * and additional potentially conflicting variable names from the VM + * to the variable validation prompt callback used in scratch-blocks. + */ + handlePromptCallback (input, variableOptions) { this.state.prompt.callback( input, this.props.vm.runtime.getAllVarNamesOfType(this.state.prompt.varType), - optionSelection); + variableOptions); this.handlePromptClose(); } handlePromptClose () { @@ -423,6 +429,7 @@ class Blocks extends React.Component { /* eslint-disable no-unused-vars */ const { anyModalVisible, + canUseCloud, customProceduresVisible, extensionLibraryVisible, options, @@ -453,8 +460,10 @@ class Blocks extends React.Component { isStage={vm.runtime.getEditingTarget().isStage} label={this.state.prompt.message} placeholder={this.state.prompt.defaultValue} - showMoreOptions={this.state.prompt.showMoreOptions} + showCloudOption={canUseCloud} + showVariableOptions={this.state.prompt.showVariableOptions} title={this.state.prompt.title} + vm={vm} onCancel={this.handlePromptClose} onOk={this.handlePromptCallback} /> @@ -481,6 +490,7 @@ class Blocks extends React.Component { Blocks.propTypes = { anyModalVisible: PropTypes.bool, + canUseCloud: PropTypes.bool, customProceduresVisible: PropTypes.bool, extensionLibraryVisible: PropTypes.bool, isRtl: PropTypes.bool, From 4dd3660ce4da6ea87ea76e8cac9e205647add2d2 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 15 Nov 2018 09:30:47 -0500 Subject: [PATCH 0821/1971] Merge pull request #3782 from LLK/greenkeeper/scratch-l10n-3.0.20181115134359 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 54be00981c..469f7b6a7a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1542223417", - "scratch-l10n": "3.0.20181031234255", + "scratch-l10n": "3.0.20181115134359", "scratch-paint": "0.2.0-prerelease.20181114155723", "scratch-render": "0.1.0-prerelease.20181102130522", "scratch-storage": "1.2.0", From 7754d18ef078ef09c7fbcde7eba54edbd9d894e0 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 15 Nov 2018 11:02:53 -0500 Subject: [PATCH 0822/1971] Merge pull request #3788 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1542296154 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.154… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 469f7b6a7a..aa891d5beb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -101,7 +101,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", - "scratch-blocks": "0.1.0-prerelease.1542223417", + "scratch-blocks": "0.1.0-prerelease.1542296154", "scratch-l10n": "3.0.20181115134359", "scratch-paint": "0.2.0-prerelease.20181114155723", "scratch-render": "0.1.0-prerelease.20181102130522", From fe1ed5c82a70da2c8c804503b7ab2744b5d217f6 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Thu, 15 Nov 2018 13:40:33 -0500 Subject: [PATCH 0823/1971] Merge pull request #3779 from benjiwheeler/error-alerts saving, creation errors use alerts; alerts css improvements --- packages/scratch-gui/src/lib/alerts/index.jsx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/scratch-gui/src/lib/alerts/index.jsx b/packages/scratch-gui/src/lib/alerts/index.jsx index a8f8b0b81c..bb4d602068 100644 --- a/packages/scratch-gui/src/lib/alerts/index.jsx +++ b/packages/scratch-gui/src/lib/alerts/index.jsx @@ -34,6 +34,28 @@ const alerts = [ iconSpinner: true, level: AlertLevels.SUCCESS }, + { + alertId: 'creatingError', + content: ( + + ), + level: AlertLevels.WARN + }, + { + alertId: 'savingError', + content: ( + + ), + level: AlertLevels.WARN + }, { alertId: 'saveSuccess', clearList: ['saving'], From b939bf775cfb9e396af46e28c4f47db75ef36b2b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 16 Nov 2018 08:23:10 -0500 Subject: [PATCH 0824/1971] Merge pull request #3791 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181115205630 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index aa891d5beb..523bee18c8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-render": "0.1.0-prerelease.20181102130522", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181101210634", - "scratch-vm": "0.2.0-prerelease.20181115103725", + "scratch-vm": "0.2.0-prerelease.20181115205630", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 91362d50206eb0f355a2586c8ab81bae13c0464c Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Fri, 16 Nov 2018 08:50:07 -0500 Subject: [PATCH 0825/1971] Merge pull request #3780 from benjiwheeler/fix-community MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit project-watcher can request eventual callback, or immediately call ca… --- .../src/components/menu-bar/menu-bar.jsx | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 12fe0c2737..73f3f2159c 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -27,6 +27,7 @@ import TurboMode from '../../containers/turbo-mode.jsx'; import {openTipsLibrary} from '../../reducers/modals'; import {setPlayer} from '../../reducers/mode'; import { + autoUpdateProject, getIsUpdating, getIsShowingProject, manualUpdateProject, @@ -170,19 +171,25 @@ class MenuBar extends React.Component { this.props.onClickSaveAsCopy(); this.props.onRequestCloseFile(); } - handleClickSeeCommunity (requestSeeCommunity) { + handleClickSeeCommunity (waitForUpdate) { if (this.props.canSave) { // save before transitioning to project page - this.props.onClickSave(); + this.props.autoUpdateProject(); + waitForUpdate(true); // queue the transition to project page + } else { + waitForUpdate(false); // immediately transition to project page } - requestSeeCommunity(); // queue the transition to project page } - handleClickShare (requestSeeCommunity) { - if (this.props.canSave && !this.props.isShared) { // save before transitioning to project page - this.props.onClickSave(); - } - if (this.props.canShare && !this.props.isShared) { // save before transitioning to project page - this.props.onShare(); - requestSeeCommunity(); // queue the transition to project page + handleClickShare (waitForUpdate) { + if (!this.props.isShared) { + if (this.props.canShare) { // save before transitioning to project page + this.props.onShare(); + } + if (this.props.canSave) { // save before transitioning to project page + this.props.autoUpdateProject(); + waitForUpdate(true); // queue the transition to project page + } else { + waitForUpdate(false); // immediately transition to project page + } } } handleRestoreOption (restoreFun) { @@ -473,17 +480,15 @@ class MenuBar extends React.Component {
{this.props.canShare ? ( (this.props.isShowingProject || this.props.isUpdating) && ( - + { - setRequesting => ( + waitForUpdate => ( { - this.handleClickShare(setRequesting); + this.handleClickShare(waitForUpdate); }} /* eslint-enable react/jsx-no-bind */ /> @@ -503,16 +508,14 @@ class MenuBar extends React.Component {
{this.props.enableCommunity ? ( (this.props.isShowingProject || this.props.isUpdating) && ( - + { - setRequesting => ( + waitForUpdate => ( { - this.handleClickSeeCommunity(setRequesting); + this.handleClickSeeCommunity(waitForUpdate); }} /* eslint-enable react/jsx-no-bind */ /> @@ -681,6 +684,7 @@ MenuBar.propTypes = { authorId: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), authorThumbnailUrl: PropTypes.string, authorUsername: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), + autoUpdateProject: PropTypes.func, canCreateCopy: PropTypes.bool, canCreateNew: PropTypes.bool, canEditTitle: PropTypes.bool, @@ -749,6 +753,7 @@ const mapStateToProps = state => { }; const mapDispatchToProps = dispatch => ({ + autoUpdateProject: () => dispatch(autoUpdateProject()), onOpenTipLibrary: () => dispatch(openTipsLibrary()), onClickAccount: () => dispatch(openAccountMenu()), onRequestCloseAccount: () => dispatch(closeAccountMenu()), From 7d0b4e9fd30db096d090cb3ebd622b4a32f1bf09 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 16 Nov 2018 09:55:56 -0500 Subject: [PATCH 0826/1971] Merge pull request #3805 from LLK/greenkeeper/chromedriver-2.43.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update chromedriver to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 523bee18c8..077cab46c5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -43,7 +43,7 @@ "babel-loader": "^8.0.4", "base64-loader": "1.0.0", "bowser": "1.9.4", - "chromedriver": "2.43.1", + "chromedriver": "2.43.2", "classnames": "2.2.6", "computed-style-to-inline-style": "3.0.0", "copy-webpack-plugin": "^4.5.1", From e193174c5ed70d69ab19cb6cbc045766a3087ce4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 16 Nov 2018 15:36:53 -0500 Subject: [PATCH 0827/1971] Merge pull request #3807 from LLK/greenkeeper/chromedriver-2.43.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update chromedriver to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 077cab46c5..1e4c84ad9f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -43,7 +43,7 @@ "babel-loader": "^8.0.4", "base64-loader": "1.0.0", "bowser": "1.9.4", - "chromedriver": "2.43.2", + "chromedriver": "2.43.3", "classnames": "2.2.6", "computed-style-to-inline-style": "3.0.0", "copy-webpack-plugin": "^4.5.1", From 4900c85dfc76cd6bdfc0e95b4f60994e5d550adb Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Mon, 19 Nov 2018 17:53:50 -0500 Subject: [PATCH 0828/1971] Merge pull request #3776 from benjiwheeler/menu-bar-messages menu bar save messaging; self-dismissing alerts --- .../src/components/menu-bar/menu-bar.jsx | 4 +++ packages/scratch-gui/src/lib/alerts/index.jsx | 29 +++++++++++++++---- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 73f3f2159c..cb64c5f0d3 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -12,6 +12,7 @@ import ShareButton from './share-button.jsx'; import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx'; import Divider from '../divider/divider.jsx'; import LanguageSelector from '../../containers/language-selector.jsx'; +import InlineMessages from '../../containers/inline-messages.jsx'; import SBFileUploader from '../../containers/sb-file-uploader.jsx'; import ProjectWatcher from '../../containers/project-watcher.jsx'; import MenuBarMenu from './menu-bar-menu.jsx'; @@ -534,6 +535,9 @@ class MenuBar extends React.Component { {/* show the proper UI in the account menu, given whether the user is logged in, and whether a session is available to log in with */}
+
+ +
{this.props.sessionExists ? ( this.props.username ? ( // ************ user is logged in ************ diff --git a/packages/scratch-gui/src/lib/alerts/index.jsx b/packages/scratch-gui/src/lib/alerts/index.jsx index bb4d602068..9fb1800f89 100644 --- a/packages/scratch-gui/src/lib/alerts/index.jsx +++ b/packages/scratch-gui/src/lib/alerts/index.jsx @@ -1,17 +1,26 @@ import React from 'react'; import {FormattedMessage} from 'react-intl'; +import keyMirror from 'keymirror'; import successImage from '../assets/icon--success.svg'; +const AlertTypes = keyMirror({ + STANDARD: null, + EXTENSION: null, + INLINE: null +}); + const AlertLevels = { SUCCESS: 'success', + INFO: 'info', WARN: 'warn' }; const alerts = [ { alertId: 'createSuccess', - clearList: ['creating'], + alertType: AlertTypes.STANDARD, + clearList: ['createSuccess', 'creating', 'saveSuccess', 'saving'], content: ( ), iconURL: successImage, - level: AlertLevels.SUCCESS + level: AlertLevels.SUCCESS, + maxDisplaySecs: 5 }, { alertId: 'creating', + alertType: AlertTypes.STANDARD, + clearList: ['createSuccess', 'creating', 'saveSuccess', 'saving'], content: ( ), iconURL: successImage, - level: AlertLevels.SUCCESS + level: AlertLevels.SUCCESS, + maxDisplaySecs: 5 }, { alertId: 'saving', + alertType: AlertTypes.INLINE, + clearList: ['createSuccess', 'creating', 'saveSuccess', 'saving'], content: ( ), iconSpinner: true, - level: AlertLevels.SUCCESS + level: AlertLevels.INFO } ]; export { alerts as default, - AlertLevels + AlertLevels, + AlertTypes }; From c0d25b397a0891b0ad5a2c9411b01240f5a1ce87 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Tue, 20 Nov 2018 11:56:05 -0500 Subject: [PATCH 0829/1971] Merge pull request #3835 from benjiwheeler/alert-safe-errors avoid create/save/error loops --- packages/scratch-gui/src/lib/alerts/index.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-gui/src/lib/alerts/index.jsx b/packages/scratch-gui/src/lib/alerts/index.jsx index 9fb1800f89..96d16a04ef 100644 --- a/packages/scratch-gui/src/lib/alerts/index.jsx +++ b/packages/scratch-gui/src/lib/alerts/index.jsx @@ -48,6 +48,7 @@ const alerts = [ }, { alertId: 'creatingError', + clearList: ['creating', 'createSuccess'], content: ( Date: Tue, 20 Nov 2018 12:15:26 -0500 Subject: [PATCH 0830/1971] Merge pull request #3844 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1542722191 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.154… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1e4c84ad9f..8e2bd517c6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -101,7 +101,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", - "scratch-blocks": "0.1.0-prerelease.1542296154", + "scratch-blocks": "0.1.0-prerelease.1542722191", "scratch-l10n": "3.0.20181115134359", "scratch-paint": "0.2.0-prerelease.20181114155723", "scratch-render": "0.1.0-prerelease.20181102130522", From eb6b9e1c41b58eb3539cccce7f5882f51f5d3e67 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 20 Nov 2018 14:39:30 -0500 Subject: [PATCH 0831/1971] Merge pull request #3849 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20181120191526 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8e2bd517c6..7dc8c89b40 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -103,7 +103,7 @@ "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1542722191", "scratch-l10n": "3.0.20181115134359", - "scratch-paint": "0.2.0-prerelease.20181114155723", + "scratch-paint": "0.2.0-prerelease.20181120191526", "scratch-render": "0.1.0-prerelease.20181102130522", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181101210634", From 136582aa52b74c7f0201684e7a060dba3c54103f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 20 Nov 2018 14:39:44 -0500 Subject: [PATCH 0832/1971] Merge pull request #3848 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181120175507 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7dc8c89b40..dfd865f20c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-render": "0.1.0-prerelease.20181102130522", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181101210634", - "scratch-vm": "0.2.0-prerelease.20181115205630", + "scratch-vm": "0.2.0-prerelease.20181120175507", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From b6e536ef2dcbbbcdc3f49185a27e7857d8db0dc5 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Wed, 21 Nov 2018 17:43:44 -0500 Subject: [PATCH 0833/1971] Merge pull request #3850 from benjiwheeler/fix-close-button restored close button styling; make close button appear on alert errors --- packages/scratch-gui/src/lib/alerts/index.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-gui/src/lib/alerts/index.jsx b/packages/scratch-gui/src/lib/alerts/index.jsx index 96d16a04ef..66474b8c84 100644 --- a/packages/scratch-gui/src/lib/alerts/index.jsx +++ b/packages/scratch-gui/src/lib/alerts/index.jsx @@ -49,6 +49,7 @@ const alerts = [ { alertId: 'creatingError', clearList: ['creating', 'createSuccess'], + closeButton: true, content: ( Date: Mon, 26 Nov 2018 09:47:53 -0500 Subject: [PATCH 0834/1971] Merge pull request #3853 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181121182825 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index dfd865f20c..81c0fedc2b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-render": "0.1.0-prerelease.20181102130522", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181101210634", - "scratch-vm": "0.2.0-prerelease.20181120175507", + "scratch-vm": "0.2.0-prerelease.20181121182825", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From a879d2a80b8990067b763607f2cc4516e24f478a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 26 Nov 2018 10:44:35 -0500 Subject: [PATCH 0835/1971] Merge pull request #3870 from LLK/greenkeeper/chromedriver-2.44.0 chore(package): update chromedriver to version 2.44.0 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 81c0fedc2b..2c93c119c3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -43,7 +43,7 @@ "babel-loader": "^8.0.4", "base64-loader": "1.0.0", "bowser": "1.9.4", - "chromedriver": "2.43.3", + "chromedriver": "2.44.0", "classnames": "2.2.6", "computed-style-to-inline-style": "3.0.0", "copy-webpack-plugin": "^4.5.1", From 087f4f96b2c3533216eab7d4411a0fe79766061d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 27 Nov 2018 08:49:37 -0500 Subject: [PATCH 0836/1971] Merge pull request #3878 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20181126213837 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2c93c119c3..ef90d77538 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "scratch-blocks": "0.1.0-prerelease.1542722191", "scratch-l10n": "3.0.20181115134359", "scratch-paint": "0.2.0-prerelease.20181120191526", - "scratch-render": "0.1.0-prerelease.20181102130522", + "scratch-render": "0.1.0-prerelease.20181126213837", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181101210634", "scratch-vm": "0.2.0-prerelease.20181121182825", From 69f6712040d9e008c96a7ea981266e7fcf5b2701 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 27 Nov 2018 08:49:55 -0500 Subject: [PATCH 0837/1971] Merge pull request #3877 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20181126212715 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ef90d77538..6ed991f389 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "scratch-paint": "0.2.0-prerelease.20181120191526", "scratch-render": "0.1.0-prerelease.20181126213837", "scratch-storage": "1.2.0", - "scratch-svg-renderer": "0.2.0-prerelease.20181101210634", + "scratch-svg-renderer": "0.2.0-prerelease.20181126212715", "scratch-vm": "0.2.0-prerelease.20181121182825", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 4c86498ac7e8327a2ee306f90c575dc3075bcd09 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 27 Nov 2018 15:48:41 -0500 Subject: [PATCH 0838/1971] Merge pull request #3884 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181127201301 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6ed991f389..769b2ded3e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-render": "0.1.0-prerelease.20181126213837", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181126212715", - "scratch-vm": "0.2.0-prerelease.20181121182825", + "scratch-vm": "0.2.0-prerelease.20181127201301", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From fd7e98b723b51e04c49f8192ced4b2599b217276 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 27 Nov 2018 15:49:00 -0500 Subject: [PATCH 0839/1971] Merge pull request #3881 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20181127155602 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 769b2ded3e..5550d2bb65 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -103,7 +103,7 @@ "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1542722191", "scratch-l10n": "3.0.20181115134359", - "scratch-paint": "0.2.0-prerelease.20181120191526", + "scratch-paint": "0.2.0-prerelease.20181127155602", "scratch-render": "0.1.0-prerelease.20181126213837", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181126212715", From a8c7811a331059afba66f39f34835c3f1b418285 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 27 Nov 2018 15:49:37 -0500 Subject: [PATCH 0840/1971] Merge pull request #3883 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20181127194508 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5550d2bb65..9bce46c3f3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "scratch-blocks": "0.1.0-prerelease.1542722191", "scratch-l10n": "3.0.20181115134359", "scratch-paint": "0.2.0-prerelease.20181127155602", - "scratch-render": "0.1.0-prerelease.20181126213837", + "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181126212715", "scratch-vm": "0.2.0-prerelease.20181127201301", From 840ffeb9cd440d1e5b16905d5088b2384fd08849 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 27 Nov 2018 15:49:54 -0500 Subject: [PATCH 0841/1971] Merge pull request #3882 from paulkaplan/selective-auto-start Green flag overlay to start in player mode --- packages/scratch-gui/src/containers/stage.jsx | 6 ++++++ packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 888dfa1c6f..077006fccc 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -59,6 +59,12 @@ class Stage extends React.Component { this.canvas = document.createElement('canvas'); this.renderer = new Renderer(this.canvas); this.props.vm.attachRenderer(this.renderer); + + // Calling draw a single time before any project is loaded just makes + // the canvas white instead of solid black–needed because it is not + // possible to use CSS to style the canvas to have a different + // default color + this.props.vm.renderer.draw(); } this.props.vm.attachV2SVGAdapter(new V2SVGAdapter()); this.props.vm.attachV2BitmapAdapter(new V2BitmapAdapter()); diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 9dee35d75b..58f45e79f0 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -8,7 +8,7 @@ import {connect} from 'react-redux'; import {updateTargets} from '../reducers/targets'; import {updateBlockDrag} from '../reducers/block-drag'; import {updateMonitors} from '../reducers/monitors'; -import {setRunningState, setTurboState} from '../reducers/vm-status'; +import {setRunningState, setTurboState, setStartedState} from '../reducers/vm-status'; import {showExtensionAlert} from '../reducers/alerts'; import {updateMicIndicator} from '../reducers/mic-indicator'; @@ -39,6 +39,7 @@ const vmListenerHOC = function (WrappedComponent) { this.props.vm.on('TURBO_MODE_OFF', this.props.onTurboModeOff); this.props.vm.on('PROJECT_RUN_START', this.props.onProjectRunStart); this.props.vm.on('PROJECT_RUN_STOP', this.props.onProjectRunStop); + this.props.vm.on('RUNTIME_STARTED', this.props.onRuntimeStarted); this.props.vm.on('PERIPHERAL_DISCONNECT_ERROR', this.props.onShowExtensionAlert); this.props.vm.on('MIC_LISTENING', this.props.onMicListeningUpdate); @@ -110,6 +111,7 @@ const vmListenerHOC = function (WrappedComponent) { onTargetsUpdate, onProjectRunStart, onProjectRunStop, + onRuntimeStarted, onTurboModeOff, onTurboModeOn, onShowExtensionAlert, @@ -128,6 +130,7 @@ const vmListenerHOC = function (WrappedComponent) { onMonitorsUpdate: PropTypes.func.isRequired, onProjectRunStart: PropTypes.func.isRequired, onProjectRunStop: PropTypes.func.isRequired, + onRuntimeStarted: PropTypes.func.isRequired, onShowExtensionAlert: PropTypes.func.isRequired, onTargetsUpdate: PropTypes.func.isRequired, onTurboModeOff: PropTypes.func.isRequired, @@ -158,6 +161,7 @@ const vmListenerHOC = function (WrappedComponent) { }, onProjectRunStart: () => dispatch(setRunningState(true)), onProjectRunStop: () => dispatch(setRunningState(false)), + onRuntimeStarted: () => dispatch(setStartedState(true)), onTurboModeOn: () => dispatch(setTurboState(true)), onTurboModeOff: () => dispatch(setTurboState(false)), onShowExtensionAlert: data => { From 756258b71232d93f2894b19f93eab9f50daea951 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 27 Nov 2018 16:55:32 -0500 Subject: [PATCH 0842/1971] Merge pull request #3885 from paulkaplan/fix-start-frame Hide entire frame unless the VM has not started --- packages/scratch-gui/src/containers/stage.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 077006fccc..94d4f092fc 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -82,7 +82,8 @@ class Stage extends React.Component { this.state.colorInfo !== nextState.colorInfo || this.props.isFullScreen !== nextProps.isFullScreen || this.state.question !== nextState.question || - this.props.micIndicator !== nextProps.micIndicator; + this.props.micIndicator !== nextProps.micIndicator || + this.props.isStarted !== nextProps.isStarted; } componentDidUpdate (prevProps) { if (this.props.isColorPicking && !prevProps.isColorPicking) { @@ -410,6 +411,7 @@ Stage.defaultProps = { const mapStateToProps = state => ({ isColorPicking: state.scratchGui.colorPicker.active, isFullScreen: state.scratchGui.mode.isFullScreen, + isStarted: state.scratchGui.vmStatus.started, micIndicator: state.scratchGui.micIndicator, // Do not use editor drag style in fullscreen or player mode. useEditorDragStyle: !(state.scratchGui.mode.isFullScreen || state.scratchGui.mode.isPlayerOnly) From 948305f4630119fa048736f1af129bb8c691d52c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 27 Nov 2018 17:57:09 -0500 Subject: [PATCH 0843/1971] Merge pull request #3888 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181127220421 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9bce46c3f3..a7666e2bd8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181126212715", - "scratch-vm": "0.2.0-prerelease.20181127201301", + "scratch-vm": "0.2.0-prerelease.20181127220421", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 251f57669facfa33c06a8665c5de4e616ec28f7f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 28 Nov 2018 08:52:43 -0500 Subject: [PATCH 0844/1971] Merge pull request #3880 from paulkaplan/prevent-space-scroll Prevent arrow/space from scrolling the page --- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 58f45e79f0..675deb04cd 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -83,6 +83,12 @@ const vmListenerHOC = function (WrappedComponent) { key: e.key, isDown: true }); + + // Prevent space/arrow key from scrolling the page. + if (e.keyCode === 32 || // 32=space + (e.keyCode >= 37 && e.keyCode <= 40)) { // 37, 38, 39, 40 are arrows + e.preventDefault(); + } } handleKeyUp (e) { // Always capture up events, From faa3a7449207e18b0371dc8ccd117865619e513b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 28 Nov 2018 10:39:32 -0500 Subject: [PATCH 0845/1971] Merge pull request #3895 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181128141527 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a7666e2bd8..a2d99398b7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181126212715", - "scratch-vm": "0.2.0-prerelease.20181127220421", + "scratch-vm": "0.2.0-prerelease.20181128141527", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From f125674d815298e766b8bf059179697a93a2aef3 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Wed, 28 Nov 2018 10:40:17 -0800 Subject: [PATCH 0846/1971] Merge pull request #3859 from benjiwheeler/translations updated scratch-l10n in package.json --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a2d99398b7..b8dc986712 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1542722191", - "scratch-l10n": "3.0.20181115134359", + "scratch-l10n": "3.0.20181121224721", "scratch-paint": "0.2.0-prerelease.20181127155602", "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", From 625536890c8550c07baf12c0e1e22427c5cce694 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 28 Nov 2018 14:46:10 -0500 Subject: [PATCH 0847/1971] Merge pull request #3879 from rschamp/project-changed-state Track "project changed" state in Redux --- packages/scratch-gui/src/containers/blocks.jsx | 5 ++++- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index e038790e47..7c757f392e 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -328,7 +328,10 @@ class Blocks extends React.Component { error.message = `Workspace Update Error: ${error.message}`; log.error(error); } - this.workspace.addChangeListener(this.props.vm.blockListener); + // All of the changes that happened during the load above are queued with + // timeouts, so re-enable the listener in the next tick, so it happens after + // the events are already fired. + setTimeout(() => this.workspace.addChangeListener(this.props.vm.blockListener)); if (this.props.vm.editingTarget && this.state.workspaceMetrics[this.props.vm.editingTarget.id]) { const {scrollX, scrollY, scale} = this.state.workspaceMetrics[this.props.vm.editingTarget.id]; diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 675deb04cd..3e36865719 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -8,6 +8,7 @@ import {connect} from 'react-redux'; import {updateTargets} from '../reducers/targets'; import {updateBlockDrag} from '../reducers/block-drag'; import {updateMonitors} from '../reducers/monitors'; +import {setProjectChanged, setProjectUnchanged} from '../reducers/project-changed'; import {setRunningState, setTurboState, setStartedState} from '../reducers/vm-status'; import {showExtensionAlert} from '../reducers/alerts'; import {updateMicIndicator} from '../reducers/mic-indicator'; @@ -24,6 +25,7 @@ const vmListenerHOC = function (WrappedComponent) { bindAll(this, [ 'handleKeyDown', 'handleKeyUp', + 'handleProjectChanged', 'handleTargetsUpdate' ]); // We have to start listening to the vm here rather than in @@ -39,6 +41,7 @@ const vmListenerHOC = function (WrappedComponent) { this.props.vm.on('TURBO_MODE_OFF', this.props.onTurboModeOff); this.props.vm.on('PROJECT_RUN_START', this.props.onProjectRunStart); this.props.vm.on('PROJECT_RUN_STOP', this.props.onProjectRunStop); + this.props.vm.on('PROJECT_CHANGED', this.handleProjectChanged); this.props.vm.on('RUNTIME_STARTED', this.props.onRuntimeStarted); this.props.vm.on('PERIPHERAL_DISCONNECT_ERROR', this.props.onShowExtensionAlert); this.props.vm.on('MIC_LISTENING', this.props.onMicListeningUpdate); @@ -69,6 +72,9 @@ const vmListenerHOC = function (WrappedComponent) { document.removeEventListener('keyup', this.handleKeyUp); } } + handleProjectChanged () { + this.props.onProjectChanged(); + } handleTargetsUpdate (data) { if (this.props.shouldEmitTargetsUpdate) { this.props.onTargetsUpdate(data); @@ -167,6 +173,8 @@ const vmListenerHOC = function (WrappedComponent) { }, onProjectRunStart: () => dispatch(setRunningState(true)), onProjectRunStop: () => dispatch(setRunningState(false)), + onProjectChanged: () => dispatch(setProjectChanged()), + onProjectSaved: () => dispatch(setProjectUnchanged()), onRuntimeStarted: () => dispatch(setStartedState(true)), onTurboModeOn: () => dispatch(setTurboState(true)), onTurboModeOff: () => dispatch(setTurboState(false)), From 1e0e6ce7e94bd60be05e21ad2ddbe2201957af4f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 28 Nov 2018 15:06:49 -0500 Subject: [PATCH 0848/1971] Merge pull request #3900 from paulkaplan/download-on-error Add download and retry saving to saving error alert. --- packages/scratch-gui/src/lib/alerts/index.jsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/scratch-gui/src/lib/alerts/index.jsx b/packages/scratch-gui/src/lib/alerts/index.jsx index 66474b8c84..dda178e1fd 100644 --- a/packages/scratch-gui/src/lib/alerts/index.jsx +++ b/packages/scratch-gui/src/lib/alerts/index.jsx @@ -61,11 +61,13 @@ const alerts = [ }, { alertId: 'savingError', - clearList: ['saving', 'saveSuccess'], - closeButton: true, + clearList: ['saving', 'saveSuccess', 'savingError'], + showDownload: true, + showSaveNow: true, + closeButton: false, content: ( @@ -75,7 +77,7 @@ const alerts = [ { alertId: 'saveSuccess', alertType: AlertTypes.INLINE, - clearList: ['createSuccess', 'creating', 'saveSuccess', 'saving'], + clearList: ['createSuccess', 'creating', 'saveSuccess', 'saving', 'savingError'], content: ( Date: Wed, 28 Nov 2018 15:07:24 -0500 Subject: [PATCH 0849/1971] Merge pull request #3898 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20181128182047 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b8dc986712..78285c6bc7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -103,7 +103,7 @@ "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1542722191", "scratch-l10n": "3.0.20181121224721", - "scratch-paint": "0.2.0-prerelease.20181127155602", + "scratch-paint": "0.2.0-prerelease.20181128182047", "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181126212715", From d5c07b2836b2f80fb595491eb32d4802b1eee188 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 28 Nov 2018 15:55:24 -0500 Subject: [PATCH 0850/1971] Merge pull request #3896 from rschamp/remove-fun Remove color rotation while saving --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index cb64c5f0d3..a9ebe0c5b4 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -285,8 +285,7 @@ class MenuBar extends React.Component {
From 73236ee74e7173f05873b116e20c292014cdf304 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 28 Nov 2018 18:11:56 -0500 Subject: [PATCH 0851/1971] Merge pull request #3904 from kchadha/cloud-connect-when-has-cloud-data Connect to cloud based on hasCloudData --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 78285c6bc7..eb74d9f471 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181126212715", - "scratch-vm": "0.2.0-prerelease.20181128141527", + "scratch-vm": "0.2.0-prerelease.20181128224414", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 913419c09a05485822822990483cd1c0ea41ccbd Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 29 Nov 2018 13:13:31 -0500 Subject: [PATCH 0852/1971] Merge pull request #3907 from paulkaplan/fix-block-changing Remove setTimeout for reattaching workspace block listener. --- packages/scratch-gui/src/containers/blocks.jsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 7c757f392e..e038790e47 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -328,10 +328,7 @@ class Blocks extends React.Component { error.message = `Workspace Update Error: ${error.message}`; log.error(error); } - // All of the changes that happened during the load above are queued with - // timeouts, so re-enable the listener in the next tick, so it happens after - // the events are already fired. - setTimeout(() => this.workspace.addChangeListener(this.props.vm.blockListener)); + this.workspace.addChangeListener(this.props.vm.blockListener); if (this.props.vm.editingTarget && this.state.workspaceMetrics[this.props.vm.editingTarget.id]) { const {scrollX, scrollY, scale} = this.state.workspaceMetrics[this.props.vm.editingTarget.id]; From c59481bb77d47dbbfb60837228d69adcaa38e7d2 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 29 Nov 2018 14:02:46 -0500 Subject: [PATCH 0853/1971] Add Makey Makey extension (#3908) --- .../src/lib/libraries/extensions/index.jsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index f1f5ddf551..8472907649 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -14,6 +14,8 @@ import ev3Image from './ev3.png'; import wedoImage from './wedo.png'; import text2speechImage from './text2speech.png'; import text2speechInsetImage from './text2speech-small.svg'; +import makeymakeyImage from './makeymakey.png'; +import makeymakeyInsetImage from './makeymakey-small.svg'; import microbitPeripheralImage from './peripheral-connection/microbit/microbit-illustration.svg'; import microbitMenuImage from './peripheral-connection/microbit/microbit-small.svg'; @@ -128,6 +130,21 @@ export default [ featured: true, internetConnectionRequired: true }, + { + name: 'Makey Makey', + extensionId: 'makeymakey', + collaborator: 'JoyLabz', + iconURL: makeymakeyImage, + insetIconURL: makeymakeyInsetImage, + description: ( + + ), + featured: true + }, { name: 'micro:bit', extensionId: 'microbit', From cd0ad5e37c02ac717bac65c4cea8f8a44c24116b Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Thu, 29 Nov 2018 12:50:42 -0800 Subject: [PATCH 0854/1971] Merge pull request #3891 from benjiwheeler/delete-translations-first delete existing translations before building translations, to avoid errors --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index eb74d9f471..3b9b0ca061 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -8,7 +8,7 @@ "clean": "rimraf ./build && mkdirp build && rimraf ./dist && mkdirp dist", "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", "prune": "./prune-gh-pages.sh", - "i18n:src": "babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/src ./translations/", + "i18n:src": "rimraf ./translations/messages/src && babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/src ./translations/", "start": "webpack-dev-server", "test": "npm run test:lint && npm run test:unit && npm run build && npm run test:integration", "test:integration": "jest --runInBand test[\\\\/]integration", From bcf83aad4986058ade8cd728cdbb771fef4ae28b Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 29 Nov 2018 16:48:42 -0500 Subject: [PATCH 0855/1971] chore(package): update scratch-vm to version 0.2.0-prerelease.20181129211442 (#3911) Closes #3910 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3b9b0ca061..d6ee0f021e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181126212715", - "scratch-vm": "0.2.0-prerelease.20181128224414", + "scratch-vm": "0.2.0-prerelease.20181129211442", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From a602419784282bd1660e718bfbbdedc584e36bec Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Mon, 3 Dec 2018 08:13:47 -0500 Subject: [PATCH 0856/1971] Merge pull request #3917 from chrisgarrity/issue/update-scratch-l10n Update scratch-l10n dependency --- packages/scratch-gui/package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d6ee0f021e..ba550d7e07 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -8,7 +8,8 @@ "clean": "rimraf ./build && mkdirp build && rimraf ./dist && mkdirp dist", "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", "prune": "./prune-gh-pages.sh", - "i18n:src": "rimraf ./translations/messages/src && babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/src ./translations/", + "i18n:push": "tx-push-src scratch-editor interface translations/en.json", + "i18n:src": "rimraf ./translations/messages/src && babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/src ./translations/ && npm run i18n:push", "start": "webpack-dev-server", "test": "npm run test:lint && npm run test:unit && npm run build && npm run test:integration", "test:integration": "jest --runInBand test[\\\\/]integration", @@ -102,7 +103,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1542722191", - "scratch-l10n": "3.0.20181121224721", + "scratch-l10n": "3.1.20181129221712", "scratch-paint": "0.2.0-prerelease.20181128182047", "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", From 7f12feb134daaef6e3567a6777930d967cc79599 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 3 Dec 2018 13:16:22 -0500 Subject: [PATCH 0857/1971] Merge pull request #3928 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181203173319 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ba550d7e07..f46b9b3d6d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181126212715", - "scratch-vm": "0.2.0-prerelease.20181129211442", + "scratch-vm": "0.2.0-prerelease.20181203173319", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 8bbacee526e54322dad66cc678b1ce28ec18d7ce Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 3 Dec 2018 13:16:45 -0500 Subject: [PATCH 0858/1971] Merge pull request #3923 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1543843239 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f46b9b3d6d..f94cba30bb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -102,7 +102,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", - "scratch-blocks": "0.1.0-prerelease.1542722191", + "scratch-blocks": "0.1.0-prerelease.1543843239", "scratch-l10n": "3.1.20181129221712", "scratch-paint": "0.2.0-prerelease.20181128182047", "scratch-render": "0.1.0-prerelease.20181127194508", From 3cb8d436cbe6f5358238c77f4bfdcb57e03e228c Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 3 Dec 2018 13:20:31 -0500 Subject: [PATCH 0859/1971] Merge pull request #3916 from kchadha/cloud-msg-throttle Cloud Variable Rate Limit --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f94cba30bb..74d3fe3ba9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -73,6 +73,7 @@ "lodash.isequal": "4.5.0", "lodash.omit": "4.5.0", "lodash.pick": "4.4.0", + "lodash.throttle": "4.0.1", "minilog": "3.1.0", "mkdirp": "^0.5.1", "postcss-import": "^12.0.0", From ed8028bed5e8252542b5ca124ded40c9bce6517d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 3 Dec 2018 14:20:06 -0500 Subject: [PATCH 0860/1971] Merge pull request #3929 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1543861503 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 74d3fe3ba9..e4ab68c81a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -103,7 +103,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", - "scratch-blocks": "0.1.0-prerelease.1543843239", + "scratch-blocks": "0.1.0-prerelease.1543861503", "scratch-l10n": "3.1.20181129221712", "scratch-paint": "0.2.0-prerelease.20181128182047", "scratch-render": "0.1.0-prerelease.20181127194508", From 65bde1baa167a867e5c9bf982bf5c5a4ce4011e4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 3 Dec 2018 14:49:27 -0500 Subject: [PATCH 0861/1971] Merge pull request #3930 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20181203192800 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e4ab68c81a..a24a183c6f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1543861503", "scratch-l10n": "3.1.20181129221712", - "scratch-paint": "0.2.0-prerelease.20181128182047", + "scratch-paint": "0.2.0-prerelease.20181203192800", "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181126212715", From e19fc61d7628f447d50b07a0db8d0721aa95d12a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 4 Dec 2018 15:30:43 -0500 Subject: [PATCH 0862/1971] Merge pull request #3938 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1543933873 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a24a183c6f..b6bd7788ba 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -103,7 +103,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", - "scratch-blocks": "0.1.0-prerelease.1543861503", + "scratch-blocks": "0.1.0-prerelease.1543933873", "scratch-l10n": "3.1.20181129221712", "scratch-paint": "0.2.0-prerelease.20181203192800", "scratch-render": "0.1.0-prerelease.20181127194508", From 10b7e9f1995618080b9b7d0254fee0764d1591e9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 4 Dec 2018 16:01:39 -0500 Subject: [PATCH 0863/1971] Merge pull request #3952 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181204154110 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2018120… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b6bd7788ba..751716498b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181126212715", - "scratch-vm": "0.2.0-prerelease.20181203173319", + "scratch-vm": "0.2.0-prerelease.20181204154110", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 4353678af9e29a6873a2c90421dcafc549f80416 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 5 Dec 2018 08:28:34 -0500 Subject: [PATCH 0864/1971] Merge pull request #3951 from paulkaplan/show-cloud-info-alert Add an alert that is shown when you first add a cloud variable --- packages/scratch-gui/src/lib/alerts/index.jsx | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/scratch-gui/src/lib/alerts/index.jsx b/packages/scratch-gui/src/lib/alerts/index.jsx index dda178e1fd..ffe84da5d9 100644 --- a/packages/scratch-gui/src/lib/alerts/index.jsx +++ b/packages/scratch-gui/src/lib/alerts/index.jsx @@ -102,6 +102,36 @@ const alerts = [ ), iconSpinner: true, level: AlertLevels.INFO + }, + { + alertId: 'cloudInfo', + alertType: AlertTypes.STANDARD, + clearList: ['cloudInfo'], + content: ( + + + + ) + }} + /> + ), + closeButton: true, + level: AlertLevels.SUCCESS, + maxDisplaySecs: 15 } ]; From c30710cccafca7cb6f2017c015125f2ff3356ba4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 5 Dec 2018 12:29:45 -0500 Subject: [PATCH 0865/1971] Merge pull request #3954 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1544016933 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 751716498b..47663506d1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -103,7 +103,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", - "scratch-blocks": "0.1.0-prerelease.1543933873", + "scratch-blocks": "0.1.0-prerelease.1544016933", "scratch-l10n": "3.1.20181129221712", "scratch-paint": "0.2.0-prerelease.20181203192800", "scratch-render": "0.1.0-prerelease.20181127194508", From b32654468b563d6683c3836bad3b9488b87e906a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 5 Dec 2018 13:08:43 -0500 Subject: [PATCH 0866/1971] Merge pull request #3956 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181205175228 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 47663506d1..51a7e3ca0a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181126212715", - "scratch-vm": "0.2.0-prerelease.20181204154110", + "scratch-vm": "0.2.0-prerelease.20181205175228", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From d3853d74958259f0b7fdc95d753c41d22ee92bb7 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 5 Dec 2018 13:11:52 -0500 Subject: [PATCH 0867/1971] Merge pull request #3921 from LLK/greenkeeper/chromedriver-2.44.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update chromedriver to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 51a7e3ca0a..4a2ab874cc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -44,7 +44,7 @@ "babel-loader": "^8.0.4", "base64-loader": "1.0.0", "bowser": "1.9.4", - "chromedriver": "2.44.0", + "chromedriver": "2.44.1", "classnames": "2.2.6", "computed-style-to-inline-style": "3.0.0", "copy-webpack-plugin": "^4.5.1", From 267e24db5390c7156e2002cd90c85a5cb810f875 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 6 Dec 2018 10:52:25 -0500 Subject: [PATCH 0868/1971] Merge pull request #3961 from kchadha/fix-error-logging Fix error logging when workspace fails to update --- packages/scratch-gui/src/containers/blocks.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index e038790e47..bc7ef7d8ed 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -325,7 +325,9 @@ class Blocks extends React.Component { // incomplete. Throwing the error would keep things like setting the // correct editing target from happening which can interfere with // some blocks and processes in the vm. - error.message = `Workspace Update Error: ${error.message}`; + if (error.message) { + error.message = `Workspace Update Error: ${error.message}`; + } log.error(error); } this.workspace.addChangeListener(this.props.vm.blockListener); From 3486b0d058054408fe061ae04678aaf1c1e764d1 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 6 Dec 2018 10:53:44 -0500 Subject: [PATCH 0869/1971] Merge pull request #3960 from kchadha/cloud-var-modal Do not show cloud variable option in list modal. --- packages/scratch-gui/src/containers/blocks.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index bc7ef7d8ed..17850ed981 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -386,6 +386,7 @@ class Blocks extends React.Component { optVarType !== this.ScratchBlocks.BROADCAST_MESSAGE_VARIABLE_TYPE && p.prompt.title !== this.ScratchBlocks.Msg.RENAME_VARIABLE_MODAL_TITLE && p.prompt.title !== this.ScratchBlocks.Msg.RENAME_LIST_MODAL_TITLE; + p.prompt.showCloudOption = (optVarType === this.ScratchBlocks.SCALAR_VARIABLE_TYPE) && this.props.canUseCloud; this.setState(p); } handleConnectionModalStart (extensionId) { @@ -462,7 +463,7 @@ class Blocks extends React.Component { isStage={vm.runtime.getEditingTarget().isStage} label={this.state.prompt.message} placeholder={this.state.prompt.defaultValue} - showCloudOption={canUseCloud} + showCloudOption={this.state.prompt.showCloudOption} showVariableOptions={this.state.prompt.showVariableOptions} title={this.state.prompt.title} vm={vm} From 1d59849ced98b94891ee5ccbe06a946b6145852c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 6 Dec 2018 11:05:46 -0500 Subject: [PATCH 0870/1971] Merge pull request #3955 from paulkaplan/save-now "Save Now" menubar option when project has changed --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 4 ++-- packages/scratch-gui/src/lib/alerts/index.jsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index a9ebe0c5b4..878e6a7b42 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -12,7 +12,7 @@ import ShareButton from './share-button.jsx'; import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx'; import Divider from '../divider/divider.jsx'; import LanguageSelector from '../../containers/language-selector.jsx'; -import InlineMessages from '../../containers/inline-messages.jsx'; +import SaveStatus from './save-status.jsx'; import SBFileUploader from '../../containers/sb-file-uploader.jsx'; import ProjectWatcher from '../../containers/project-watcher.jsx'; import MenuBarMenu from './menu-bar-menu.jsx'; @@ -535,7 +535,7 @@ class MenuBar extends React.Component { logged in, and whether a session is available to log in with */}
- +
{this.props.sessionExists ? ( this.props.username ? ( diff --git a/packages/scratch-gui/src/lib/alerts/index.jsx b/packages/scratch-gui/src/lib/alerts/index.jsx index ffe84da5d9..53de8f8489 100644 --- a/packages/scratch-gui/src/lib/alerts/index.jsx +++ b/packages/scratch-gui/src/lib/alerts/index.jsx @@ -87,7 +87,7 @@ const alerts = [ ), iconURL: successImage, level: AlertLevels.SUCCESS, - maxDisplaySecs: 5 + maxDisplaySecs: 3 }, { alertId: 'saving', From 46a49717a14f195b311119e80fce6563b42647dc Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 6 Dec 2018 12:59:27 -0500 Subject: [PATCH 0871/1971] Merge pull request #3972 from paulkaplan/bind-green-flag Bind vm project_start event to passed in onGreenFlag prop --- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 3e36865719..ed9715c31b 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -43,6 +43,7 @@ const vmListenerHOC = function (WrappedComponent) { this.props.vm.on('PROJECT_RUN_STOP', this.props.onProjectRunStop); this.props.vm.on('PROJECT_CHANGED', this.handleProjectChanged); this.props.vm.on('RUNTIME_STARTED', this.props.onRuntimeStarted); + this.props.vm.on('PROJECT_START', this.props.onGreenFlag); this.props.vm.on('PERIPHERAL_DISCONNECT_ERROR', this.props.onShowExtensionAlert); this.props.vm.on('MIC_LISTENING', this.props.onMicListeningUpdate); @@ -116,6 +117,7 @@ const vmListenerHOC = function (WrappedComponent) { attachKeyboardEvents, shouldEmitTargetsUpdate, onBlockDragUpdate, + onGreenFlag, onKeyDown, onKeyUp, onMicListeningUpdate, @@ -136,6 +138,7 @@ const vmListenerHOC = function (WrappedComponent) { VMListener.propTypes = { attachKeyboardEvents: PropTypes.bool, onBlockDragUpdate: PropTypes.func.isRequired, + onGreenFlag: PropTypes.func, onKeyDown: PropTypes.func, onKeyUp: PropTypes.func, onMicListeningUpdate: PropTypes.func.isRequired, @@ -152,7 +155,8 @@ const vmListenerHOC = function (WrappedComponent) { vm: PropTypes.instanceOf(VM).isRequired }; VMListener.defaultProps = { - attachKeyboardEvents: true + attachKeyboardEvents: true, + onGreenFlag: () => ({}) }; const mapStateToProps = state => ({ // Do not emit target updates in fullscreen or player only mode From 7a521e0e781575a04a4dfa9264e7dd7c09d8730c Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 6 Dec 2018 17:31:38 -0500 Subject: [PATCH 0872/1971] Merge pull request #3975 from LLK/greenkeeper/scratch-l10n-3.1.20181206185857 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4a2ab874cc..f3890fe60d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1544016933", - "scratch-l10n": "3.1.20181129221712", + "scratch-l10n": "3.1.20181206185857", "scratch-paint": "0.2.0-prerelease.20181203192800", "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", From f9c84679910b447c955e5a5179e8127d31990e2a Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 6 Dec 2018 23:09:55 -0500 Subject: [PATCH 0873/1971] Merge pull request #3981 from benjiwheeler/view-count update the scratch-vm we use in package.json --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f3890fe60d..93dbfd6877 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181126212715", - "scratch-vm": "0.2.0-prerelease.20181205175228", + "scratch-vm": "0.2.0-prerelease.20181206210221", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From e0cfe2074f402bb84310a8f2d0188309c33b9075 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Fri, 7 Dec 2018 08:04:43 -0500 Subject: [PATCH 0874/1971] Merge pull request #3974 from benjiwheeler/translate-confirm-alert translate new project confirm alert --- .../scratch-gui/src/components/menu-bar/menu-bar.jsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 878e6a7b42..1a4c05a838 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -66,6 +66,13 @@ import languageIcon from '../language-selector/language-icon.svg'; import scratchLogo from './scratch-logo.svg'; +const messages = defineMessages({ + confirmNav: { + id: 'gui.menuBar.confirmNewWithoutSaving', + defaultMessage: 'Replace contents of the current project?', + description: 'message for prompting user to confirm that they want to create new project without saving' + } +}); const ariaMessages = defineMessages({ language: { id: 'gui.menuBar.LanguageSelector', @@ -153,7 +160,7 @@ class MenuBar extends React.Component { // if canSave===true and canCreateNew===true, it's safe to replace current project, // since we will auto-save first. Else, confirm first. const readyToReplaceProject = (this.props.canSave && this.props.canCreateNew) || - confirm('Replace contents of the current project?'); // eslint-disable-line no-alert + confirm(this.props.intl.formatMessage(messages.confirmNav)); // eslint-disable-line no-alert this.props.onRequestCloseFile(); if (readyToReplaceProject) { this.props.onClickNew(this.props.canSave && this.props.canCreateNew); From d22596f53fbb8a0e92e13a8582c4ef624088c7d4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 7 Dec 2018 08:31:13 -0500 Subject: [PATCH 0875/1971] Merge pull request #3989 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1544187969 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 93dbfd6877..ea6cd58e37 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -103,7 +103,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", - "scratch-blocks": "0.1.0-prerelease.1544016933", + "scratch-blocks": "0.1.0-prerelease.1544187969", "scratch-l10n": "3.1.20181206185857", "scratch-paint": "0.2.0-prerelease.20181203192800", "scratch-render": "0.1.0-prerelease.20181127194508", From 4e0cb24edb11f366da7f95df06eff8f70f63de6c Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Fri, 7 Dec 2018 09:17:08 -0500 Subject: [PATCH 0876/1971] Merge pull request #3937 from benjiwheeler/alert-text use blue creating screen for new projects; revised alert text for copy/remix --- packages/scratch-gui/src/lib/alerts/index.jsx | 86 ++++++++++++++++--- 1 file changed, 76 insertions(+), 10 deletions(-) diff --git a/packages/scratch-gui/src/lib/alerts/index.jsx b/packages/scratch-gui/src/lib/alerts/index.jsx index 53de8f8489..e5c42cce35 100644 --- a/packages/scratch-gui/src/lib/alerts/index.jsx +++ b/packages/scratch-gui/src/lib/alerts/index.jsx @@ -20,10 +20,11 @@ const alerts = [ { alertId: 'createSuccess', alertType: AlertTypes.STANDARD, - clearList: ['createSuccess', 'creating', 'saveSuccess', 'saving'], + clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', + 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], content: ( @@ -32,13 +33,46 @@ const alerts = [ level: AlertLevels.SUCCESS, maxDisplaySecs: 5 }, + { + alertId: 'createCopySuccess', + alertType: AlertTypes.STANDARD, + clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', + 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], + content: ( + + ), + iconURL: successImage, + level: AlertLevels.SUCCESS, + maxDisplaySecs: 5 + }, + { + alertId: 'createRemixSuccess', + alertType: AlertTypes.STANDARD, + clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', + 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], + content: ( + + ), + iconURL: successImage, + level: AlertLevels.SUCCESS, + maxDisplaySecs: 5 + }, { alertId: 'creating', alertType: AlertTypes.STANDARD, - clearList: ['createSuccess', 'creating', 'saveSuccess', 'saving'], + clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', + 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], content: ( @@ -46,9 +80,40 @@ const alerts = [ iconSpinner: true, level: AlertLevels.SUCCESS }, + { + alertId: 'creatingCopy', + alertType: AlertTypes.STANDARD, + clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', + 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], + content: ( + + ), + iconSpinner: true, + level: AlertLevels.SUCCESS + }, + { + alertId: 'creatingRemix', + alertType: AlertTypes.STANDARD, + clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', + 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], + content: ( + + ), + iconSpinner: true, + level: AlertLevels.SUCCESS + }, { alertId: 'creatingError', - clearList: ['creating', 'createSuccess'], + clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', + 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], closeButton: true, content: ( @@ -92,10 +158,10 @@ const alerts = [ { alertId: 'saving', alertType: AlertTypes.INLINE, - clearList: ['createSuccess', 'creating', 'saveSuccess', 'saving', 'savingError'], + clearList: ['saveSuccess', 'saving', 'savingError'], content: ( From 3e036df77be4b5335fdbea75d3c9db20cc951805 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 7 Dec 2018 14:03:27 -0500 Subject: [PATCH 0877/1971] Merge pull request #3993 from paulkaplan/fix-too-much-changing Fix an issue where switching player to editor mode caused "Save Now". --- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index ed9715c31b..dc763bf6f8 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -63,7 +63,7 @@ const vmListenerHOC = function (WrappedComponent) { // Re-request a targets update when the shouldEmitTargetsUpdate state changes to true // i.e. when the editor transitions out of fullscreen/player only modes if (this.props.shouldEmitTargetsUpdate && !prevProps.shouldEmitTargetsUpdate) { - this.props.vm.emitTargetsUpdate(); + this.props.vm.emitTargetsUpdate(false /* Emit the event, but do not trigger project change */); } } componentWillUnmount () { From 7d0e817ce5929d4a0a258a2e06b80001bed60a63 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 7 Dec 2018 15:23:07 -0500 Subject: [PATCH 0878/1971] Merge pull request #3992 from paulkaplan/save-only-for-savers Only show the `SaveStatus` component for people who can save --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 1a4c05a838..95085475ac 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -542,7 +542,9 @@ class MenuBar extends React.Component { logged in, and whether a session is available to log in with */}
- + {this.props.canSave && ( + + )}
{this.props.sessionExists ? ( this.props.username ? ( From 8d16baea2ef9b9acde31fa0bee593eeee610ede5 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 7 Dec 2018 16:09:56 -0500 Subject: [PATCH 0879/1971] Merge pull request #3996 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181207201549 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ea6cd58e37..f161d7bc8c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181126212715", - "scratch-vm": "0.2.0-prerelease.20181206210221", + "scratch-vm": "0.2.0-prerelease.20181207201549", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From a748b534040fa891b9c41d3073c59551444f7607 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 7 Dec 2018 16:46:24 -0500 Subject: [PATCH 0880/1971] Merge pull request #3997 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1544217251 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f161d7bc8c..52aa7bb0c9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -103,7 +103,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", - "scratch-blocks": "0.1.0-prerelease.1544187969", + "scratch-blocks": "0.1.0-prerelease.1544217251", "scratch-l10n": "3.1.20181206185857", "scratch-paint": "0.2.0-prerelease.20181203192800", "scratch-render": "0.1.0-prerelease.20181127194508", From e3b91e1ce18db413a74eb17e450a22a4c8f0b2ab Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 10 Dec 2018 10:26:25 -0500 Subject: [PATCH 0881/1971] Merge pull request #3990 from paulkaplan/fix-video-flipping Only attach the video provider once on the VM --- packages/scratch-gui/src/containers/stage.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 94d4f092fc..c5c36c2e68 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -60,6 +60,9 @@ class Stage extends React.Component { this.renderer = new Renderer(this.canvas); this.props.vm.attachRenderer(this.renderer); + // Only attach a video provider once because it is stateful + this.props.vm.setVideoProvider(new VideoProvider()); + // Calling draw a single time before any project is loaded just makes // the canvas white instead of solid black–needed because it is not // possible to use CSS to style the canvas to have a different @@ -68,7 +71,6 @@ class Stage extends React.Component { } this.props.vm.attachV2SVGAdapter(new V2SVGAdapter()); this.props.vm.attachV2BitmapAdapter(new V2BitmapAdapter()); - this.props.vm.setVideoProvider(new VideoProvider()); } componentDidMount () { this.attachRectEvents(); From a1c35fcb85e1fde55d0b937e20f39013a024ea34 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 10 Dec 2018 10:41:51 -0500 Subject: [PATCH 0882/1971] Merge pull request #4004 from paulkaplan/no-new-tab Do not open a new tab when clicking scratch logo --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 95085475ac..4eac083615 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -298,11 +298,7 @@ class MenuBar extends React.Component {
- + Scratch Date: Mon, 10 Dec 2018 10:42:05 -0500 Subject: [PATCH 0883/1971] Merge pull request #4005 from LLK/greenkeeper/scratch-l10n-3.1.20181210144244 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 52aa7bb0c9..c9508d9d09 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1544217251", - "scratch-l10n": "3.1.20181206185857", + "scratch-l10n": "3.1.20181210144244", "scratch-paint": "0.2.0-prerelease.20181203192800", "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", From dabc8a7b7553cbfd1ee99c24acd58f71f94e5d12 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 10 Dec 2018 10:42:19 -0500 Subject: [PATCH 0884/1971] Merge pull request #4007 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181210152453 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c9508d9d09..dbd8b60d87 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181126212715", - "scratch-vm": "0.2.0-prerelease.20181207201549", + "scratch-vm": "0.2.0-prerelease.20181210152453", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 4ab8103421a83815bd3f0a8b80e7b32d4bb4de86 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 10 Dec 2018 12:54:59 -0500 Subject: [PATCH 0885/1971] Merge pull request #4009 from kchadha/unskip-sprite-upload-test Unskip integration test for #3608. --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index dbd8b60d87..bcc949ba32 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181126212715", - "scratch-vm": "0.2.0-prerelease.20181210152453", + "scratch-vm": "0.2.0-prerelease.20181210154926", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 5313fe8e92cebeca32aa8e5a0159a9ecb8cdcc4f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 11 Dec 2018 16:40:33 -0500 Subject: [PATCH 0886/1971] Merge pull request #4019 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20181211153626 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-paint to version 0.2.0-prerelease.2018… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index bcc949ba32..2592203394 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1544217251", "scratch-l10n": "3.1.20181210144244", - "scratch-paint": "0.2.0-prerelease.20181203192800", + "scratch-paint": "0.2.0-prerelease.20181211153626", "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181126212715", From 0c734c465882c7e8a7f303fbced051ff3bac3832 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Wed, 12 Dec 2018 10:22:22 -0500 Subject: [PATCH 0887/1971] Merge pull request #3969 from benjiwheeler/remove-component-props removed various props from children; renamed prop to be more consistent --- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index dc763bf6f8..abd4ce1b2a 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -123,8 +123,10 @@ const vmListenerHOC = function (WrappedComponent) { onMicListeningUpdate, onMonitorsUpdate, onTargetsUpdate, + onProjectChanged, onProjectRunStart, onProjectRunStop, + onProjectSaved, onRuntimeStarted, onTurboModeOff, onTurboModeOn, From bcc983f5503ab14d05ffc5c7b12bb81287eca939 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 12 Dec 2018 10:31:58 -0500 Subject: [PATCH 0888/1971] Merge pull request #4025 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1544625383 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.154… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2592203394..38d6aa3014 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -103,7 +103,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", - "scratch-blocks": "0.1.0-prerelease.1544217251", + "scratch-blocks": "0.1.0-prerelease.1544625383", "scratch-l10n": "3.1.20181210144244", "scratch-paint": "0.2.0-prerelease.20181211153626", "scratch-render": "0.1.0-prerelease.20181127194508", From cad69d456a25587949b83e04d75aca139f1bea46 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 12 Dec 2018 17:11:22 -0500 Subject: [PATCH 0889/1971] Merge pull request #4031 from paulkaplan/fix-stage-blur Make mousedown on the stage blur inputs, with @picklesrus --- packages/scratch-gui/src/containers/stage.jsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index c5c36c2e68..7cb733b197 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -255,7 +255,12 @@ class Stage extends React.Component { }; this.props.vm.postIOData('mouse', data); if (e.preventDefault) { + // Prevent default to prevent touch from dragging page e.preventDefault(); + // But we do want any active input to be blurred + if (document.activeElement && document.activeElement.blur) { + document.activeElement.blur(); + } } if (this.props.isColorPicking) { const {r, g, b} = this.state.colorInfo.color; From d1a1b4e5deafd90bbdc78455fbfe29ca9d562c82 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 12 Dec 2018 17:11:43 -0500 Subject: [PATCH 0890/1971] Merge pull request #4026 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1544635172 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 38d6aa3014..5bc7cdfa0a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -103,7 +103,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", - "scratch-blocks": "0.1.0-prerelease.1544625383", + "scratch-blocks": "0.1.0-prerelease.1544635172", "scratch-l10n": "3.1.20181210144244", "scratch-paint": "0.2.0-prerelease.20181211153626", "scratch-render": "0.1.0-prerelease.20181127194508", From 34a579762c77dbe8946bdc661a4f165de2158afa Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Wed, 12 Dec 2018 17:50:02 -0500 Subject: [PATCH 0891/1971] Merge pull request #4035 from benjiwheeler/admins-see-author admins will see author credits, not title editing field, in editor --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 4eac083615..a368d48d31 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -459,7 +459,7 @@ class MenuBar extends React.Component {
- {this.props.canEditTitle ? ( + {this.props.canSave && this.props.canEditTitle ? (
Date: Wed, 12 Dec 2018 19:27:47 -0500 Subject: [PATCH 0892/1971] Merge pull request #4034 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20181212190400 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5bc7cdfa0a..0a8d15fd00 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "scratch-paint": "0.2.0-prerelease.20181211153626", "scratch-render": "0.1.0-prerelease.20181127194508", "scratch-storage": "1.2.0", - "scratch-svg-renderer": "0.2.0-prerelease.20181126212715", + "scratch-svg-renderer": "0.2.0-prerelease.20181212230607", "scratch-vm": "0.2.0-prerelease.20181210154926", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From b2d424356db31ade29b929adf877f973535a1ade Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 12 Dec 2018 19:58:22 -0500 Subject: [PATCH 0893/1971] Merge pull request #4053 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20181212233715 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0a8d15fd00..b91ea22625 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "scratch-blocks": "0.1.0-prerelease.1544635172", "scratch-l10n": "3.1.20181210144244", "scratch-paint": "0.2.0-prerelease.20181211153626", - "scratch-render": "0.1.0-prerelease.20181127194508", + "scratch-render": "0.1.0-prerelease.20181212233715", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181212230607", "scratch-vm": "0.2.0-prerelease.20181210154926", From 65110f57423dcb1457e47c5b8a6d257f32c0e8e4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 12 Dec 2018 19:59:29 -0500 Subject: [PATCH 0894/1971] Merge pull request #4052 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20181212232948 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b91ea22625..5fa563d176 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1544635172", "scratch-l10n": "3.1.20181210144244", - "scratch-paint": "0.2.0-prerelease.20181211153626", + "scratch-paint": "0.2.0-prerelease.20181212232948", "scratch-render": "0.1.0-prerelease.20181212233715", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181212230607", From 946ead6f01ad96ee07ba4b44149f84ed1136ba20 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 13 Dec 2018 07:26:18 -0500 Subject: [PATCH 0895/1971] Merge pull request #4039 from rschamp/on-click-logo Add onClick prop for the logo --- .../src/components/menu-bar/menu-bar.jsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index a368d48d31..7e1bf25094 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -298,14 +298,15 @@ class MenuBar extends React.Component {
Date: Thu, 13 Dec 2018 08:43:20 -0500 Subject: [PATCH 0896/1971] Merge pull request #4058 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181213022307 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2018121… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5fa563d176..76583a178b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-render": "0.1.0-prerelease.20181212233715", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181212230607", - "scratch-vm": "0.2.0-prerelease.20181210154926", + "scratch-vm": "0.2.0-prerelease.20181213022307", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From dd531da5a72e6b17eafdac9f7ea548f3be65b949 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 13 Dec 2018 08:43:55 -0500 Subject: [PATCH 0897/1971] Merge pull request #4059 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1544667276 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.154… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 76583a178b..8de55461ec 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -103,7 +103,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", - "scratch-blocks": "0.1.0-prerelease.1544635172", + "scratch-blocks": "0.1.0-prerelease.1544667276", "scratch-l10n": "3.1.20181210144244", "scratch-paint": "0.2.0-prerelease.20181212232948", "scratch-render": "0.1.0-prerelease.20181212233715", From b7810f2fd79ebd9a7693ce25685de0556843d89f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 13 Dec 2018 11:07:40 -0500 Subject: [PATCH 0898/1971] Merge pull request #4060 from LLK/greenkeeper/scratch-l10n-3.1.20181213140204 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8de55461ec..9d620dd0de 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1544667276", - "scratch-l10n": "3.1.20181210144244", + "scratch-l10n": "3.1.20181213140204", "scratch-paint": "0.2.0-prerelease.20181212232948", "scratch-render": "0.1.0-prerelease.20181212233715", "scratch-storage": "1.2.0", From 46ab6fda3cc76c729f2a0d9e982f28ca18b282f2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 13 Dec 2018 11:54:51 -0500 Subject: [PATCH 0899/1971] Merge pull request #4062 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181213163414 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9d620dd0de..f2576e7d93 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-render": "0.1.0-prerelease.20181212233715", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181212230607", - "scratch-vm": "0.2.0-prerelease.20181213022307", + "scratch-vm": "0.2.0-prerelease.20181213163414", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From ecaab96778f3cc681ad469422389d71f0c04c4c4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 13 Dec 2018 11:57:40 -0500 Subject: [PATCH 0900/1971] Merge pull request #4066 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20181213165142 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f2576e7d93..a51aa3e65f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "scratch-paint": "0.2.0-prerelease.20181212232948", "scratch-render": "0.1.0-prerelease.20181212233715", "scratch-storage": "1.2.0", - "scratch-svg-renderer": "0.2.0-prerelease.20181212230607", + "scratch-svg-renderer": "0.2.0-prerelease.20181213165142", "scratch-vm": "0.2.0-prerelease.20181213163414", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From a2b3fcc5ced3603b2ea875e38861b571d7408947 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Thu, 13 Dec 2018 12:39:37 -0500 Subject: [PATCH 0901/1971] Merge pull request #4024 from towerofnix/conditional-menu-section Fix empty --- .../src/components/menu-bar/menu-bar.jsx | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 7e1bf25094..d8793b111c 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -348,23 +348,25 @@ class MenuBar extends React.Component { {newProjectMessage} - - {this.props.canSave ? ( - - {saveNowMessage} - - ) : []} - {this.props.canCreateCopy ? ( - - {createCopyMessage} - - ) : []} - {this.props.canRemix ? ( - - {remixMessage} - - ) : []} - + {(this.props.canSave || this.props.canCreateCopy || this.props.canRemix) && ( + + {this.props.canSave ? ( + + {saveNowMessage} + + ) : []} + {this.props.canCreateCopy ? ( + + {createCopyMessage} + + ) : []} + {this.props.canRemix ? ( + + {remixMessage} + + ) : []} + + )} {(className, renderFileInput, loadProject) => ( From 610a9694d18bd392deefdd0d77d5fcae513e705e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 13 Dec 2018 15:03:39 -0500 Subject: [PATCH 0902/1971] Merge pull request #4068 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1544721188 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a51aa3e65f..c094348532 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -103,7 +103,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", - "scratch-blocks": "0.1.0-prerelease.1544667276", + "scratch-blocks": "0.1.0-prerelease.1544721188", "scratch-l10n": "3.1.20181213140204", "scratch-paint": "0.2.0-prerelease.20181212232948", "scratch-render": "0.1.0-prerelease.20181212233715", From 4ea5da06c46073113444b7f56099780c976b7fdd Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 13 Dec 2018 15:03:53 -0500 Subject: [PATCH 0903/1971] Merge pull request #3984 from towerofnix/treat-defaultValue-properly Treat defaultValue from Blockly prompts properly --- packages/scratch-gui/src/containers/blocks.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 17850ed981..80cc87167d 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -460,9 +460,9 @@ class Blocks extends React.Component { /> {this.state.prompt ? ( Date: Thu, 13 Dec 2018 15:04:22 -0500 Subject: [PATCH 0904/1971] Merge pull request #4075 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181213192047 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2018121… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c094348532..184be9f4ff 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-render": "0.1.0-prerelease.20181212233715", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181213165142", - "scratch-vm": "0.2.0-prerelease.20181213163414", + "scratch-vm": "0.2.0-prerelease.20181213192047", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From e86d1da63a8c39cad98bea5c9236494cef752fee Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 13 Dec 2018 15:07:23 -0500 Subject: [PATCH 0905/1971] Merge pull request #4070 from LLK/greenkeeper/scratch-l10n-3.1.20181213173343 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 184be9f4ff..3aa4da986f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1544721188", - "scratch-l10n": "3.1.20181213140204", + "scratch-l10n": "3.1.20181213173343", "scratch-paint": "0.2.0-prerelease.20181212232948", "scratch-render": "0.1.0-prerelease.20181212233715", "scratch-storage": "1.2.0", From d4bd5a9549b45d6cbb7ededb8a32a2684128fb2c Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Thu, 13 Dec 2018 15:49:17 -0500 Subject: [PATCH 0906/1971] Merge pull request #4033 from benjiwheeler/close-file-menu2 close file menu on uploading sb file; don't close file menu twice --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index d8793b111c..21ebc084c5 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -161,7 +161,6 @@ class MenuBar extends React.Component { // since we will auto-save first. Else, confirm first. const readyToReplaceProject = (this.props.canSave && this.props.canCreateNew) || confirm(this.props.intl.formatMessage(messages.confirmNav)); // eslint-disable-line no-alert - this.props.onRequestCloseFile(); if (readyToReplaceProject) { this.props.onClickNew(this.props.canSave && this.props.canCreateNew); } From eb48d85c71a9e720490c1c220104168f9fd62502 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 13 Dec 2018 16:04:03 -0500 Subject: [PATCH 0907/1971] Merge pull request #4076 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20181213192400 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3aa4da986f..05b97c7e53 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "scratch-paint": "0.2.0-prerelease.20181212232948", "scratch-render": "0.1.0-prerelease.20181212233715", "scratch-storage": "1.2.0", - "scratch-svg-renderer": "0.2.0-prerelease.20181213165142", + "scratch-svg-renderer": "0.2.0-prerelease.20181213192400", "scratch-vm": "0.2.0-prerelease.20181213192047", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From c92324dc0e83cae59f8f58987d25a81fd763fae4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 14 Dec 2018 08:39:34 -0500 Subject: [PATCH 0908/1971] Merge pull request #4084 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181213211546 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 05b97c7e53..55571a8a7d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-render": "0.1.0-prerelease.20181212233715", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181213192400", - "scratch-vm": "0.2.0-prerelease.20181213192047", + "scratch-vm": "0.2.0-prerelease.20181213211546", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 37000422f90c262968aa902baa40e71ada78458b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 14 Dec 2018 08:39:47 -0500 Subject: [PATCH 0909/1971] Merge pull request #4083 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20181213210237 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 55571a8a7d..d801b447de 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1544721188", "scratch-l10n": "3.1.20181213173343", - "scratch-paint": "0.2.0-prerelease.20181212232948", + "scratch-paint": "0.2.0-prerelease.20181213210237", "scratch-render": "0.1.0-prerelease.20181212233715", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181213192400", From 46ac285bb6dc18ff09c78acc3812dcd521851731 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 14 Dec 2018 08:40:14 -0500 Subject: [PATCH 0910/1971] Merge pull request #4082 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20181213205951 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d801b447de..2ae095bc8d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "scratch-blocks": "0.1.0-prerelease.1544721188", "scratch-l10n": "3.1.20181213173343", "scratch-paint": "0.2.0-prerelease.20181213210237", - "scratch-render": "0.1.0-prerelease.20181212233715", + "scratch-render": "0.1.0-prerelease.20181213205951", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181213192400", "scratch-vm": "0.2.0-prerelease.20181213211546", From 96792de89c401993bbcd00f7ba3f9ecaa8bacbe9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 14 Dec 2018 08:40:46 -0500 Subject: [PATCH 0911/1971] Merge pull request #4086 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1544731479 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.154… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2ae095bc8d..68fd438c29 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -103,7 +103,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", - "scratch-blocks": "0.1.0-prerelease.1544721188", + "scratch-blocks": "0.1.0-prerelease.1544731479", "scratch-l10n": "3.1.20181213173343", "scratch-paint": "0.2.0-prerelease.20181213210237", "scratch-render": "0.1.0-prerelease.20181213205951", From ca344af86e536bc2636520d9ec4417139a0ab040 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 14 Dec 2018 12:23:36 -0500 Subject: [PATCH 0912/1971] Merge pull request #4088 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20181214155149 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 68fd438c29..695a809107 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1544731479", "scratch-l10n": "3.1.20181213173343", - "scratch-paint": "0.2.0-prerelease.20181213210237", + "scratch-paint": "0.2.0-prerelease.20181214155149", "scratch-render": "0.1.0-prerelease.20181213205951", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181213192400", From 6eb3e4d01296f0b2aeb7f8db70b962bfe267b51f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 14 Dec 2018 16:35:43 -0500 Subject: [PATCH 0913/1971] Merge pull request #4092 from LLK/revert-4035-admin-sees-author Revert 4035 admin sees author --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 21ebc084c5..5cf6cb6bbf 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -461,7 +461,7 @@ class MenuBar extends React.Component {
- {this.props.canSave && this.props.canEditTitle ? ( + {this.props.canEditTitle ? (
Date: Fri, 14 Dec 2018 16:45:20 -0500 Subject: [PATCH 0914/1971] Merge pull request #3866 from benjiwheeler/dont-prompt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit only prompt user to save if project has changed and won’t autosave --- .../src/components/menu-bar/menu-bar.jsx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 5cf6cb6bbf..825f6096f9 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -157,10 +157,18 @@ class MenuBar extends React.Component { ]); } handleClickNew () { - // if canSave===true and canCreateNew===true, it's safe to replace current project, - // since we will auto-save first. Else, confirm first. - const readyToReplaceProject = (this.props.canSave && this.props.canCreateNew) || - confirm(this.props.intl.formatMessage(messages.confirmNav)); // eslint-disable-line no-alert + let readyToReplaceProject = true; + // if the project is dirty, and user owns the project, we will autosave. + // but if they are not logged in and can't save, user should consider + // downloading or logging in first. + // Note that if user is logged in and editing someone else's project, + // they'll lose their work. + if (this.props.projectChanged && !this.props.canCreateNew) { + readyToReplaceProject = confirm( // eslint-disable-line no-alert + this.props.intl.formatMessage(messages.confirmNav) + ); + } + this.props.onRequestCloseFile(); if (readyToReplaceProject) { this.props.onClickNew(this.props.canSave && this.props.canCreateNew); } @@ -734,6 +742,7 @@ MenuBar.propTypes = { onShare: PropTypes.func, onToggleLoginOpen: PropTypes.func, onUpdateProjectTitle: PropTypes.func, + projectChanged: PropTypes.bool, projectTitle: PropTypes.string, renderLogin: PropTypes.func, sessionExists: PropTypes.bool, @@ -757,6 +766,7 @@ const mapStateToProps = state => { isShowingProject: getIsShowingProject(loadingState), languageMenuOpen: languageMenuOpen(state), loginMenuOpen: loginMenuOpen(state), + projectChanged: state.scratchGui.projectChanged, projectTitle: state.scratchGui.projectTitle, sessionExists: state.session && typeof state.session.session !== 'undefined', username: user ? user.username : null From 134ed565c51c96229f730432e50c1fa75cb0b4a9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 17 Dec 2018 13:38:24 -0500 Subject: [PATCH 0915/1971] Merge pull request #4102 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20181217155806 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 695a809107..d5dd48cb03 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1544731479", "scratch-l10n": "3.1.20181213173343", - "scratch-paint": "0.2.0-prerelease.20181214155149", + "scratch-paint": "0.2.0-prerelease.20181217155806", "scratch-render": "0.1.0-prerelease.20181213205951", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181213192400", From da409e8144029775ecf0bcfe452550298ed27ab9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 17 Dec 2018 14:37:59 -0500 Subject: [PATCH 0916/1971] Merge pull request #4105 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181217191056 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d5dd48cb03..a1b2cea7b1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-render": "0.1.0-prerelease.20181213205951", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181213192400", - "scratch-vm": "0.2.0-prerelease.20181213211546", + "scratch-vm": "0.2.0-prerelease.20181217191056", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 25573cbdb5fb248f86c80bdfffdc7467fcade40d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 18 Dec 2018 10:42:54 -0500 Subject: [PATCH 0917/1971] Merge pull request #4122 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181218150038 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a1b2cea7b1..c9484a4861 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-render": "0.1.0-prerelease.20181213205951", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181213192400", - "scratch-vm": "0.2.0-prerelease.20181217191056", + "scratch-vm": "0.2.0-prerelease.20181218150038", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 3684c8871dfb1f1acbe6ed497aef62e0b0bd0ac2 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 18 Dec 2018 12:11:57 -0500 Subject: [PATCH 0918/1971] Merge pull request #4124 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20181218153528 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c9484a4861..06b98aee24 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "scratch-paint": "0.2.0-prerelease.20181217155806", "scratch-render": "0.1.0-prerelease.20181213205951", "scratch-storage": "1.2.0", - "scratch-svg-renderer": "0.2.0-prerelease.20181213192400", + "scratch-svg-renderer": "0.2.0-prerelease.20181218153528", "scratch-vm": "0.2.0-prerelease.20181218150038", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 854fd42ac5c547cb4bff51110e707ee764dd002b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 18 Dec 2018 12:26:10 -0500 Subject: [PATCH 0919/1971] Merge pull request #4130 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181218163005 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 06b98aee24..662a79741f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-render": "0.1.0-prerelease.20181213205951", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181218153528", - "scratch-vm": "0.2.0-prerelease.20181218150038", + "scratch-vm": "0.2.0-prerelease.20181218163005", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From b91bf20cebdcc7b932b6341b20cea0d02abc7ee7 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 18 Dec 2018 12:26:25 -0500 Subject: [PATCH 0920/1971] Merge pull request #4129 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20181218161344 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 662a79741f..9b6d9ad8c4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1544731479", "scratch-l10n": "3.1.20181213173343", - "scratch-paint": "0.2.0-prerelease.20181217155806", + "scratch-paint": "0.2.0-prerelease.20181218161344", "scratch-render": "0.1.0-prerelease.20181213205951", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181218153528", From 9477be2a447bbc19892214fafce21a754ae719a4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 18 Dec 2018 12:27:00 -0500 Subject: [PATCH 0921/1971] Merge pull request #4128 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20181218160410 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9b6d9ad8c4..ddfb2c8906 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,8 +105,7 @@ "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1544731479", "scratch-l10n": "3.1.20181213173343", - "scratch-paint": "0.2.0-prerelease.20181218161344", - "scratch-render": "0.1.0-prerelease.20181213205951", + "scratch-render": "0.1.0-prerelease.20181218160410", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181218153528", "scratch-vm": "0.2.0-prerelease.20181218163005", From d0ce1dd01ae5258a743ef9a8068c3e634b421505 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 18 Dec 2018 12:37:54 -0500 Subject: [PATCH 0922/1971] Fix removed package from #4128 --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ddfb2c8906..a0ca7c2a39 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,6 +105,7 @@ "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1544731479", "scratch-l10n": "3.1.20181213173343", + "scratch-paint": "0.2.0-prerelease.20181218161344" "scratch-render": "0.1.0-prerelease.20181218160410", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181218153528", From a287694ecc76940bacdef7bddf67cd6ba91f102a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 18 Dec 2018 12:38:28 -0500 Subject: [PATCH 0923/1971] Fix comma error from previous commit --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a0ca7c2a39..cc7dc57cb4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1544731479", "scratch-l10n": "3.1.20181213173343", - "scratch-paint": "0.2.0-prerelease.20181218161344" + "scratch-paint": "0.2.0-prerelease.20181218161344", "scratch-render": "0.1.0-prerelease.20181218160410", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181218153528", From 6e8f993dea1385ef014551832c04b8e8377f6743 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 18 Dec 2018 13:15:13 -0500 Subject: [PATCH 0924/1971] Merge pull request #4131 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181218172555 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cc7dc57cb4..0433823a8f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-render": "0.1.0-prerelease.20181218160410", "scratch-storage": "1.2.0", "scratch-svg-renderer": "0.2.0-prerelease.20181218153528", - "scratch-vm": "0.2.0-prerelease.20181218163005", + "scratch-vm": "0.2.0-prerelease.20181218172555", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 610ce314a2ba5edc28e9fad9e41a4e9308405b6e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 18 Dec 2018 15:45:57 -0500 Subject: [PATCH 0925/1971] Merge pull request #4135 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1545162154 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0433823a8f..c13702732c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -103,7 +103,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", - "scratch-blocks": "0.1.0-prerelease.1544731479", + "scratch-blocks": "0.1.0-prerelease.1545162154", "scratch-l10n": "3.1.20181213173343", "scratch-paint": "0.2.0-prerelease.20181218161344", "scratch-render": "0.1.0-prerelease.20181218160410", From b1d2e49af7c5c6033d65fbc766089a6f9f54f831 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 19 Dec 2018 09:01:35 -0500 Subject: [PATCH 0926/1971] Merge pull request #4115 from towerofnix/fix-touchevent-error Fix "TouchEvent is not defined" error --- packages/scratch-gui/src/containers/stage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 7cb733b197..a6204bab7c 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -236,7 +236,7 @@ class Stage extends React.Component { this.updateRect(); const {x, y} = getEventXY(e); const mousePosition = [x - this.rect.left, y - this.rect.top]; - if (e.button === 0 || e instanceof TouchEvent) { + if (e.button === 0 || (window.TouchEvent && e instanceof TouchEvent)) { this.setState({ mouseDown: true, mouseDownPosition: mousePosition, From 3aad5ca7a1a0cd470bb6d357aff701851ad236df Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 19 Dec 2018 14:25:41 -0500 Subject: [PATCH 0927/1971] Merge pull request #4150 from ktbee/update-scratch-storage Update scratch-storage package --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c13702732c..aae3ead73d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-l10n": "3.1.20181213173343", "scratch-paint": "0.2.0-prerelease.20181218161344", "scratch-render": "0.1.0-prerelease.20181218160410", - "scratch-storage": "1.2.0", + "scratch-storage": "1.2.1", "scratch-svg-renderer": "0.2.0-prerelease.20181218153528", "scratch-vm": "0.2.0-prerelease.20181218172555", "selenium-webdriver": "3.6.0", From 19eeb8cedd8ec3e0196173691405a58e6094db33 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 20 Dec 2018 08:29:56 -0500 Subject: [PATCH 0928/1971] Merge pull request #4156 from paulkaplan/only-trigger-saves-from-editor Ignore projectChange updates from the VM when not in editor mode --- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index abd4ce1b2a..8c80de40ae 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -74,10 +74,12 @@ const vmListenerHOC = function (WrappedComponent) { } } handleProjectChanged () { - this.props.onProjectChanged(); + if (this.props.shouldEmitUpdates) { + this.props.onProjectChanged(); + } } handleTargetsUpdate (data) { - if (this.props.shouldEmitTargetsUpdate) { + if (this.props.shouldEmitUpdates) { this.props.onTargetsUpdate(data); } } @@ -115,7 +117,7 @@ const vmListenerHOC = function (WrappedComponent) { const { /* eslint-disable no-unused-vars */ attachKeyboardEvents, - shouldEmitTargetsUpdate, + shouldEmitUpdates, onBlockDragUpdate, onGreenFlag, onKeyDown, @@ -152,7 +154,7 @@ const vmListenerHOC = function (WrappedComponent) { onTargetsUpdate: PropTypes.func.isRequired, onTurboModeOff: PropTypes.func.isRequired, onTurboModeOn: PropTypes.func.isRequired, - shouldEmitTargetsUpdate: PropTypes.bool, + shouldEmitUpdates: PropTypes.bool, username: PropTypes.string, vm: PropTypes.instanceOf(VM).isRequired }; @@ -161,8 +163,8 @@ const vmListenerHOC = function (WrappedComponent) { onGreenFlag: () => ({}) }; const mapStateToProps = state => ({ - // Do not emit target updates in fullscreen or player only mode - shouldEmitTargetsUpdate: !state.scratchGui.mode.isFullScreen && !state.scratchGui.mode.isPlayerOnly, + // Do not emit target or project updates in fullscreen or player only mode + shouldEmitUpdates: !state.scratchGui.mode.isFullScreen && !state.scratchGui.mode.isPlayerOnly, vm: state.scratchGui.vm, username: state.session && state.session.session && state.session.session.user ? state.session.session.user.username : '' From 8e86c3c4266ffe87564ad9a1c34d675c04112e02 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 20 Dec 2018 08:30:14 -0500 Subject: [PATCH 0929/1971] Merge pull request #4157 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181219200003 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index aae3ead73d..31f62ba013 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-render": "0.1.0-prerelease.20181218160410", "scratch-storage": "1.2.1", "scratch-svg-renderer": "0.2.0-prerelease.20181218153528", - "scratch-vm": "0.2.0-prerelease.20181218172555", + "scratch-vm": "0.2.0-prerelease.20181219200003", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 28b82c5a1079549c82e5b5cac54d02907706a5ba Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 20 Dec 2018 13:22:22 -0500 Subject: [PATCH 0930/1971] Merge pull request #4166 from paulkaplan/reduce-analytics-events Reduce analytics events --- packages/scratch-gui/src/containers/blocks.jsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 80cc87167d..e6eebec41b 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -7,7 +7,6 @@ import React from 'react'; import VMScratchBlocks from '../lib/blocks'; import VM from 'scratch-vm'; -import analytics from '../lib/analytics'; import log from '../lib/log.js'; import Prompt from './prompt.jsx'; import BlocksComponent from '../components/blocks/blocks.jsx'; @@ -113,8 +112,6 @@ class Blocks extends React.Component { if (this.props.isVisible) { this.setLocale(); } - - analytics.pageview('/editors/blocks'); } shouldComponentUpdate (nextProps, nextState) { return ( From 8d2e0f4c8bd4991fa434130aad41df08c9913b3f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 20 Dec 2018 13:35:54 -0500 Subject: [PATCH 0931/1971] Merge pull request #4146 from paulkaplan/list-import Implement list import and export from files --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 31f62ba013..3296c29e9d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -76,6 +76,7 @@ "lodash.throttle": "4.0.1", "minilog": "3.1.0", "mkdirp": "^0.5.1", + "papaparse": "4.6.2", "postcss-import": "^12.0.0", "postcss-loader": "^3.0.0", "postcss-simple-vars": "^5.0.1", From ab1108f9c8eebb52e00afa842546ce4a10ac7b22 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 20 Dec 2018 15:40:13 -0500 Subject: [PATCH 0932/1971] Merge pull request #4171 from fsih/updateNewlines Update deps to preserve spaces on stage --- packages/scratch-gui/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3296c29e9d..d4334a8c17 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,10 +106,10 @@ "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1545162154", "scratch-l10n": "3.1.20181213173343", - "scratch-paint": "0.2.0-prerelease.20181218161344", - "scratch-render": "0.1.0-prerelease.20181218160410", + "scratch-paint": "0.2.0-prerelease.20181220194927", + "scratch-render": "0.1.0-prerelease.20181220195236", "scratch-storage": "1.2.1", - "scratch-svg-renderer": "0.2.0-prerelease.20181218153528", + "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", "scratch-vm": "0.2.0-prerelease.20181219200003", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From a7da0ba54701136e74a9e7a97599f594acb73ff5 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 20 Dec 2018 16:01:29 -0500 Subject: [PATCH 0933/1971] Merge pull request #4136 from kchadha/accept-sb The file uploader file input should accept .sb files as well. --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d4334a8c17..fd356bbffa 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20181220195236", "scratch-storage": "1.2.1", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", - "scratch-vm": "0.2.0-prerelease.20181219200003", + "scratch-vm": "0.2.0-prerelease.20181220195716", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 40d0efd61fee71798d41b1eb675e7bfd36aec032 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 20 Dec 2018 22:11:56 -0500 Subject: [PATCH 0934/1971] Merge pull request #4175 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181221030743 chore(package): update scratch-vm to version 0.2.0-prerelease.20181221030743 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index fd356bbffa..740342f88a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20181220195236", "scratch-storage": "1.2.1", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", - "scratch-vm": "0.2.0-prerelease.20181220195716", + "scratch-vm": "0.2.0-prerelease.20181221030743", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From b657e45a47a0a120e882166e812fc1c785676970 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 20 Dec 2018 22:52:10 -0500 Subject: [PATCH 0935/1971] Merge pull request #4176 from LLK/greenkeeper/scratch-l10n-3.1.20181220222259 chore(package): update scratch-l10n to version 3.1.20181220222259 Removes the three languages from the menu that still need to be reviewed --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 740342f88a..576ec254de 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", "scratch-blocks": "0.1.0-prerelease.1545162154", - "scratch-l10n": "3.1.20181213173343", + "scratch-l10n": "3.1.20181220222259", "scratch-paint": "0.2.0-prerelease.20181220194927", "scratch-render": "0.1.0-prerelease.20181220195236", "scratch-storage": "1.2.1", From 796cadc790c5749a7074f092fde600c03344fb93 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Fri, 21 Dec 2018 09:04:09 -0500 Subject: [PATCH 0936/1971] Merge pull request #4178 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1545364693 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 576ec254de..d5c6c551bf 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20181023202904", - "scratch-blocks": "0.1.0-prerelease.1545162154", + "scratch-blocks": "0.1.0-prerelease.1545364693", "scratch-l10n": "3.1.20181220222259", "scratch-paint": "0.2.0-prerelease.20181220194927", "scratch-render": "0.1.0-prerelease.20181220195236", From 043a8383199bd38c880f71c7bb9b610dacc1474e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 21 Dec 2018 13:11:56 -0500 Subject: [PATCH 0937/1971] Merge pull request #4182 from LLK/paulkaplan-patch-1 Update to storage 1.2.2 for clean asset flag fix --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d5c6c551bf..3c59c42b06 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "scratch-l10n": "3.1.20181220222259", "scratch-paint": "0.2.0-prerelease.20181220194927", "scratch-render": "0.1.0-prerelease.20181220195236", - "scratch-storage": "1.2.1", + "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", "scratch-vm": "0.2.0-prerelease.20181221030743", "selenium-webdriver": "3.6.0", From a6dd12c341bc2c53c4751f92c1a3964812c810bd Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 21 Dec 2018 14:49:08 -0500 Subject: [PATCH 0938/1971] Merge pull request #4183 from kchadha/update-vm Update scratch-vm to pull in changes from scratch-sb1-converter --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3c59c42b06..1fa650f286 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20181220195236", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", - "scratch-vm": "0.2.0-prerelease.20181221030743", + "scratch-vm": "0.2.0-prerelease.20181221194157", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From b9234588ce7eb34e720f30130cfe7a577bc4f71e Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 21 Dec 2018 16:18:13 -0500 Subject: [PATCH 0939/1971] Merge pull request #4185 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181221211506 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1fa650f286..9f54de22e9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20181220195236", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", - "scratch-vm": "0.2.0-prerelease.20181221194157", + "scratch-vm": "0.2.0-prerelease.20181221211506", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 2c3c4f7459b480bb90bd78b943a5e6ef600e47f6 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 26 Dec 2018 10:44:27 -0500 Subject: [PATCH 0940/1971] Merge pull request #4200 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181226154000 chore(package): update scratch-vm to version 0.2.0-prerelease.20181226154000 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9f54de22e9..a4654e7f81 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20181220195236", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", - "scratch-vm": "0.2.0-prerelease.20181221211506", + "scratch-vm": "0.2.0-prerelease.20181226154000", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 435fcf0163be0e12d00cc989874916804b56eb95 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 26 Dec 2018 10:45:42 -0500 Subject: [PATCH 0941/1971] Merge pull request #4199 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20181226153401 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a4654e7f81..b2f3cb235f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-blocks": "0.1.0-prerelease.1545364693", "scratch-l10n": "3.1.20181220222259", "scratch-paint": "0.2.0-prerelease.20181220194927", - "scratch-render": "0.1.0-prerelease.20181220195236", + "scratch-render": "0.1.0-prerelease.20181226153401", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", "scratch-vm": "0.2.0-prerelease.20181226154000", From 793265f316f516faadf4ec270c3b57ed02f31d98 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 26 Dec 2018 16:43:19 -0500 Subject: [PATCH 0942/1971] Merge pull request #4205 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181226213940 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b2f3cb235f..625e7a037f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20181226153401", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", - "scratch-vm": "0.2.0-prerelease.20181226154000", + "scratch-vm": "0.2.0-prerelease.20181226213940", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From c7042e66e21f8dae4d877c919608eca072624f03 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 28 Dec 2018 10:10:24 -0500 Subject: [PATCH 0943/1971] Merge pull request #4215 from paulkaplan/pin-eslint-plugin-react Pin eslint-plugin-react because v7.12 has a bug causing erroneous fails --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 625e7a037f..144233e36f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -56,7 +56,7 @@ "eslint": "^5.0.1", "eslint-config-scratch": "^5.0.0", "eslint-plugin-import": "^2.8.0", - "eslint-plugin-react": "^7.5.1", + "eslint-plugin-react": "7.11.1", "file-loader": "2.0.0", "get-float-time-domain-data": "0.1.0", "get-user-media-promise": "1.1.4", From 6f1875471bcfd7f77ecbf298792c150b89eda95a Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 2 Jan 2019 12:56:23 -0500 Subject: [PATCH 0944/1971] Merge pull request #4223 from LLK/rschamp-patch-1 Bump scratch-vm --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 144233e36f..6a618cbcf2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20181226153401", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", - "scratch-vm": "0.2.0-prerelease.20181226213940", + "scratch-vm": "0.2.0-prerelease.20190102175344", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 1f43e8257fbf35f30ab710cdb61e6ec7234e467f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 3 Jan 2019 14:43:06 -0500 Subject: [PATCH 0945/1971] Merge pull request #4246 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190103190631 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6a618cbcf2..039ed7117b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-blocks": "0.1.0-prerelease.1545364693", "scratch-l10n": "3.1.20181220222259", "scratch-paint": "0.2.0-prerelease.20181220194927", - "scratch-render": "0.1.0-prerelease.20181226153401", + "scratch-render": "0.1.0-prerelease.20190103190631", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", "scratch-vm": "0.2.0-prerelease.20190102175344", From b023e28ff09644ec97e96024944586d400279953 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 7 Jan 2019 13:20:30 -0500 Subject: [PATCH 0946/1971] Merge pull request #4279 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190107160043 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 039ed7117b..72b7b22e28 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190103190631", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", - "scratch-vm": "0.2.0-prerelease.20190102175344", + "scratch-vm": "0.2.0-prerelease.20190107160043", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From b5653a9f344611a77f0ee3e9e481afa62bb4b0bf Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 7 Jan 2019 13:49:29 -0500 Subject: [PATCH 0947/1971] Merge pull request #4281 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190107183612 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 72b7b22e28..5891881fae 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190103190631", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", - "scratch-vm": "0.2.0-prerelease.20190107160043", + "scratch-vm": "0.2.0-prerelease.20190107183612", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 769608a2138e5c3da1d0c1e8dcb6d8ed90d3d1c2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 7 Jan 2019 13:49:40 -0500 Subject: [PATCH 0948/1971] Merge pull request #4280 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190107163047 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5891881fae..5c7eb113f1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-blocks": "0.1.0-prerelease.1545364693", "scratch-l10n": "3.1.20181220222259", "scratch-paint": "0.2.0-prerelease.20181220194927", - "scratch-render": "0.1.0-prerelease.20190103190631", + "scratch-render": "0.1.0-prerelease.20190107163047", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", "scratch-vm": "0.2.0-prerelease.20190107183612", From 207d64e95df848e8ad4548c58adcef0edfce4bdc Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 7 Jan 2019 14:05:47 -0500 Subject: [PATCH 0949/1971] Merge pull request #4282 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190107184530 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5c7eb113f1..1736a0c3a2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190107163047", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", - "scratch-vm": "0.2.0-prerelease.20190107183612", + "scratch-vm": "0.2.0-prerelease.20190107184530", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From ac28e7a30ecd1c445a80ed630b81c082c2d0b12d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 8 Jan 2019 13:27:38 -0500 Subject: [PATCH 0950/1971] Merge pull request #4289 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190108175224 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1736a0c3a2..f267d6b3e7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-blocks": "0.1.0-prerelease.1545364693", "scratch-l10n": "3.1.20181220222259", "scratch-paint": "0.2.0-prerelease.20181220194927", - "scratch-render": "0.1.0-prerelease.20190107163047", + "scratch-render": "0.1.0-prerelease.20190108175224", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", "scratch-vm": "0.2.0-prerelease.20190107184530", From db1ffb08229ee0f7aba3f2ddab702380f972d3bf Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 8 Jan 2019 13:28:23 -0500 Subject: [PATCH 0951/1971] Merge pull request #4290 from LLK/greenkeeper/scratch-audio-0.1.0-prerelease.20190108181031 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-audio to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f267d6b3e7..b272a080e2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -103,7 +103,7 @@ "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "0.1.0-prerelease.20181023202904", + "scratch-audio": "0.1.0-prerelease.20190108181031", "scratch-blocks": "0.1.0-prerelease.1545364693", "scratch-l10n": "3.1.20181220222259", "scratch-paint": "0.2.0-prerelease.20181220194927", From 76025473f259eec37a977e333c3cde8b3e8dbe3b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 8 Jan 2019 15:53:41 -0500 Subject: [PATCH 0952/1971] Merge pull request #4295 from LLK/revert-4289-greenkeeper/scratch-render-0.1.0-prerelease.20190108175224 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "Update scratch-render to the latest version 🚀" --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b272a080e2..c6755f40a5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-blocks": "0.1.0-prerelease.1545364693", "scratch-l10n": "3.1.20181220222259", "scratch-paint": "0.2.0-prerelease.20181220194927", - "scratch-render": "0.1.0-prerelease.20190108175224", + "scratch-render": "0.1.0-prerelease.20190107163047", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", "scratch-vm": "0.2.0-prerelease.20190107184530", From c5442e7f372b83a2172d08d2dcbfb5174f637bd7 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 9 Jan 2019 14:50:40 -0500 Subject: [PATCH 0953/1971] Merge pull request #4299 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190108223629 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c6755f40a5..d8114646ff 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-blocks": "0.1.0-prerelease.1545364693", "scratch-l10n": "3.1.20181220222259", "scratch-paint": "0.2.0-prerelease.20181220194927", - "scratch-render": "0.1.0-prerelease.20190107163047", + "scratch-render": "0.1.0-prerelease.20190108223629", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", "scratch-vm": "0.2.0-prerelease.20190107184530", From 38fd753a8d5c82396e7c8ea34adeac04a2e0a1f1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 9 Jan 2019 14:51:06 -0500 Subject: [PATCH 0954/1971] Merge pull request #4305 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190108233032 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019010… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d8114646ff..d923b3c9e8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190108223629", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", - "scratch-vm": "0.2.0-prerelease.20190107184530", + "scratch-vm": "0.2.0-prerelease.20190108233032", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From f27cdb331a3f22f853c58c83b0cf564421763808 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 9 Jan 2019 15:15:25 -0500 Subject: [PATCH 0955/1971] Merge pull request #4308 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190109201300 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d923b3c9e8..0a1199bc9a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-blocks": "0.1.0-prerelease.1545364693", "scratch-l10n": "3.1.20181220222259", "scratch-paint": "0.2.0-prerelease.20181220194927", - "scratch-render": "0.1.0-prerelease.20190108223629", + "scratch-render": "0.1.0-prerelease.20190109201300", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", "scratch-vm": "0.2.0-prerelease.20190108233032", From 6c5ffb468d5df5a82bdcad47ba0c02680eed364f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 9 Jan 2019 15:15:39 -0500 Subject: [PATCH 0956/1971] Merge pull request #4307 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190109200835 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0a1199bc9a..08f6ee7c91 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190109201300", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", - "scratch-vm": "0.2.0-prerelease.20190108233032", + "scratch-vm": "0.2.0-prerelease.20190109200835", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 77e952b49dbb1378c5de7a53a30df9cc294fd385 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 9 Jan 2019 15:18:20 -0500 Subject: [PATCH 0957/1971] Merge pull request #4309 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20190109201344 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 08f6ee7c91..4ac4d58cec 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-paint": "0.2.0-prerelease.20181220194927", "scratch-render": "0.1.0-prerelease.20190109201300", "scratch-storage": "1.2.2", - "scratch-svg-renderer": "0.2.0-prerelease.20181220183040", + "scratch-svg-renderer": "0.2.0-prerelease.20190109201344", "scratch-vm": "0.2.0-prerelease.20190109200835", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From bdf0b9016f5ae7a5350cd41945fbb9a2dbd8c4e3 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 10 Jan 2019 13:59:14 -0500 Subject: [PATCH 0958/1971] Merge pull request #4298 from chrisgarrity/issue/null-flyout check for non-null flyout --- packages/scratch-gui/src/containers/blocks.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index e6eebec41b..73e58e8b24 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -259,7 +259,7 @@ class Blocks extends React.Component { } onTargetsUpdate () { - if (this.props.vm.editingTarget) { + if (this.props.vm.editingTarget && this.workspace.getFlyout()) { ['glide', 'move', 'set'].forEach(prefix => { this.updateToolboxBlockValue(`${prefix}x`, Math.round(this.props.vm.editingTarget.x).toString()); this.updateToolboxBlockValue(`${prefix}y`, Math.round(this.props.vm.editingTarget.y).toString()); From 76efd4fddbd1f6745f765bcdbe71b12f8b078cfe Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 10 Jan 2019 13:59:48 -0500 Subject: [PATCH 0959/1971] Merge pull request #4310 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190109203013 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4ac4d58cec..1ab864f6cf 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-blocks": "0.1.0-prerelease.1545364693", "scratch-l10n": "3.1.20181220222259", "scratch-paint": "0.2.0-prerelease.20181220194927", - "scratch-render": "0.1.0-prerelease.20190109201300", + "scratch-render": "0.1.0-prerelease.20190109203013", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190109201344", "scratch-vm": "0.2.0-prerelease.20190109200835", From a083b2761cf2a082a1d0a02dbd196e0446f38d04 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 11 Jan 2019 13:32:36 -0500 Subject: [PATCH 0960/1971] Merge pull request #4318 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20190110205335 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1ab864f6cf..b3c941fe0e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-paint": "0.2.0-prerelease.20181220194927", "scratch-render": "0.1.0-prerelease.20190109203013", "scratch-storage": "1.2.2", - "scratch-svg-renderer": "0.2.0-prerelease.20190109201344", + "scratch-svg-renderer": "0.2.0-prerelease.20190110205335", "scratch-vm": "0.2.0-prerelease.20190109200835", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 9582ed8993a7c4db6b5d3ffd81b238f74a330c78 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 14 Jan 2019 15:34:55 -0500 Subject: [PATCH 0961/1971] Merge pull request #4341 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190114200827 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019011… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b3c941fe0e..5405a8eb82 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190109203013", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190110205335", - "scratch-vm": "0.2.0-prerelease.20190109200835", + "scratch-vm": "0.2.0-prerelease.20190114200827", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From dc6f3f45d43da3ac24ea2554ea0c59f971853ef2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 14 Jan 2019 15:35:11 -0500 Subject: [PATCH 0962/1971] Merge pull request #4317 from LLK/greenkeeper/scratch-l10n-3.1.20190110195702 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5405a8eb82..898612718c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190108181031", "scratch-blocks": "0.1.0-prerelease.1545364693", - "scratch-l10n": "3.1.20181220222259", + "scratch-l10n": "3.1.20190110195702", "scratch-paint": "0.2.0-prerelease.20181220194927", "scratch-render": "0.1.0-prerelease.20190109203013", "scratch-storage": "1.2.2", From 965f70bff7bd4847e6b08a183a446da614ebd1ef Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 14 Jan 2019 16:57:19 -0500 Subject: [PATCH 0963/1971] Merge pull request #4343 from LLK/greenkeeper/scratch-audio-0.1.0-prerelease.20190114210212 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-audio to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 898612718c..822e14fb98 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -103,7 +103,7 @@ "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "0.1.0-prerelease.20190108181031", + "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1545364693", "scratch-l10n": "3.1.20190110195702", "scratch-paint": "0.2.0-prerelease.20181220194927", From 02897709f8694cab736032f2405f275f6aff14d5 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 14 Jan 2019 16:57:33 -0500 Subject: [PATCH 0964/1971] Merge pull request #4342 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190114205252 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 822e14fb98..5a44287c45 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1545364693", "scratch-l10n": "3.1.20190110195702", - "scratch-paint": "0.2.0-prerelease.20181220194927", + "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190109203013", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190110205335", From 22c62c634b9f178cf5332396f7909bf2fa86cd80 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Tue, 15 Jan 2019 12:14:40 -0500 Subject: [PATCH 0965/1971] Merge pull request #4349 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1547568209 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5a44287c45..ded58cde03 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1545364693", + "scratch-blocks": "0.1.0-prerelease.1547568209", "scratch-l10n": "3.1.20190110195702", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190109203013", From 2bd985ddc1a69ab792fc1cc9ea224de197009485 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 15 Jan 2019 12:27:06 -0500 Subject: [PATCH 0966/1971] Merge pull request #4351 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190115164649 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019011… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ded58cde03..6408025864 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190109203013", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190110205335", - "scratch-vm": "0.2.0-prerelease.20190114200827", + "scratch-vm": "0.2.0-prerelease.20190115164649", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 11d53838e101adfa0f7a625f393e60ad81949d65 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 15 Jan 2019 12:44:38 -0500 Subject: [PATCH 0967/1971] Merge pull request #4352 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1547570425 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.154… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6408025864..31bd76a52f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1547568209", + "scratch-blocks": "0.1.0-prerelease.1547570425", "scratch-l10n": "3.1.20190110195702", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190109203013", From def66cef4b6012ca008f1f26627eed59a629276f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 15 Jan 2019 12:44:54 -0500 Subject: [PATCH 0968/1971] Merge pull request #4353 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190115173447 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 31bd76a52f..8235b1f18d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-blocks": "0.1.0-prerelease.1547570425", "scratch-l10n": "3.1.20190110195702", "scratch-paint": "0.2.0-prerelease.20190114205252", - "scratch-render": "0.1.0-prerelease.20190109203013", + "scratch-render": "0.1.0-prerelease.20190115173447", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190110205335", "scratch-vm": "0.2.0-prerelease.20190115164649", From 02ae849ed84fe361f5c8302bdd3861709b1881b1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 15 Jan 2019 16:15:08 -0500 Subject: [PATCH 0969/1971] Merge pull request #4358 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190115203710 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8235b1f18d..e9e45f93b2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190115173447", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190110205335", - "scratch-vm": "0.2.0-prerelease.20190115164649", + "scratch-vm": "0.2.0-prerelease.20190115203710", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 027b02b692d7b9560128d29415b056058abe33b6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 16 Jan 2019 13:15:36 -0500 Subject: [PATCH 0970/1971] Merge pull request #4361 from LLK/greenkeeper/scratch-l10n-3.1.20190116142721 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e9e45f93b2..10df0d5f12 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1547570425", - "scratch-l10n": "3.1.20190110195702", + "scratch-l10n": "3.1.20190116142721", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190115173447", "scratch-storage": "1.2.2", From 9c240e45915b826ebb4119d7662feca3248586e6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 16 Jan 2019 16:21:00 -0500 Subject: [PATCH 0971/1971] Merge pull request #4368 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190116202234 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019011… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 10df0d5f12..74612e8268 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190115173447", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190110205335", - "scratch-vm": "0.2.0-prerelease.20190115203710", + "scratch-vm": "0.2.0-prerelease.20190116202234", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 6684506e6368595c8dcb1f914b3963e040323fd8 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 16 Jan 2019 16:21:22 -0500 Subject: [PATCH 0972/1971] Merge pull request #4366 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190116202853 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 74612e8268..f316e24aa1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-blocks": "0.1.0-prerelease.1547570425", "scratch-l10n": "3.1.20190116142721", "scratch-paint": "0.2.0-prerelease.20190114205252", - "scratch-render": "0.1.0-prerelease.20190115173447", + "scratch-render": "0.1.0-prerelease.20190116202853", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190110205335", "scratch-vm": "0.2.0-prerelease.20190116202234", From b0c197b710d8bb6680c696e034c16393a2c262b3 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 16 Jan 2019 16:21:34 -0500 Subject: [PATCH 0973/1971] Merge pull request #4364 from LLK/greenkeeper/scratch-l10n-3.1.20190116193006 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f316e24aa1..802afa1a47 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1547570425", - "scratch-l10n": "3.1.20190116142721", + "scratch-l10n": "3.1.20190116193006", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190116202853", "scratch-storage": "1.2.2", From 279464a3e42747008fa5d0d2ddec5dc5c5239bcb Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 16 Jan 2019 16:57:27 -0500 Subject: [PATCH 0974/1971] Merge pull request #4369 from kchadha/update-scratch-blocks Update scratch-blocks --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 802afa1a47..8d8d2d5441 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1547570425", + "scratch-blocks": "0.1.0-prerelease.1547674279", "scratch-l10n": "3.1.20190116193006", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190116202853", From 3e13e30a928d79f5207f9105dcd1b5ec16ba84a5 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 17 Jan 2019 09:36:07 -0500 Subject: [PATCH 0975/1971] Merge pull request #4377 from chrisgarrity/update-blocks update scratch-blocks for languages. --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8d8d2d5441..effd6d512f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1547674279", + "scratch-blocks": "0.1.0-prerelease.1547735159", "scratch-l10n": "3.1.20190116193006", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190116202853", From 6c20fb50948d0911f7a16c370ff158ff3b58eeed Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 17 Jan 2019 15:12:46 -0500 Subject: [PATCH 0976/1971] Merge pull request #4379 from LLK/greenkeeper/scratch-l10n-3.1.20190117191816 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index effd6d512f..fa8bb61f44 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1547735159", - "scratch-l10n": "3.1.20190116193006", + "scratch-l10n": "3.1.20190117191816", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190116202853", "scratch-storage": "1.2.2", From 2ab588bc442752616bdecbb2cb8eebc7721f816d Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 18 Jan 2019 16:59:22 -0500 Subject: [PATCH 0977/1971] Merge pull request #3922 from evhan55/extensions/disconnect-errors Peripheral disconnect status using new runtime/vm event --- packages/scratch-gui/src/containers/blocks.jsx | 4 ++-- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 73e58e8b24..f64e7c3076 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -230,7 +230,7 @@ class Blocks extends React.Component { this.props.vm.addListener('EXTENSION_ADDED', this.handleExtensionAdded); this.props.vm.addListener('BLOCKSINFO_UPDATE', this.handleBlocksInfoUpdate); this.props.vm.addListener('PERIPHERAL_CONNECTED', this.handleStatusButtonUpdate); - this.props.vm.addListener('PERIPHERAL_DISCONNECT_ERROR', this.handleStatusButtonUpdate); + this.props.vm.addListener('PERIPHERAL_DISCONNECTED', this.handleStatusButtonUpdate); } detachVM () { this.props.vm.removeListener('SCRIPT_GLOW_ON', this.onScriptGlowOn); @@ -243,7 +243,7 @@ class Blocks extends React.Component { this.props.vm.removeListener('EXTENSION_ADDED', this.handleExtensionAdded); this.props.vm.removeListener('BLOCKSINFO_UPDATE', this.handleBlocksInfoUpdate); this.props.vm.removeListener('PERIPHERAL_CONNECTED', this.handleStatusButtonUpdate); - this.props.vm.removeListener('PERIPHERAL_DISCONNECT_ERROR', this.handleStatusButtonUpdate); + this.props.vm.removeListener('PERIPHERAL_DISCONNECTED', this.handleStatusButtonUpdate); } updateToolboxBlockValue (id, value) { diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 8c80de40ae..5e51aad16a 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -44,7 +44,7 @@ const vmListenerHOC = function (WrappedComponent) { this.props.vm.on('PROJECT_CHANGED', this.handleProjectChanged); this.props.vm.on('RUNTIME_STARTED', this.props.onRuntimeStarted); this.props.vm.on('PROJECT_START', this.props.onGreenFlag); - this.props.vm.on('PERIPHERAL_DISCONNECT_ERROR', this.props.onShowExtensionAlert); + this.props.vm.on('PERIPHERAL_CONNECTION_LOST_ERROR', this.props.onShowExtensionAlert); this.props.vm.on('MIC_LISTENING', this.props.onMicListeningUpdate); } @@ -67,7 +67,7 @@ const vmListenerHOC = function (WrappedComponent) { } } componentWillUnmount () { - this.props.vm.removeListener('PERIPHERAL_DISCONNECT_ERROR', this.props.onShowExtensionAlert); + this.props.vm.removeListener('PERIPHERAL_CONNECTION_LOST_ERROR', this.props.onShowExtensionAlert); if (this.props.attachKeyboardEvents) { document.removeEventListener('keydown', this.handleKeyDown); document.removeEventListener('keyup', this.handleKeyUp); From 76c27bccd82b1ebc27c683cf8dc2328fd1126645 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 22 Jan 2019 17:38:44 -0500 Subject: [PATCH 0978/1971] Merge pull request #4405 from ericrosenbaum/bugfix/lego-branding Update WeDo 2.0 extension name --- packages/scratch-gui/src/lib/libraries/extensions/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index 8472907649..6517473dba 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -204,7 +204,7 @@ export default [ helpLink: 'https://scratch.mit.edu/ev3' }, { - name: 'LEGO WeDo 2.0', + name: 'LEGO Education WeDo 2.0', extensionId: 'wedo2', collaborator: 'LEGO', iconURL: wedoImage, From 13832dc4ae437e4b70273926b4f1911a4fedaf05 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 23 Jan 2019 08:44:51 -0500 Subject: [PATCH 0979/1971] Merge pull request #4411 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190122223537 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-render to version 0.1.0-prerelease.201… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index fa8bb61f44..adaa85dd4c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-blocks": "0.1.0-prerelease.1547735159", "scratch-l10n": "3.1.20190117191816", "scratch-paint": "0.2.0-prerelease.20190114205252", - "scratch-render": "0.1.0-prerelease.20190116202853", + "scratch-render": "0.1.0-prerelease.20190122223537", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190110205335", "scratch-vm": "0.2.0-prerelease.20190116202234", From 8b7d9af063b240541b8975d930696b597d5e44b5 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 23 Jan 2019 09:06:47 -0500 Subject: [PATCH 0980/1971] Merge pull request #4412 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190118221822 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019011… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index adaa85dd4c..1060f45cf7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190122223537", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190110205335", - "scratch-vm": "0.2.0-prerelease.20190116202234", + "scratch-vm": "0.2.0-prerelease.20190118221822", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 608e0e86a32aa7cc77901cd92d93ae77981f72e0 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 23 Jan 2019 12:12:33 -0500 Subject: [PATCH 0981/1971] Merge pull request #4407 from paulkaplan/no-updates-while-recording Disable target updates while sound recording --- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 5e51aad16a..efeaa35008 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -164,7 +164,9 @@ const vmListenerHOC = function (WrappedComponent) { }; const mapStateToProps = state => ({ // Do not emit target or project updates in fullscreen or player only mode - shouldEmitUpdates: !state.scratchGui.mode.isFullScreen && !state.scratchGui.mode.isPlayerOnly, + // or when recording sounds (it leads to garbled recordings on low-power machines) + shouldEmitUpdates: !state.scratchGui.mode.isFullScreen && !state.scratchGui.mode.isPlayerOnly && + !state.scratchGui.modals.soundRecorder, vm: state.scratchGui.vm, username: state.session && state.session.session && state.session.session.user ? state.session.session.user.username : '' From 23bb8c5cd1f0c8148181d4e41eaedf727aa0e8d1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 23 Jan 2019 12:35:32 -0500 Subject: [PATCH 0982/1971] Merge pull request #4414 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190123164824 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1060f45cf7..1ac0dabd29 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190122223537", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190110205335", - "scratch-vm": "0.2.0-prerelease.20190118221822", + "scratch-vm": "0.2.0-prerelease.20190123164824", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From f37420f75d6bbcc3c9b18b4ffdc398ff51ed32be Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 23 Jan 2019 15:16:14 -0500 Subject: [PATCH 0983/1971] Merge pull request #4418 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1548272895 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1ac0dabd29..b0c9a47ddc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1547735159", + "scratch-blocks": "0.1.0-prerelease.1548272895", "scratch-l10n": "3.1.20190117191816", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190122223537", From 3b8277df6d3fd30729d2039d2725d607178b7372 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 23 Jan 2019 16:27:49 -0500 Subject: [PATCH 0984/1971] Merge pull request #4419 from LLK/greenkeeper/scratch-l10n-3.1.20190123150639 chore(package): update scratch-l10n to version 3.1.20190123150639 --- packages/scratch-gui/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b0c9a47ddc..2f9bc35ee5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,8 +104,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1548272895", - "scratch-l10n": "3.1.20190117191816", + "scratch-l10n": "3.1.20190123150639", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190122223537", "scratch-storage": "1.2.2", From d13d6d58a41e04cf7d45272cfd392b9cc67ff01c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 23 Jan 2019 16:28:44 -0500 Subject: [PATCH 0985/1971] Merge pull request #4420 from LLK/revert-4419-greenkeeper/scratch-l10n-3.1.20190123150639 Revert "chore(package): update scratch-l10n to version 3.1.20190123150639" --- packages/scratch-gui/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2f9bc35ee5..b0c9a47ddc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,8 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-l10n": "3.1.20190123150639", + "scratch-blocks": "0.1.0-prerelease.1548272895", + "scratch-l10n": "3.1.20190117191816", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190122223537", "scratch-storage": "1.2.2", From 9bcdd61d1c1c27eaaaac6c58e2825f1862584dcc Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 23 Jan 2019 16:30:48 -0500 Subject: [PATCH 0986/1971] Merge pull request #4421 from LLK/paulkaplan-patch-1 Update l10n --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b0c9a47ddc..e028cd5a4d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1548272895", - "scratch-l10n": "3.1.20190117191816", + "scratch-l10n": "3.1.20190123150639", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190122223537", "scratch-storage": "1.2.2", From f7217ab53b84c2691c24c59bf52663b3c816a9d9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 28 Jan 2019 09:28:32 -0500 Subject: [PATCH 0987/1971] Merge pull request #4434 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20190125192231 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e028cd5a4d..546b34a4e5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190122223537", "scratch-storage": "1.2.2", - "scratch-svg-renderer": "0.2.0-prerelease.20190110205335", + "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", "scratch-vm": "0.2.0-prerelease.20190123164824", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 76e1d099892aeaf6303c7764fd4ac4b430ba6795 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 28 Jan 2019 09:29:28 -0500 Subject: [PATCH 0988/1971] Merge pull request #4430 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190125023926 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 546b34a4e5..3e42eb4186 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-blocks": "0.1.0-prerelease.1548272895", "scratch-l10n": "3.1.20190123150639", "scratch-paint": "0.2.0-prerelease.20190114205252", - "scratch-render": "0.1.0-prerelease.20190122223537", + "scratch-render": "0.1.0-prerelease.20190125023926", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", "scratch-vm": "0.2.0-prerelease.20190123164824", From 2b3fa83878e8cf161df73a77b646e4315f18b5a2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 28 Jan 2019 09:29:54 -0500 Subject: [PATCH 0989/1971] Merge pull request #4441 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1548354812 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.154… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3e42eb4186..ef9461abd8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1548272895", + "scratch-blocks": "0.1.0-prerelease.1548354812", "scratch-l10n": "3.1.20190123150639", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190125023926", From 8b9f049e930d58e156e41220a28a76f9b3024532 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 28 Jan 2019 14:06:27 -0500 Subject: [PATCH 0990/1971] Merge pull request #4444 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1548691077 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ef9461abd8..fe0654e17a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1548354812", + "scratch-blocks": "0.1.0-prerelease.1548691077", "scratch-l10n": "3.1.20190123150639", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190125023926", From a1136b1a89c64c40a1301c84ad99dde316bfa7d2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 28 Jan 2019 14:06:50 -0500 Subject: [PATCH 0991/1971] Merge pull request #4443 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190128155421 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index fe0654e17a..317ed9731d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190125023926", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", - "scratch-vm": "0.2.0-prerelease.20190123164824", + "scratch-vm": "0.2.0-prerelease.20190128155421", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 4965db7138b0def2dd8ce7d43fab0cbc9ccd6f2b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 28 Jan 2019 14:07:16 -0500 Subject: [PATCH 0992/1971] Merge pull request #4442 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190128154859 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 317ed9731d..f3c64edb7e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-blocks": "0.1.0-prerelease.1548691077", "scratch-l10n": "3.1.20190123150639", "scratch-paint": "0.2.0-prerelease.20190114205252", - "scratch-render": "0.1.0-prerelease.20190125023926", + "scratch-render": "0.1.0-prerelease.20190128154859", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", "scratch-vm": "0.2.0-prerelease.20190128155421", From 36827edefd27de3d42fa2f2a46ae3fa44607d67a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 28 Jan 2019 14:42:03 -0500 Subject: [PATCH 0993/1971] Merge pull request #4446 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190128162704 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019012… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f3c64edb7e..737ab8c2dd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190128154859", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", - "scratch-vm": "0.2.0-prerelease.20190128155421", + "scratch-vm": "0.2.0-prerelease.20190128162704", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From f1c12840a6184377a261aa7664d9859bb26e09e5 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 29 Jan 2019 14:05:04 -0500 Subject: [PATCH 0994/1971] Merge pull request #4440 from paulkaplan/improve-block-perf Avoid double toolbox update on mount and when setting language --- packages/scratch-gui/src/containers/blocks.jsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index f64e7c3076..a3bf587228 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -170,13 +170,16 @@ class Blocks extends React.Component { } setLocale () { - this.workspace.getFlyout().setRecyclingEnabled(false); this.ScratchBlocks.ScratchMsgs.setLocale(this.props.locale); this.props.vm.setLocale(this.props.locale, this.props.messages) .then(() => { + this.workspace.getFlyout().setRecyclingEnabled(false); this.props.vm.refreshWorkspace(); - this.updateToolbox(); - this.workspace.getFlyout().setRecyclingEnabled(true); + // refreshWorkspace will cause a toolbox update + // wait for update to go through before reenabling recycling + this.withToolboxUpdates(() => { + this.workspace.getFlyout().setRecyclingEnabled(true); + }); }); } From 5d543874c866168c1296901f60fa6c718c68f292 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 29 Jan 2019 14:12:13 -0500 Subject: [PATCH 0995/1971] Merge pull request #4448 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190128192220 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 737ab8c2dd..30a493c086 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190128154859", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", - "scratch-vm": "0.2.0-prerelease.20190128162704", + "scratch-vm": "0.2.0-prerelease.20190128192220", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From b3983c6788c71e1e5f3263f5e049e5aa89658adf Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 30 Jan 2019 11:55:19 -0500 Subject: [PATCH 0996/1971] Merge pull request #4455 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190130160436 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 30a493c086..7f34e38f2f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190128154859", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", - "scratch-vm": "0.2.0-prerelease.20190128192220", + "scratch-vm": "0.2.0-prerelease.20190130160436", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 43f96c8b79c01cefea02d02b44c32d7c6653dd9a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 30 Jan 2019 14:15:51 -0500 Subject: [PATCH 0997/1971] Merge pull request #4456 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1548864869 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7f34e38f2f..4052c2b79b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1548691077", + "scratch-blocks": "0.1.0-prerelease.1548864869", "scratch-l10n": "3.1.20190123150639", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190128154859", From d2300183d6bece467afcc26aaecabe48e0f594a0 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 30 Jan 2019 14:18:23 -0500 Subject: [PATCH 0998/1971] Merge pull request #4454 from LLK/greenkeeper/scratch-l10n-3.1.20190130142816 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4052c2b79b..1c15e9a1e5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1548864869", - "scratch-l10n": "3.1.20190123150639", + "scratch-l10n": "3.1.20190130142816", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190128154859", "scratch-storage": "1.2.2", From 5fe312d98c5c411c4fb2a0374c1279db783a6f8d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 30 Jan 2019 15:01:01 -0500 Subject: [PATCH 0999/1971] Merge pull request #4449 from paulkaplan/improve-toolbox-perf Only update the toolbox when the blocks tab is visible. --- .../scratch-gui/src/containers/blocks.jsx | 54 ++++++++++++++----- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index a3bf587228..98a01db1bf 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -50,6 +50,7 @@ class Blocks extends React.Component { bindAll(this, [ 'attachVM', 'detachVM', + 'getToolboxXML', 'handleCategorySelected', 'handleConnectionModalStart', 'handleDrop', @@ -94,6 +95,11 @@ class Blocks extends React.Component { ); this.workspace = this.ScratchBlocks.inject(this.blocks, workspaceConfig); + // Store the xml of the toolbox that is actually rendered. + // This is used in componentDidUpdate instead of prevProps, because + // the xml can change while e.g. on the costumes tab. + this._renderedToolboxXML = this.props.toolboxXML; + // we actually never want the workspace to enable "refresh toolbox" - this basically re-renders the // entire toolbox every time we reset the workspace. We call updateToolbox as a part of // componentDidUpdate so the toolbox will still correctly be updated @@ -117,7 +123,7 @@ class Blocks extends React.Component { return ( this.state.prompt !== nextState.prompt || this.props.isVisible !== nextProps.isVisible || - this.props.toolboxXML !== nextProps.toolboxXML || + this._renderedToolboxXML !== nextProps.toolboxXML || this.props.extensionLibraryVisible !== nextProps.extensionLibraryVisible || this.props.customProceduresVisible !== nextProps.customProceduresVisible || this.props.locale !== nextProps.locale || @@ -131,13 +137,17 @@ class Blocks extends React.Component { this.ScratchBlocks.hideChaff(); } - if (prevProps.toolboxXML !== this.props.toolboxXML) { + // Only rerender the toolbox when the blocks are visible and the xml is + // different from the previously rendered toolbox xml. + // Do not check against prevProps.toolboxXML because that may not have been rendered. + if (this.props.isVisible && this.props.toolboxXML !== this._renderedToolboxXML) { // rather than update the toolbox "sync" -- update it in the next frame clearTimeout(this.toolboxUpdateTimeout); this.toolboxUpdateTimeout = setTimeout(() => { this.updateToolbox(); }, 0); } + if (this.props.isVisible === prevProps.isVisible) { if (this.props.stageSize !== prevProps.stageSize) { // force workspace to redraw for the new stage size @@ -155,7 +165,6 @@ class Blocks extends React.Component { this.setLocale(); } else { this.props.vm.refreshWorkspace(); - this.updateToolbox(); } window.dispatchEvent(new Event('resize')); @@ -189,6 +198,8 @@ class Blocks extends React.Component { const categoryId = this.workspace.toolbox_.getSelectedCategoryId(); const offset = this.workspace.toolbox_.getCategoryScrollOffset(); this.workspace.updateToolbox(this.props.toolboxXML); + this._renderedToolboxXML = this.props.toolboxXML; + // In order to catch any changes that mutate the toolbox during "normal runtime" // (variable changes/etc), re-enable toolbox refresh. // Using the setter function will rerender the entire toolbox which we just rendered. @@ -297,12 +308,32 @@ class Blocks extends React.Component { onVisualReport (data) { this.workspace.reportValue(data.id, data.value); } + getToolboxXML () { + // Use try/catch because this requires digging pretty deep into the VM + // Code inside intentionally ignores several error situations (no stage, etc.) + // Because they would get caught by this try/catch + try { + let {editingTarget: target, runtime} = this.props.vm; + const stage = runtime.getTargetForStage(); + if (!target) target = stage; // If no editingTarget, use the stage + + const stageCostumes = stage.getCostumes(); + const targetCostumes = target.getCostumes(); + const targetSounds = target.getSounds(); + const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(); + return makeToolboxXML(target.isStage, target.id, dynamicBlocksXML, + targetCostumes[0].name, + stageCostumes[0].name, + targetSounds.length > 0 ? targetSounds[0].name : '' + ); + } catch { + return null; + } + } onWorkspaceUpdate (data) { // When we change sprites, update the toolbox to have the new sprite's blocks - if (this.props.vm.editingTarget) { - const target = this.props.vm.editingTarget; - const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(); - const toolboxXML = makeToolboxXML(target.isStage, target.id, dynamicBlocksXML); + const toolboxXML = this.getToolboxXML(); + if (toolboxXML) { this.props.updateToolboxState(toolboxXML); } @@ -350,12 +381,9 @@ class Blocks extends React.Component { // this actually defines blocks and MUST run regardless of the UI state this.ScratchBlocks.defineBlocksWithJsonArray(blocksInfo.map(blockInfo => blockInfo.json).filter(x => x)); - // update the toolbox view: this can be skipped if we're not looking at a target, etc. - const runtime = this.props.vm.runtime; - const target = runtime.getEditingTarget() || runtime.getTargetForStage(); - if (target) { - const dynamicBlocksXML = runtime.getBlocksXML(); - const toolboxXML = makeToolboxXML(target.isStage, target.id, dynamicBlocksXML); + // Update the toolbox with new blocks + const toolboxXML = this.getToolboxXML(); + if (toolboxXML) { this.props.updateToolboxState(toolboxXML); } } From ff4696d270e4737f7bb5057b44ed029b3da333ea Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 31 Jan 2019 13:51:11 +0100 Subject: [PATCH 1000/1971] Merge pull request #4462 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1548885087 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1c15e9a1e5..65ebd0c7b0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1548864869", + "scratch-blocks": "0.1.0-prerelease.1548885087", "scratch-l10n": "3.1.20190130142816", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190128154859", From 93223cc16bd00004eb56b2a0355abe7d362328b6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 1 Feb 2019 09:28:07 -0500 Subject: [PATCH 1001/1971] Merge pull request #4463 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190130220715 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 65ebd0c7b0..c27a39fa2e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190128154859", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", - "scratch-vm": "0.2.0-prerelease.20190130160436", + "scratch-vm": "0.2.0-prerelease.20190130220715", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From f34132020b1c0aabdc08c94408821d71471a6f03 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 4 Feb 2019 09:59:23 -0500 Subject: [PATCH 1002/1971] Merge pull request #4433 from towerofnix/ctrl-s-to-save-2 Control/Command+S to save --- .../src/components/menu-bar/menu-bar.jsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 825f6096f9..647a90fd05 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -3,6 +3,7 @@ import {connect} from 'react-redux'; import {defineMessages, FormattedMessage, injectIntl, intlShape} from 'react-intl'; import PropTypes from 'prop-types'; import bindAll from 'lodash.bindall'; +import bowser from 'bowser'; import React from 'react'; import Box from '../box/box.jsx'; @@ -151,11 +152,18 @@ class MenuBar extends React.Component { 'handleClickSeeCommunity', 'handleClickShare', 'handleCloseFileMenuAndThen', + 'handleKeyPress', 'handleLanguageMouseUp', 'handleRestoreOption', 'restoreOptionMessage' ]); } + componentDidMount () { + document.addEventListener('keydown', this.handleKeyPress); + } + componentWillUnmount () { + document.removeEventListener('keydown', this.handleKeyPress); + } handleClickNew () { let readyToReplaceProject = true; // if the project is dirty, and user owns the project, we will autosave. @@ -219,6 +227,13 @@ class MenuBar extends React.Component { fn(); }; } + handleKeyPress (event) { + const modifier = bowser.mac ? event.metaKey : event.ctrlKey; + if (modifier && event.key === 's') { + this.props.onClickSave(); + event.preventDefault(); + } + } handleLanguageMouseUp (e) { if (!this.props.languageMenuOpen) { this.props.onClickLanguage(e); From dfb8883bf2f8a836e11fec99fdcbb66678bf98ac Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 4 Feb 2019 11:35:21 -0500 Subject: [PATCH 1003/1971] Merge pull request #4481 from paulkaplan/hotfix/toolbox-language [develop] hotfix toolbox language --- packages/scratch-gui/src/containers/blocks.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 98a01db1bf..c982bfeccf 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -87,6 +87,7 @@ class Blocks extends React.Component { componentDidMount () { this.ScratchBlocks.FieldColourSlider.activateEyedropper_ = this.props.onActivateColorPicker; this.ScratchBlocks.Procedures.externalProcedureDefCallback = this.props.onActivateCustomProcedures; + this.ScratchBlocks.ScratchMsgs.setLocale(this.props.locale); const workspaceConfig = defaultsDeep({}, Blocks.defaultOptions, @@ -526,7 +527,7 @@ Blocks.propTypes = { extensionLibraryVisible: PropTypes.bool, isRtl: PropTypes.bool, isVisible: PropTypes.bool, - locale: PropTypes.string, + locale: PropTypes.string.isRequired, messages: PropTypes.objectOf(PropTypes.string), onActivateColorPicker: PropTypes.func, onActivateCustomProcedures: PropTypes.func, From 991f428bf3aaff030eb8393bc6cb3cbd06cda0f5 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 5 Feb 2019 12:07:18 -0500 Subject: [PATCH 1004/1971] Merge pull request #4467 from paulkaplan/fix-backpack-code-updating Call updateToolbox after adding new blocks from the backpack. --- packages/scratch-gui/src/containers/blocks.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index c982bfeccf..8726aa2c73 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -455,6 +455,7 @@ class Blocks extends React.Component { .then(blocks => this.props.vm.shareBlocksToTarget(blocks, this.props.vm.editingTarget.id)) .then(() => { this.props.vm.refreshWorkspace(); + this.updateToolbox(); // To show new variables/custom blocks }); } render () { From 69fba6fb739e3266133e9cbc6570a9547cbac741 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 6 Feb 2019 10:37:22 -0500 Subject: [PATCH 1005/1971] Merge pull request #4493 from paulkaplan/fix-see-inside-toolbox Fix "See Inside" not showing variables --- packages/scratch-gui/src/containers/blocks.jsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 8726aa2c73..d548ccff8d 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -142,11 +142,7 @@ class Blocks extends React.Component { // different from the previously rendered toolbox xml. // Do not check against prevProps.toolboxXML because that may not have been rendered. if (this.props.isVisible && this.props.toolboxXML !== this._renderedToolboxXML) { - // rather than update the toolbox "sync" -- update it in the next frame - clearTimeout(this.toolboxUpdateTimeout); - this.toolboxUpdateTimeout = setTimeout(() => { - this.updateToolbox(); - }, 0); + this.requestToolboxUpdate(); } if (this.props.isVisible === prevProps.isVisible) { @@ -178,15 +174,19 @@ class Blocks extends React.Component { this.workspace.dispose(); clearTimeout(this.toolboxUpdateTimeout); } - + requestToolboxUpdate () { + clearTimeout(this.toolboxUpdateTimeout); + this.toolboxUpdateTimeout = setTimeout(() => { + this.updateToolbox(); + }, 0); + } setLocale () { this.ScratchBlocks.ScratchMsgs.setLocale(this.props.locale); this.props.vm.setLocale(this.props.locale, this.props.messages) .then(() => { this.workspace.getFlyout().setRecyclingEnabled(false); this.props.vm.refreshWorkspace(); - // refreshWorkspace will cause a toolbox update - // wait for update to go through before reenabling recycling + this.requestToolboxUpdate(); this.withToolboxUpdates(() => { this.workspace.getFlyout().setRecyclingEnabled(true); }); From 1a70b433b4f404d66af6b01913f4489c6198d561 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 6 Feb 2019 15:56:56 -0500 Subject: [PATCH 1006/1971] Merge pull request #4494 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190205221329 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019020… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c27a39fa2e..e4fe40cca7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190128154859", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", - "scratch-vm": "0.2.0-prerelease.20190130220715", + "scratch-vm": "0.2.0-prerelease.20190205221329", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 36af1806505ac60b591ebcf0267bff6733613147 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 6 Feb 2019 15:57:08 -0500 Subject: [PATCH 1007/1971] Merge pull request #4487 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1549376808 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e4fe40cca7..2e838b0a6a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1548885087", + "scratch-blocks": "0.1.0-prerelease.1549376808", "scratch-l10n": "3.1.20190130142816", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190128154859", From 120de30c481218bb5f81189434b6cb49361f31bb Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 6 Feb 2019 16:27:11 -0500 Subject: [PATCH 1008/1971] Merge pull request #4497 from LLK/greenkeeper/scratch-l10n-3.1.20190206143031 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2e838b0a6a..b6fafe6186 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1549376808", - "scratch-l10n": "3.1.20190130142816", + "scratch-l10n": "3.1.20190206143031", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190128154859", "scratch-storage": "1.2.2", From f577d69ba09e9b07f716e6586746f5a5535d9679 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 7 Feb 2019 12:13:24 +0100 Subject: [PATCH 1009/1971] Merge pull request #4502 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1549534784 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b6fafe6186..8f40e641eb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1549376808", + "scratch-blocks": "0.1.0-prerelease.1549534784", "scratch-l10n": "3.1.20190206143031", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190128154859", From 380cdf82497b5e7c4e9dbfa263d92f139ba43662 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 7 Feb 2019 08:34:03 -0500 Subject: [PATCH 1010/1971] Merge pull request #4501 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190206213754 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8f40e641eb..d980658466 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-blocks": "0.1.0-prerelease.1549534784", "scratch-l10n": "3.1.20190206143031", "scratch-paint": "0.2.0-prerelease.20190114205252", - "scratch-render": "0.1.0-prerelease.20190128154859", + "scratch-render": "0.1.0-prerelease.20190206213754", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", "scratch-vm": "0.2.0-prerelease.20190205221329", From f14a340803066aac13733fe0e06ff1e5aacf95e2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 7 Feb 2019 08:56:15 -0500 Subject: [PATCH 1011/1971] Merge pull request #4504 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190207134238 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019020… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d980658466..f9470fe388 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190206213754", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", - "scratch-vm": "0.2.0-prerelease.20190205221329", + "scratch-vm": "0.2.0-prerelease.20190207134238", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 25fff47bedf8ee7aadb825541eeb2756b89cafa3 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 7 Feb 2019 14:38:17 -0500 Subject: [PATCH 1012/1971] Merge pull request #4508 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190207191220 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019020… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f9470fe388..dbbdf36b97 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190206213754", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", - "scratch-vm": "0.2.0-prerelease.20190207134238", + "scratch-vm": "0.2.0-prerelease.20190207191220", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 4a6ffcdb4053538afb78a4913f442ea8a0db70fa Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 7 Feb 2019 16:24:15 -0500 Subject: [PATCH 1013/1971] Merge pull request #4512 from LLK/revert-4501-greenkeeper/scratch-render-0.1.0-prerelease.20190206213754 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "Update scratch-render to the latest version 🚀" --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index dbbdf36b97..cbefa39544 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-blocks": "0.1.0-prerelease.1549534784", "scratch-l10n": "3.1.20190206143031", "scratch-paint": "0.2.0-prerelease.20190114205252", - "scratch-render": "0.1.0-prerelease.20190206213754", + "scratch-render": "0.1.0-prerelease.20190128154859", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", "scratch-vm": "0.2.0-prerelease.20190207191220", From f39c6748bcd759bc36fe7584d6d80fa2aa906828 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 8 Feb 2019 13:23:48 -0500 Subject: [PATCH 1014/1971] Merge pull request #4518 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190208165820 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-render to version 0.1.0-prerelease.201… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cbefa39544..c461b71fff 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-blocks": "0.1.0-prerelease.1549534784", "scratch-l10n": "3.1.20190206143031", "scratch-paint": "0.2.0-prerelease.20190114205252", - "scratch-render": "0.1.0-prerelease.20190128154859", + "scratch-render": "0.1.0-prerelease.20190208165820", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", "scratch-vm": "0.2.0-prerelease.20190207191220", From c13a440a4cf0e8555c262038d928863938873649 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 8 Feb 2019 13:24:50 -0500 Subject: [PATCH 1015/1971] Merge pull request #4519 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190207224121 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019020… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c461b71fff..4835e445a5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-render": "0.1.0-prerelease.20190208165820", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", - "scratch-vm": "0.2.0-prerelease.20190207191220", + "scratch-vm": "0.2.0-prerelease.20190207224121", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 3b5374468f726ed01fd5c7b0c09ee0b5bc28dc63 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 8 Feb 2019 13:38:02 -0500 Subject: [PATCH 1016/1971] Merge pull request #4515 from LLK/greenkeeper/scratch-l10n-3.1.20190207224638 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4835e445a5..7dfd18c896 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1549534784", - "scratch-l10n": "3.1.20190206143031", + "scratch-l10n": "3.1.20190207224638", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190208165820", "scratch-storage": "1.2.2", From 558aa82974cee29066f1739d44897c6b4808690c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 8 Feb 2019 13:38:41 -0500 Subject: [PATCH 1017/1971] Merge pull request #4517 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1549643185 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7dfd18c896..f83f826fe7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -104,7 +104,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1549534784", + "scratch-blocks": "0.1.0-prerelease.1549643185", "scratch-l10n": "3.1.20190207224638", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190208165820", From fe4d919762aa9490784f47ad3cfd26f90746780c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 11 Feb 2019 10:12:40 -0500 Subject: [PATCH 1018/1971] Merge pull request #4509 from LLK/gif-upload Gif upload v1: sprites, costumes --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f83f826fe7..c2ab59e637 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -76,6 +76,7 @@ "lodash.throttle": "4.0.1", "minilog": "3.1.0", "mkdirp": "^0.5.1", + "omggif": "1.0.9", "papaparse": "4.6.2", "postcss-import": "^12.0.0", "postcss-loader": "^3.0.0", From 1e5fbc9c3ffbf3e7d8a90840faab7679c55c52ae Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 11 Feb 2019 15:23:31 -0500 Subject: [PATCH 1019/1971] Merge pull request #4526 from LLK/greenkeeper/scratch-l10n-3.1.20190211142555 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c2ab59e637..1a29fff04c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1549643185", - "scratch-l10n": "3.1.20190207224638", + "scratch-l10n": "3.1.20190211142555", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190208165820", "scratch-storage": "1.2.2", From 7716889325767aa28c1c21dace4b6e87b38e794e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 12 Feb 2019 11:14:04 -0500 Subject: [PATCH 1020/1971] Merge pull request #4535 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190212150857 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1a29fff04c..715e2349ac 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190208165820", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", - "scratch-vm": "0.2.0-prerelease.20190207224121", + "scratch-vm": "0.2.0-prerelease.20190212150857", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From b6771589478d071d3b88960a018e6ac4eff3182c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 12 Feb 2019 13:59:26 -0500 Subject: [PATCH 1021/1971] Merge pull request #4541 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1549990124 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.154… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 715e2349ac..47e5e7345c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1549643185", + "scratch-blocks": "0.1.0-prerelease.1549990124", "scratch-l10n": "3.1.20190211142555", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190208165820", From d8df242768688cbc996671b451ed1459bb871ca5 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 13 Feb 2019 09:30:57 -0500 Subject: [PATCH 1022/1971] Merge pull request #4538 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190212160344 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 47e5e7345c..4d14217c7a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190208165820", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", - "scratch-vm": "0.2.0-prerelease.20190212150857", + "scratch-vm": "0.2.0-prerelease.20190212160344", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 7a0fbc9f4ed559a0d162cf2236c97eef8abbc76c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 13 Feb 2019 09:42:33 -0500 Subject: [PATCH 1023/1971] Merge pull request #4531 from paulkaplan/prompt-before-upload-replace Prompt before file upload replaces project --- .../scratch-gui/src/components/menu-bar/menu-bar.jsx | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 647a90fd05..f985881c15 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -67,13 +67,8 @@ import languageIcon from '../language-selector/language-icon.svg'; import scratchLogo from './scratch-logo.svg'; -const messages = defineMessages({ - confirmNav: { - id: 'gui.menuBar.confirmNewWithoutSaving', - defaultMessage: 'Replace contents of the current project?', - description: 'message for prompting user to confirm that they want to create new project without saving' - } -}); +import sharedMessages from '../../lib/shared-messages'; + const ariaMessages = defineMessages({ language: { id: 'gui.menuBar.LanguageSelector', @@ -173,7 +168,7 @@ class MenuBar extends React.Component { // they'll lose their work. if (this.props.projectChanged && !this.props.canCreateNew) { readyToReplaceProject = confirm( // eslint-disable-line no-alert - this.props.intl.formatMessage(messages.confirmNav) + this.props.intl.formatMessage(sharedMessages.replaceProjectWarning) ); } this.props.onRequestCloseFile(); From 935edba23f91b049332a937926135aacd30c7ba5 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 13 Feb 2019 12:59:57 -0500 Subject: [PATCH 1024/1971] Merge pull request #4547 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190213162739 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4d14217c7a..bdde4b79fc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190208165820", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", - "scratch-vm": "0.2.0-prerelease.20190212160344", + "scratch-vm": "0.2.0-prerelease.20190213162739", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From bda6aeedd935ac1e66532a4c3f9c5cf0826a12fd Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 13 Feb 2019 13:02:52 -0500 Subject: [PATCH 1025/1971] Merge pull request #4545 from LLK/greenkeeper/scratch-l10n-3.1.20190213142844 chore(package): update scratch-l10n to version 3.1.20190213142844 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index bdde4b79fc..900d4a13f6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1549990124", - "scratch-l10n": "3.1.20190211142555", + "scratch-l10n": "3.1.20190213142844", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190208165820", "scratch-storage": "1.2.2", From 5bd435c253350a1ae57a91e0c4fa05d39466fade Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 13 Feb 2019 15:38:17 -0500 Subject: [PATCH 1026/1971] Merge pull request #4550 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190213183713 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-render to version 0.1.0-prerelease.201… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 900d4a13f6..596275ce57 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "scratch-blocks": "0.1.0-prerelease.1549990124", "scratch-l10n": "3.1.20190213142844", "scratch-paint": "0.2.0-prerelease.20190114205252", - "scratch-render": "0.1.0-prerelease.20190208165820", + "scratch-render": "0.1.0-prerelease.20190213183713", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", "scratch-vm": "0.2.0-prerelease.20190213162739", From 99cac95e50d7b960160bd809b69b8d048a00c711 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 13 Feb 2019 15:56:34 -0500 Subject: [PATCH 1027/1971] Merge pull request #4549 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190213190040 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019021… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 596275ce57..d2152c3ee8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190213183713", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", - "scratch-vm": "0.2.0-prerelease.20190213162739", + "scratch-vm": "0.2.0-prerelease.20190213190040", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 36baced37362cfc28a69e83faba33b2f978bdb9e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 13 Feb 2019 16:30:28 -0500 Subject: [PATCH 1028/1971] Merge pull request #4534 from paulkaplan/importing-alert Show an "Importing..." alert during file import --- packages/scratch-gui/src/lib/alerts/index.jsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/scratch-gui/src/lib/alerts/index.jsx b/packages/scratch-gui/src/lib/alerts/index.jsx index e5c42cce35..dbd4f2ce7d 100644 --- a/packages/scratch-gui/src/lib/alerts/index.jsx +++ b/packages/scratch-gui/src/lib/alerts/index.jsx @@ -198,6 +198,20 @@ const alerts = [ closeButton: true, level: AlertLevels.SUCCESS, maxDisplaySecs: 15 + }, + { + alertId: 'importingAsset', + alertType: AlertTypes.STANDARD, + clearList: [], + content: ( + + ), + iconSpinner: true, + level: AlertLevels.SUCCESS } ]; From 6d356aad2d11c23d8f8e0a9f46ecac40d9774b87 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 13 Feb 2019 17:16:15 -0500 Subject: [PATCH 1029/1971] Merge pull request #4551 from LLK/greenkeeper/scratch-l10n-3.1.20190213193054 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d2152c3ee8..b49bb1cf5b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1549990124", - "scratch-l10n": "3.1.20190213142844", + "scratch-l10n": "3.1.20190213193054", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190213183713", "scratch-storage": "1.2.2", From eed62fea462298f70e9d80543fbef485b38794e0 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 14 Feb 2019 15:38:38 -0500 Subject: [PATCH 1030/1971] Merge pull request #4553 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190213210403 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b49bb1cf5b..3208b170e3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190213183713", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", - "scratch-vm": "0.2.0-prerelease.20190213190040", + "scratch-vm": "0.2.0-prerelease.20190213210403", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 3866b2fdbe7eccb1b9bae371f029979d842229ae Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 15 Feb 2019 09:25:17 -0500 Subject: [PATCH 1031/1971] Merge pull request #4562 from mzgoddard/mouse-move-drag-perf Mouse move drag perf --- packages/scratch-gui/src/containers/stage.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index a6204bab7c..5cee4e89bd 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -320,7 +320,6 @@ class Stage extends React.Component { if (this.state.dragId) return; const drawableId = this.renderer.pick(x, y); if (drawableId === null) return; - const drawableData = this.renderer.extractDrawable(drawableId, x, y); const targetId = this.props.vm.getTargetIdForDrawableId(drawableId); if (targetId === null) return; @@ -332,6 +331,9 @@ class Stage extends React.Component { // Dragging always brings the target to the front target.goToFront(); + // Extract the drawable art + const drawableData = this.renderer.extractDrawable(drawableId, x, y); + this.props.vm.startDrag(targetId); this.setState({ isDragging: true, From cae2ef457bb84b3c68ee70dc13b8032e1653c7ea Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 15 Feb 2019 11:27:01 -0500 Subject: [PATCH 1032/1971] Merge pull request #4563 from paulkaplan/fix-tab-toolbox-refresh Request a toolbox refresh after switching between tabs to fix #4561 --- packages/scratch-gui/src/containers/blocks.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index d548ccff8d..ecb998ea4c 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -162,6 +162,7 @@ class Blocks extends React.Component { this.setLocale(); } else { this.props.vm.refreshWorkspace(); + this.requestToolboxUpdate(); } window.dispatchEvent(new Event('resize')); From 55a351faf73992a6ccf3d6df078d10621baeaa52 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Sun, 17 Feb 2019 08:55:04 -0500 Subject: [PATCH 1033/1971] Merge pull request #4566 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190215190223 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3208b170e3..cf6a47f407 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190213183713", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", - "scratch-vm": "0.2.0-prerelease.20190213210403", + "scratch-vm": "0.2.0-prerelease.20190215190223", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From d2a66a0df9aa4e1599fa09393cd1f6d1f59ddb51 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 19 Feb 2019 12:02:04 -0500 Subject: [PATCH 1034/1971] Merge pull request #4571 from LLK/greenkeeper/scratch-l10n-3.1.20190218084652 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cf6a47f407..10601ea34a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1549990124", - "scratch-l10n": "3.1.20190213193054", + "scratch-l10n": "3.1.20190218084652", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190213183713", "scratch-storage": "1.2.2", From c1460902c053ad4d9b3be4b217402dd012ce47b6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 28 Feb 2019 09:00:05 -0500 Subject: [PATCH 1035/1971] Merge pull request #4575 from LLK/greenkeeper/scratch-l10n-3.1.20190220143732 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 10601ea34a..78d9fb8563 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1549990124", - "scratch-l10n": "3.1.20190218084652", + "scratch-l10n": "3.1.20190220143732", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190213183713", "scratch-storage": "1.2.2", From 98fb0ffaad2293e38f3d9a28c4e1717df3b0fdbc Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 28 Feb 2019 09:00:51 -0500 Subject: [PATCH 1036/1971] Merge pull request #4596 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190226023539 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019022… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 78d9fb8563..7eb62ba1a0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190213183713", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", - "scratch-vm": "0.2.0-prerelease.20190215190223", + "scratch-vm": "0.2.0-prerelease.20190226023539", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 9bed95d18990f82124290ffc4e2266979cc4284e Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 5 Mar 2019 14:50:44 -0500 Subject: [PATCH 1037/1971] Merge pull request #4621 from ericrosenbaum/feature/boost-coming-soon Add Boost extension "coming soon" tile --- .../src/lib/libraries/extensions/index.jsx | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index 6517473dba..9bc3bc3d2e 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -16,6 +16,8 @@ import text2speechImage from './text2speech.png'; import text2speechInsetImage from './text2speech-small.svg'; import makeymakeyImage from './makeymakey.png'; import makeymakeyInsetImage from './makeymakey-small.svg'; +import boostImage from './boost.png'; +import boostInsetImage from './boost-small.svg'; import microbitPeripheralImage from './peripheral-connection/microbit/microbit-illustration.svg'; import microbitMenuImage from './peripheral-connection/microbit/microbit-small.svg'; @@ -232,6 +234,22 @@ export default [ /> ), helpLink: 'https://scratch.mit.edu/wedo' - + }, + { + name: 'LEGO BOOST', + extensionId: 'boost', + collaborator: 'LEGO', + iconURL: boostImage, + insetIconURL: boostInsetImage, + description: ( + + ), + featured: true, + disabled: true, + bluetoothRequired: true } ]; From 2ce59e4954fb1d16a93a31e4212c6ea29797fc08 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 6 Mar 2019 11:19:33 -0500 Subject: [PATCH 1038/1971] Merge pull request #4625 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190304203619 chore(package): update scratch-vm to version 0.2.0-prerelease.20190304203619 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7eb62ba1a0..7131d2b893 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190213183713", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", - "scratch-vm": "0.2.0-prerelease.20190226023539", + "scratch-vm": "0.2.0-prerelease.20190304203619", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 3e2663d2072ad7084b1ca2a8847b8cd90bf6e719 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 6 Mar 2019 17:10:11 -0500 Subject: [PATCH 1039/1971] Merge pull request #4629 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1551865183 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.155… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7131d2b893..4cffb15bac 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1549990124", + "scratch-blocks": "0.1.0-prerelease.1551865183", "scratch-l10n": "3.1.20190220143732", "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190213183713", From c635d357d8816323a30ea68abcd6869eaf9f854e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 6 Mar 2019 17:10:51 -0500 Subject: [PATCH 1040/1971] Merge pull request #4630 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190306171051 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019030… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4cffb15bac..6e9462cc85 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190213183713", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", - "scratch-vm": "0.2.0-prerelease.20190304203619", + "scratch-vm": "0.2.0-prerelease.20190306171051", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 9f2c9bd8c5c07ca06336b7215221e59580e63848 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 6 Mar 2019 17:12:30 -0500 Subject: [PATCH 1041/1971] Merge pull request #4616 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20190304180800 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6e9462cc85..38641d1d67 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-paint": "0.2.0-prerelease.20190114205252", "scratch-render": "0.1.0-prerelease.20190213183713", "scratch-storage": "1.2.2", - "scratch-svg-renderer": "0.2.0-prerelease.20190125192231", + "scratch-svg-renderer": "0.2.0-prerelease.20190304180800", "scratch-vm": "0.2.0-prerelease.20190306171051", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 91852ea71f08a8657e258cc78e774850a058264d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 11 Mar 2019 11:00:31 -0400 Subject: [PATCH 1042/1971] Merge pull request #4628 from LLK/touch-picker Make the stage color picker work with touch. --- packages/scratch-gui/src/containers/stage.jsx | 90 +++++++++++-------- 1 file changed, 54 insertions(+), 36 deletions(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 5cee4e89bd..e0e3096a15 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -112,7 +112,9 @@ class Stage extends React.Component { } startColorPickingLoop () { this.intervalId = setInterval(() => { - this.setState({colorInfo: this.getColorInfo(this.pickX, this.pickY)}); + if (typeof this.pickX === 'number') { + this.setState({colorInfo: this.getColorInfo(this.pickX, this.pickY)}); + } }, 30); } stopColorPickingLoop () { @@ -175,9 +177,11 @@ class Stage extends React.Component { const {x, y} = getEventXY(e); const mousePosition = [x - this.rect.left, y - this.rect.top]; - // Set the pickX/Y for the color picker loop to pick up - this.pickX = mousePosition[0]; - this.pickY = mousePosition[1]; + if (this.props.isColorPicking) { + // Set the pickX/Y for the color picker loop to pick up + this.pickX = mousePosition[0]; + this.pickY = mousePosition[1]; + } if (this.state.mouseDown && !this.state.isDragging) { const distanceFromMouseDown = Math.sqrt( @@ -231,38 +235,11 @@ class Stage extends React.Component { this.onStopDrag(mousePosition[0], mousePosition[1]); } this.props.vm.postIOData('mouse', data); - } - onMouseDown (e) { - this.updateRect(); - const {x, y} = getEventXY(e); - const mousePosition = [x - this.rect.left, y - this.rect.top]; - if (e.button === 0 || (window.TouchEvent && e instanceof TouchEvent)) { - this.setState({ - mouseDown: true, - mouseDownPosition: mousePosition, - mouseDownTimeoutId: setTimeout( - this.onStartDrag.bind(this, mousePosition[0], mousePosition[1]), - 400 - ) - }); - } - const data = { - isDown: true, - x: mousePosition[0], - y: mousePosition[1], - canvasWidth: this.rect.width, - canvasHeight: this.rect.height - }; - this.props.vm.postIOData('mouse', data); - if (e.preventDefault) { - // Prevent default to prevent touch from dragging page - e.preventDefault(); - // But we do want any active input to be blurred - if (document.activeElement && document.activeElement.blur) { - document.activeElement.blur(); - } - } - if (this.props.isColorPicking) { + + if (this.props.isColorPicking && + mousePosition[0] > 0 && mousePosition[0] < this.rect.width && + mousePosition[1] > 0 && mousePosition[1] < this.rect.height + ) { const {r, g, b} = this.state.colorInfo.color; const componentToString = c => { const hex = c.toString(16); @@ -271,6 +248,47 @@ class Stage extends React.Component { const colorString = `#${componentToString(r)}${componentToString(g)}${componentToString(b)}`; this.props.onDeactivateColorPicker(colorString); this.setState({colorInfo: null}); + this.pickX = null; + this.pickY = null; + } + } + onMouseDown (e) { + this.updateRect(); + const {x, y} = getEventXY(e); + const mousePosition = [x - this.rect.left, y - this.rect.top]; + if (this.props.isColorPicking) { + // Set the pickX/Y for the color picker loop to pick up + this.pickX = mousePosition[0]; + this.pickY = mousePosition[1]; + // Immediately update the color picker info + this.setState({colorInfo: this.getColorInfo(this.pickX, this.pickY)}); + } else { + if (e.button === 0 || (window.TouchEvent && e instanceof TouchEvent)) { + this.setState({ + mouseDown: true, + mouseDownPosition: mousePosition, + mouseDownTimeoutId: setTimeout( + this.onStartDrag.bind(this, mousePosition[0], mousePosition[1]), + 400 + ) + }); + } + const data = { + isDown: true, + x: mousePosition[0], + y: mousePosition[1], + canvasWidth: this.rect.width, + canvasHeight: this.rect.height + }; + this.props.vm.postIOData('mouse', data); + if (e.preventDefault) { + // Prevent default to prevent touch from dragging page + e.preventDefault(); + // But we do want any active input to be blurred + if (document.activeElement && document.activeElement.blur) { + document.activeElement.blur(); + } + } } } onWheel (e) { From e4a42497e0ffd3e6a07d85acc779c5b48ec16b32 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 11 Mar 2019 11:37:23 -0400 Subject: [PATCH 1043/1971] Merge pull request #4635 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190304181151 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-render to version 0.1.0-prerelease.201… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 38641d1d67..41e0cc1562 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "scratch-blocks": "0.1.0-prerelease.1551865183", "scratch-l10n": "3.1.20190220143732", "scratch-paint": "0.2.0-prerelease.20190114205252", - "scratch-render": "0.1.0-prerelease.20190213183713", + "scratch-render": "0.1.0-prerelease.20190304181151", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190304180800", "scratch-vm": "0.2.0-prerelease.20190306171051", From 68866eb10bafd0d503e8f488bacd27590c53d3a6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 11 Mar 2019 11:37:36 -0400 Subject: [PATCH 1044/1971] Merge pull request #4633 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190308165927 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 41e0cc1562..fa5daf8272 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190304181151", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190304180800", - "scratch-vm": "0.2.0-prerelease.20190306171051", + "scratch-vm": "0.2.0-prerelease.20190308165927", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From edba4bb9d19ffb7c88e7e4880f625244b75262f0 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 11 Mar 2019 15:55:26 -0400 Subject: [PATCH 1045/1971] Merge pull request #4636 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190311150519 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index fa5daf8272..b14bfe7737 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1551865183", "scratch-l10n": "3.1.20190220143732", - "scratch-paint": "0.2.0-prerelease.20190114205252", + "scratch-paint": "0.2.0-prerelease.20190311150519", "scratch-render": "0.1.0-prerelease.20190304181151", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190304180800", From cb6c3b4a9ff62470cb65114080679724fa70f67b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 12 Mar 2019 14:35:38 -0400 Subject: [PATCH 1046/1971] Merge pull request #4642 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190312125229 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b14bfe7737..509cf39f56 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "scratch-blocks": "0.1.0-prerelease.1551865183", "scratch-l10n": "3.1.20190220143732", "scratch-paint": "0.2.0-prerelease.20190311150519", - "scratch-render": "0.1.0-prerelease.20190304181151", + "scratch-render": "0.1.0-prerelease.20190312125229", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190304180800", "scratch-vm": "0.2.0-prerelease.20190308165927", From 03d3126f97d78c0ffeadd38011e0756c073f760d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 13 Mar 2019 14:08:56 -0400 Subject: [PATCH 1047/1971] Merge pull request #4647 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190312212117 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 509cf39f56..fc91a9c818 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190312125229", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190304180800", - "scratch-vm": "0.2.0-prerelease.20190308165927", + "scratch-vm": "0.2.0-prerelease.20190312212117", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From dbe0c4d9af98c740bee9e407567998d941dbbc26 Mon Sep 17 00:00:00 2001 From: Evelyn Eastmond Date: Wed, 13 Mar 2019 19:39:57 -0400 Subject: [PATCH 1048/1971] Merge pull request #4641 from evhan55/feature/extensions-wifi-icon Allow for both bluetooth and internet connection icons in extensions --- packages/scratch-gui/src/lib/libraries/extensions/index.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index 9bc3bc3d2e..3d1dd66585 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -163,6 +163,7 @@ export default [ featured: true, disabled: false, bluetoothRequired: true, + internetConnectionRequired: true, launchPeripheralConnectionFlow: true, useAutoScan: false, peripheralImage: microbitPeripheralImage, @@ -192,6 +193,7 @@ export default [ featured: true, disabled: false, bluetoothRequired: true, + internetConnectionRequired: true, launchPeripheralConnectionFlow: true, useAutoScan: false, peripheralImage: ev3PeripheralImage, @@ -221,6 +223,7 @@ export default [ featured: true, disabled: false, bluetoothRequired: true, + internetConnectionRequired: true, launchPeripheralConnectionFlow: true, useAutoScan: true, peripheralImage: wedoPeripheralImage, @@ -250,6 +253,7 @@ export default [ ), featured: true, disabled: true, - bluetoothRequired: true + bluetoothRequired: true, + internetConnectionRequired: true } ]; From 2a104715ccaa4242335ac32510aa178489ecb9c7 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 14 Mar 2019 08:03:06 -0400 Subject: [PATCH 1049/1971] Merge pull request #4651 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1552509492 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index fc91a9c818..4af6fcb044 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1551865183", + "scratch-blocks": "0.1.0-prerelease.1552509492", "scratch-l10n": "3.1.20190220143732", "scratch-paint": "0.2.0-prerelease.20190311150519", "scratch-render": "0.1.0-prerelease.20190312125229", From 35671adda326002bb4e5c7396ad4eb151f9a01f4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 14 Mar 2019 08:03:48 -0400 Subject: [PATCH 1050/1971] Merge pull request #4652 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190314020908 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019031… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4af6fcb044..11b8a5f993 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190312125229", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190304180800", - "scratch-vm": "0.2.0-prerelease.20190312212117", + "scratch-vm": "0.2.0-prerelease.20190314020908", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 34d1b508c2f73c2bfadbe41c1926dac1a9607f03 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Thu, 14 Mar 2019 10:47:59 -0700 Subject: [PATCH 1051/1971] Merge pull request #4645 from cwillisf/add-telemetry-hooks Add telemetry hooks --- .../src/components/menu-bar/menu-bar.jsx | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index f985881c15..972809d2d4 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -6,6 +6,8 @@ import bindAll from 'lodash.bindall'; import bowser from 'bowser'; import React from 'react'; +import VM from 'scratch-vm'; + import Box from '../box/box.jsx'; import Button from '../button/button.jsx'; import CommunityButton from './community-button.jsx'; @@ -55,6 +57,8 @@ import { loginMenuOpen } from '../../reducers/menus'; +import collectMetadata from '../../lib/collect-metadata'; + import styles from './menu-bar.css'; import helpIcon from '../../lib/assets/icon--tutorials.svg'; @@ -146,10 +150,10 @@ class MenuBar extends React.Component { 'handleClickSaveAsCopy', 'handleClickSeeCommunity', 'handleClickShare', - 'handleCloseFileMenuAndThen', 'handleKeyPress', 'handleLanguageMouseUp', 'handleRestoreOption', + 'handleSaveToComputer', 'restoreOptionMessage' ]); } @@ -216,12 +220,6 @@ class MenuBar extends React.Component { this.props.onRequestCloseEdit(); }; } - handleCloseFileMenuAndThen (fn) { - return () => { - this.props.onRequestCloseFile(); - fn(); - }; - } handleKeyPress (event) { const modifier = bowser.mac ? event.metaKey : event.ctrlKey; if (modifier && event.key === 's') { @@ -229,6 +227,16 @@ class MenuBar extends React.Component { event.preventDefault(); } } + handleSaveToComputer (downloadProjectCallback) { + return () => { + this.props.onRequestCloseFile(); + downloadProjectCallback(); + if (this.props.onProjectTelemetryEvent) { + const metadata = collectMetadata(this.props.vm, this.props.projectTitle, this.props.locale); + this.props.onProjectTelemetryEvent('projectDidSave', metadata); + } + }; + } handleLanguageMouseUp (e) { if (!this.props.languageMenuOpen) { this.props.onClickLanguage(e); @@ -402,10 +410,10 @@ class MenuBar extends React.Component { )} - {(className, downloadProject) => ( + {(className, downloadProjectCallback) => ( { isUpdating: getIsUpdating(loadingState), isShowingProject: getIsShowingProject(loadingState), languageMenuOpen: languageMenuOpen(state), + locale: state.locales.locale, loginMenuOpen: loginMenuOpen(state), projectChanged: state.scratchGui.projectChanged, projectTitle: state.scratchGui.projectTitle, sessionExists: state.session && typeof state.session.session !== 'undefined', - username: user ? user.username : null + username: user ? user.username : null, + vm: state.scratchGui.vm }; }; From 8ee759b61f32220347dd83f613bc183249562ebd Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 15 Mar 2019 11:20:05 -0400 Subject: [PATCH 1052/1971] Merge pull request #4655 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190314205043 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 11b8a5f993..1a2c6d5d55 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190312125229", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190304180800", - "scratch-vm": "0.2.0-prerelease.20190314020908", + "scratch-vm": "0.2.0-prerelease.20190314205043", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 5c5eee971dd8ee72b40e46d645677fe11547f918 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 18 Mar 2019 13:15:26 -0400 Subject: [PATCH 1053/1971] Merge pull request #4658 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190318152140 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1a2c6d5d55..2ac1affca2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190312125229", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190304180800", - "scratch-vm": "0.2.0-prerelease.20190314205043", + "scratch-vm": "0.2.0-prerelease.20190318152140", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From aaaf23cc0c3b8ec484d80f87c3f0df602a1ec85a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 18 Mar 2019 13:18:24 -0400 Subject: [PATCH 1054/1971] Merge pull request #4660 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1552662801 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.155… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2ac1affca2..109eb6ddaa 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1552509492", + "scratch-blocks": "0.1.0-prerelease.1552662801", "scratch-l10n": "3.1.20190220143732", "scratch-paint": "0.2.0-prerelease.20190311150519", "scratch-render": "0.1.0-prerelease.20190312125229", From 81bb950ddb1a85287c30e045ea58f136ae1e0928 Mon Sep 17 00:00:00 2001 From: Evelyn Eastmond Date: Tue, 19 Mar 2019 11:10:58 -0400 Subject: [PATCH 1055/1971] Merge pull request #4657 from evhan55/extensions/lib-index-file-refactor Refactor extensions library index file --- .../src/lib/libraries/extensions/index.jsx | 113 ++++++++++-------- 1 file changed, 62 insertions(+), 51 deletions(-) diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index 3d1dd66585..b80a72945a 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -1,31 +1,42 @@ import React from 'react'; import {FormattedMessage} from 'react-intl'; -import musicImage from './music.png'; -import musicInsetImage from './music-small.svg'; -import penImage from './pen.png'; -import penInsetImage from './pen-small.svg'; -import videoImage from './video-sensing.png'; -import videoInsetImage from './video-sensing-small.svg'; -import translateImage from './translate.png'; -import translateInsetImage from './translate-small.png'; -import microbitImage from './microbit.png'; -import ev3Image from './ev3.png'; -import wedoImage from './wedo.png'; -import text2speechImage from './text2speech.png'; -import text2speechInsetImage from './text2speech-small.svg'; -import makeymakeyImage from './makeymakey.png'; -import makeymakeyInsetImage from './makeymakey-small.svg'; -import boostImage from './boost.png'; -import boostInsetImage from './boost-small.svg'; +import musicIconURL from './music/music.png'; +import musicInsetIconURL from './music/music-small.svg'; -import microbitPeripheralImage from './peripheral-connection/microbit/microbit-illustration.svg'; -import microbitMenuImage from './peripheral-connection/microbit/microbit-small.svg'; -import ev3PeripheralImage from './peripheral-connection/ev3/ev3-hub-illustration.svg'; -import ev3MenuImage from './peripheral-connection/ev3/ev3-small.svg'; -import wedoPeripheralImage from './peripheral-connection/wedo/wedo-illustration.svg'; -import wedoMenuImage from './peripheral-connection/wedo/wedo-small.svg'; -import wedoButtonImage from './peripheral-connection/wedo/wedo-button-illustration.svg'; +import penIconURL from './pen/pen.png'; +import penInsetIconURL from './pen/pen-small.svg'; + +import videoSensingIconURL from './videoSensing/video-sensing.png'; +import videoSensingInsetIconURL from './videoSensing/video-sensing-small.svg'; + +import text2speechIconURL from './text2speech/text2speech.png'; +import text2speechInsetIconURL from './text2speech/text2speech-small.svg'; + +import translateIconURL from './translate/translate.png'; +import translateInsetIconURL from './translate/translate-small.png'; + +import makeymakeyIconURL from './makeymakey/makeymakey.png'; +import makeymakeyInsetIconURL from './makeymakey/makeymakey-small.svg'; + +import microbitIconURL from './microbit/microbit.png'; +import microbitInsetIconURL from './microbit/microbit-small.svg'; +import microbitConnectionIconURL from './microbit/microbit-illustration.svg'; +import microbitConnectionSmallIconURL from './microbit/microbit-small.svg'; + +import ev3IconURL from './ev3/ev3.png'; +import ev3InsetIconURL from './ev3/ev3-small.svg'; +import ev3ConnectionIconURL from './ev3/ev3-hub-illustration.svg'; +import ev3ConnectionSmallIconURL from './ev3/ev3-small.svg'; + +import wedo2IconURL from './wedo2/wedo.png'; // TODO: Rename file names to match variable/prop names? +import wedo2InsetIconURL from './wedo2/wedo-small.svg'; +import wedo2ConnectionIconURL from './wedo2/wedo-illustration.svg'; +import wedo2ConnectionSmallIconURL from './wedo2/wedo-small.svg'; +import wedo2ConnectionTipIconURL from './wedo2/wedo-button-illustration.svg'; + +import boostIconURL from './boost/boost.png'; +import boostInsetIconURL from './boost/boost-small.svg'; export default [ { @@ -37,8 +48,8 @@ export default [ /> ), extensionId: 'music', - iconURL: musicImage, - insetIconURL: musicInsetImage, + iconURL: musicIconURL, + insetIconURL: musicInsetIconURL, description: ( ), extensionId: 'pen', - iconURL: penImage, - insetIconURL: penInsetImage, + iconURL: penIconURL, + insetIconURL: penInsetIconURL, description: ( ), extensionId: 'videoSensing', - iconURL: videoImage, - insetIconURL: videoInsetImage, + iconURL: videoSensingIconURL, + insetIconURL: videoSensingInsetIconURL, description: ( Date: Tue, 19 Mar 2019 14:08:09 -0400 Subject: [PATCH 1056/1971] Merge pull request #4666 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190319154833 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019031… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 109eb6ddaa..683332eedb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190312125229", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190304180800", - "scratch-vm": "0.2.0-prerelease.20190318152140", + "scratch-vm": "0.2.0-prerelease.20190319154833", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 5180eeb12fb1dfdd3f71c616e5c21c075c417c12 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 20 Mar 2019 11:51:53 -0400 Subject: [PATCH 1057/1971] Merge pull request #4671 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190319213349 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 683332eedb..6ddf96a8b4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190312125229", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190304180800", - "scratch-vm": "0.2.0-prerelease.20190319154833", + "scratch-vm": "0.2.0-prerelease.20190319213349", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From be7146e7bae57ccde75f2896dc66b684ed33e139 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 20 Mar 2019 15:06:35 -0400 Subject: [PATCH 1058/1971] Merge pull request #4673 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190320150859 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6ddf96a8b4..458f452097 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190312125229", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190304180800", - "scratch-vm": "0.2.0-prerelease.20190319213349", + "scratch-vm": "0.2.0-prerelease.20190320150859", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 109bfa69b6bbcd06c59344aefceb885abb0effa2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 20 Mar 2019 15:27:00 -0400 Subject: [PATCH 1059/1971] Merge pull request #4661 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190318170811 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 458f452097..f672e5c2a8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1552662801", "scratch-l10n": "3.1.20190220143732", - "scratch-paint": "0.2.0-prerelease.20190311150519", + "scratch-paint": "0.2.0-prerelease.20190318170811", "scratch-render": "0.1.0-prerelease.20190312125229", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190304180800", From b9602998c84cf20babd8768af0b34ce837adf2f3 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Thu, 21 Mar 2019 11:21:53 -0400 Subject: [PATCH 1060/1971] Merge pull request #4675 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190321151527 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f672e5c2a8..d147b23727 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190312125229", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190304180800", - "scratch-vm": "0.2.0-prerelease.20190320150859", + "scratch-vm": "0.2.0-prerelease.20190321151527", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 3872c63e897f417c5ec27d7d0b21168f89b44dd7 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 26 Mar 2019 12:07:05 -0400 Subject: [PATCH 1061/1971] Merge pull request #4688 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190325203133 chore(package): update scratch-vm to latest version --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d147b23727..43eaa6cbbb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190312125229", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190304180800", - "scratch-vm": "0.2.0-prerelease.20190321151527", + "scratch-vm": "0.2.0-prerelease.20190326160300", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 15a7b6dc31e2ba376670e092c4c8ad8ceed9ed05 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 28 Mar 2019 14:01:18 -0400 Subject: [PATCH 1062/1971] Merge pull request #4674 from mzgoddard/project-changed dispatch projectChange redux action when project has not changed --- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index efeaa35008..bac53a5140 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -74,7 +74,7 @@ const vmListenerHOC = function (WrappedComponent) { } } handleProjectChanged () { - if (this.props.shouldEmitUpdates) { + if (this.props.shouldEmitUpdates && !this.props.projectChanged) { this.props.onProjectChanged(); } } @@ -117,6 +117,7 @@ const vmListenerHOC = function (WrappedComponent) { const { /* eslint-disable no-unused-vars */ attachKeyboardEvents, + projectChanged, shouldEmitUpdates, onBlockDragUpdate, onGreenFlag, @@ -154,6 +155,7 @@ const vmListenerHOC = function (WrappedComponent) { onTargetsUpdate: PropTypes.func.isRequired, onTurboModeOff: PropTypes.func.isRequired, onTurboModeOn: PropTypes.func.isRequired, + projectChanged: PropTypes.bool, shouldEmitUpdates: PropTypes.bool, username: PropTypes.string, vm: PropTypes.instanceOf(VM).isRequired @@ -163,6 +165,7 @@ const vmListenerHOC = function (WrappedComponent) { onGreenFlag: () => ({}) }; const mapStateToProps = state => ({ + projectChanged: state.scratchGui.projectChanged, // Do not emit target or project updates in fullscreen or player only mode // or when recording sounds (it leads to garbled recordings on low-power machines) shouldEmitUpdates: !state.scratchGui.mode.isFullScreen && !state.scratchGui.mode.isPlayerOnly && From aca389bfb0dec9cc9231fd4d8664bf6be38ea933 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Fri, 29 Mar 2019 10:19:05 +0100 Subject: [PATCH 1063/1971] Merge pull request #4695 from chrisgarrity/update-l10n update scratch-l10n to latest --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 43eaa6cbbb..e5b021f504 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1552662801", - "scratch-l10n": "3.1.20190220143732", + "scratch-l10n": "3.1.20190328114324", "scratch-paint": "0.2.0-prerelease.20190318170811", "scratch-render": "0.1.0-prerelease.20190312125229", "scratch-storage": "1.2.2", From c3e273841826c3132aaa8b7b51bcf246db699677 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 3 Apr 2019 11:39:08 -0400 Subject: [PATCH 1064/1971] Merge pull request #4712 from kchadha/extension-button-support Register callback keys for buttons used in future extensions. --- packages/scratch-gui/src/containers/blocks.jsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index ecb998ea4c..d79c7e9c72 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -96,6 +96,21 @@ class Blocks extends React.Component { ); this.workspace = this.ScratchBlocks.inject(this.blocks, workspaceConfig); + // Register buttons under new callback keys for creating variables, + // lists, and procedures from extensions. + + const toolboxWorkspace = this.workspace.getFlyout().getWorkspace(); + + const varListButtonCallback = type => + (() => this.ScratchBlocks.Variables.createVariable(this.workspace, null, type)); + const procButtonCallback = () => { + this.ScratchBlocks.Procedures.createProcedureDefCallback_(this.workspace); + }; + + toolboxWorkspace.registerButtonCallback('MAKE_A_VARIABLE', varListButtonCallback('')); + toolboxWorkspace.registerButtonCallback('MAKE_A_LIST', varListButtonCallback('list')); + toolboxWorkspace.registerButtonCallback('MAKE_A_PROCEDURE', procButtonCallback); + // Store the xml of the toolbox that is actually rendered. // This is used in componentDidUpdate instead of prevProps, because // the xml can change while e.g. on the costumes tab. From 87ad2792d659787c30ebc30ecc0b2965cbbbb4d4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 3 Apr 2019 13:53:23 -0400 Subject: [PATCH 1065/1971] Merge pull request #4699 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190329172358 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e5b021f504..92bef64890 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190312125229", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190304180800", - "scratch-vm": "0.2.0-prerelease.20190326160300", + "scratch-vm": "0.2.0-prerelease.20190329172358", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 9540580cdd8fb09c3eb5f43245de74dc9b5d5b32 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 3 Apr 2019 13:54:04 -0400 Subject: [PATCH 1066/1971] Merge pull request #4698 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20190329052730 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 92bef64890..2f3c80fec3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-paint": "0.2.0-prerelease.20190318170811", "scratch-render": "0.1.0-prerelease.20190312125229", "scratch-storage": "1.2.2", - "scratch-svg-renderer": "0.2.0-prerelease.20190304180800", + "scratch-svg-renderer": "0.2.0-prerelease.20190329052730", "scratch-vm": "0.2.0-prerelease.20190329172358", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 7e3e036941c1f32fe869376a6caa328a5505cc22 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 3 Apr 2019 13:55:13 -0400 Subject: [PATCH 1067/1971] Merge pull request #4713 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1554297463 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-blocks to version 0.1.0-prerelease.155… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2f3c80fec3..c9691f5e85 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1552662801", + "scratch-blocks": "0.1.0-prerelease.1554297463", "scratch-l10n": "3.1.20190328114324", "scratch-paint": "0.2.0-prerelease.20190318170811", "scratch-render": "0.1.0-prerelease.20190312125229", From 3f753ea9f87b2cda0e5c65507e61b7f4e6863d89 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 3 Apr 2019 13:55:35 -0400 Subject: [PATCH 1068/1971] Merge pull request #4689 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190326161919 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c9691f5e85..61463a56ef 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "scratch-blocks": "0.1.0-prerelease.1554297463", "scratch-l10n": "3.1.20190328114324", "scratch-paint": "0.2.0-prerelease.20190318170811", - "scratch-render": "0.1.0-prerelease.20190312125229", + "scratch-render": "0.1.0-prerelease.20190326161919", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190329052730", "scratch-vm": "0.2.0-prerelease.20190329172358", From f2874ac70d00480fbedd73d094c5fc8e8c60ed76 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 4 Apr 2019 12:02:42 +0200 Subject: [PATCH 1069/1971] Merge pull request #4716 from LLK/greenkeeper/scratch-l10n-3.1.20190404094457 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 61463a56ef..cf240c81e4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1554297463", - "scratch-l10n": "3.1.20190328114324", + "scratch-l10n": "3.1.20190404094457", "scratch-paint": "0.2.0-prerelease.20190318170811", "scratch-render": "0.1.0-prerelease.20190326161919", "scratch-storage": "1.2.2", From f651a7c1ccd97b99c13723fd6b3c6326eda90a3c Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 9 Apr 2019 15:32:11 -0400 Subject: [PATCH 1070/1971] Merge pull request #4720 from ericrosenbaum/feature/add-vernier-extension2 Add Vernier Force and Accel extension --- .../src/lib/libraries/extensions/index.jsx | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index b80a72945a..5b05be4f28 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -38,6 +38,11 @@ import wedo2ConnectionTipIconURL from './wedo2/wedo-button-illustration.svg'; import boostIconURL from './boost/boost.png'; import boostInsetIconURL from './boost/boost-small.svg'; +import gdxforIconURL from './gdxfor/gdxfor.png'; +import gdxforInsetIconURL from './gdxfor/gdxfor-small.svg'; +import gdxforConnectionIconURL from './gdxfor/gdxfor-illustration.svg'; +import gdxforConnectionSmallIconURL from './gdxfor/gdxfor-small.svg'; + export default [ { name: ( @@ -266,5 +271,35 @@ export default [ disabled: true, bluetoothRequired: true, internetConnectionRequired: true + }, + { + name: 'GoDirect Force & Acceleration', + extensionId: 'gdxfor', + collaborator: 'Vernier', + iconURL: gdxforIconURL, + insetIconURL: gdxforInsetIconURL, + description: ( + + ), + featured: true, + disabled: false, + bluetoothRequired: true, + internetConnectionRequired: true, + launchPeripheralConnectionFlow: true, + useAutoScan: false, + connectionIconURL: gdxforConnectionIconURL, + connectionSmallIconURL: gdxforConnectionSmallIconURL, + connectingMessage: ( + + ), + helpLink: 'https://scratch.mit.edu/vernier' } ]; From 73e2418d29517a00f709c6a605718cdaff64b9fa Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 10 Apr 2019 09:01:42 -0400 Subject: [PATCH 1071/1971] Merge pull request #4725 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190409205534 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019040… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cf240c81e4..31b4fcc777 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190326161919", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190329052730", - "scratch-vm": "0.2.0-prerelease.20190329172358", + "scratch-vm": "0.2.0-prerelease.20190409205534", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 3505d78448a21cd277a7cdf612f6e8e979fca144 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 10 Apr 2019 12:11:58 -0400 Subject: [PATCH 1072/1971] Merge pull request #4728 from LLK/greenkeeper/scratch-l10n-3.1.20190410143750 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 31b4fcc777..48d7d46adc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1554297463", - "scratch-l10n": "3.1.20190404094457", + "scratch-l10n": "3.1.20190410143750", "scratch-paint": "0.2.0-prerelease.20190318170811", "scratch-render": "0.1.0-prerelease.20190326161919", "scratch-storage": "1.2.2", From 19c17143f191dee917fac68eed8e92c9a8a3d3fd Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 10 Apr 2019 12:12:27 -0400 Subject: [PATCH 1073/1971] Merge pull request #4730 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190410143927 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019041… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 48d7d46adc..7283a9b905 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190326161919", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190329052730", - "scratch-vm": "0.2.0-prerelease.20190409205534", + "scratch-vm": "0.2.0-prerelease.20190410143927", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 25999f02a589d62df922af35968db618b12bf9df Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 10 Apr 2019 13:29:57 -0400 Subject: [PATCH 1074/1971] Merge pull request #4726 from LLK/greenkeeper/scratch-storage-1.3.1 chore(package): update scratch-storage to version 1.3.1 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7283a9b905..f397909683 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-l10n": "3.1.20190410143750", "scratch-paint": "0.2.0-prerelease.20190318170811", "scratch-render": "0.1.0-prerelease.20190326161919", - "scratch-storage": "1.2.2", + "scratch-storage": "1.3.1", "scratch-svg-renderer": "0.2.0-prerelease.20190329052730", "scratch-vm": "0.2.0-prerelease.20190410143927", "selenium-webdriver": "3.6.0", From 9f4017deaf5e81272da629225642677e70dd558b Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 10 Apr 2019 11:35:46 -0700 Subject: [PATCH 1075/1971] Merge pull request #4644 from cwillisf/load-library-images-through-storage Load library images through storage --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f397909683..89ecba684d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -100,6 +100,7 @@ "react-test-renderer": "16.2.0", "react-tooltip": "3.8.0", "react-virtualized": "9.20.1", + "react-visibility-sensor": "5.0.2", "redux": "3.7.2", "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", From 0e457a3bf34ade9c7e2ff571aab4cbdd4b92e0cc Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 10 Apr 2019 16:39:15 -0400 Subject: [PATCH 1076/1971] Merge pull request #4732 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190410184112 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 89ecba684d..0c298eee8a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-blocks": "0.1.0-prerelease.1554297463", "scratch-l10n": "3.1.20190410143750", "scratch-paint": "0.2.0-prerelease.20190318170811", - "scratch-render": "0.1.0-prerelease.20190326161919", + "scratch-render": "0.1.0-prerelease.20190410184112", "scratch-storage": "1.3.1", "scratch-svg-renderer": "0.2.0-prerelease.20190329052730", "scratch-vm": "0.2.0-prerelease.20190410143927", From 1f6ae71977c7eee17f589fc62ba146f879874b06 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 11 Apr 2019 11:14:20 -0400 Subject: [PATCH 1077/1971] Merge pull request #4736 from LLK/revert-4644-load-library-images-through-storage Revert "Load library images through storage" --- packages/scratch-gui/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0c298eee8a..869201c487 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -100,7 +100,6 @@ "react-test-renderer": "16.2.0", "react-tooltip": "3.8.0", "react-virtualized": "9.20.1", - "react-visibility-sensor": "5.0.2", "redux": "3.7.2", "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", From 1db2963fc721b16533074c0327802224f3d6d76b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 11 Apr 2019 11:14:36 -0400 Subject: [PATCH 1078/1971] Merge pull request #4735 from paulkaplan/revert-storage-update Revert storage update due to asset saving errors --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 869201c487..cbc44be024 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-l10n": "3.1.20190410143750", "scratch-paint": "0.2.0-prerelease.20190318170811", "scratch-render": "0.1.0-prerelease.20190410184112", - "scratch-storage": "1.3.1", + "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190329052730", "scratch-vm": "0.2.0-prerelease.20190410143927", "selenium-webdriver": "3.6.0", From fb88d3be72f8763ac660fd4d82948129b989c15c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 11 Apr 2019 11:15:29 -0400 Subject: [PATCH 1079/1971] Merge pull request #4734 from LLK/revert-4732-greenkeeper/scratch-render-0.1.0-prerelease.20190410184112 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "Update scratch-render to the latest version 🚀" --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cbc44be024..7283a9b905 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "scratch-blocks": "0.1.0-prerelease.1554297463", "scratch-l10n": "3.1.20190410143750", "scratch-paint": "0.2.0-prerelease.20190318170811", - "scratch-render": "0.1.0-prerelease.20190410184112", + "scratch-render": "0.1.0-prerelease.20190326161919", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190329052730", "scratch-vm": "0.2.0-prerelease.20190410143927", From f57bd7a6e4170ffb5c99f864cdc58f7361c30573 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Wed, 17 Apr 2019 14:54:51 -0400 Subject: [PATCH 1080/1971] Merge pull request #4700 from benjiwheeler/upload-in-place upload in current project id, rather than creating new project id --- .../src/components/menu-bar/menu-bar.jsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 972809d2d4..5970fd4d64 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -393,19 +393,17 @@ class MenuBar extends React.Component { )} - + {(className, renderFileInput, loadProject) => ( - + {this.props.intl.formatMessage(sharedMessages.loadFromComputerTitle)} {renderFileInput()} )} @@ -774,7 +772,7 @@ MenuBar.defaultProps = { onShare: () => {} }; -const mapStateToProps = state => { +const mapStateToProps = (state, ownProps) => { const loadingState = state.scratchGui.projectState.loadingState; const user = state.session && state.session.session && state.session.session.user; return { @@ -791,6 +789,8 @@ const mapStateToProps = state => { projectTitle: state.scratchGui.projectTitle, sessionExists: state.session && typeof state.session.session !== 'undefined', username: user ? user.username : null, + userOwnsProject: ownProps.authorUsername && user && + (ownProps.authorUsername === user.username), vm: state.scratchGui.vm }; }; From 866fb2778c8f3d7f4567b3ac0d4c89a7be00802d Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Wed, 17 Apr 2019 14:55:22 -0400 Subject: [PATCH 1081/1971] Merge pull request #4710 from benjiwheeler/only-autosave-if-changed Share, See-project-page buttons will only save if project has changed --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 5970fd4d64..eafa4578da 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -194,7 +194,7 @@ class MenuBar extends React.Component { this.props.onRequestCloseFile(); } handleClickSeeCommunity (waitForUpdate) { - if (this.props.canSave) { // save before transitioning to project page + if (this.props.canSave && this.props.projectChanged) { // save before transitioning to project page this.props.autoUpdateProject(); waitForUpdate(true); // queue the transition to project page } else { From 9fc319abc46a9d7b8dbb29191c4eb882f4016b56 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 17 Apr 2019 15:47:56 -0400 Subject: [PATCH 1082/1971] Merge pull request #4747 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1555512815 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7283a9b905..4069bbe1ee 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1554297463", + "scratch-blocks": "0.1.0-prerelease.1555512815", "scratch-l10n": "3.1.20190410143750", "scratch-paint": "0.2.0-prerelease.20190318170811", "scratch-render": "0.1.0-prerelease.20190326161919", From 8af520f844295bd78d5897ef2c7f6cd00d411de2 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 17 Apr 2019 16:21:46 -0400 Subject: [PATCH 1083/1971] Merge pull request #4743 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190416185326 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4069bbe1ee..03f5491d03 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1555512815", "scratch-l10n": "3.1.20190410143750", - "scratch-paint": "0.2.0-prerelease.20190318170811", + "scratch-paint": "0.2.0-prerelease.20190416185326", "scratch-render": "0.1.0-prerelease.20190326161919", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190329052730", From 59ea90616317bbf7591302b9dc7dbde2daa5ef21 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 17 Apr 2019 17:08:23 -0400 Subject: [PATCH 1084/1971] Merge pull request #4752 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190417203423 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019041… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 03f5491d03..e8e2c59db9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190326161919", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190329052730", - "scratch-vm": "0.2.0-prerelease.20190410143927", + "scratch-vm": "0.2.0-prerelease.20190417203423", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From a0bcb3b445039f08e136b401502073536440c2f7 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 17 Apr 2019 17:30:19 -0400 Subject: [PATCH 1085/1971] Merge pull request #4751 from LLK/greenkeeper/react-intl-2.8.0 chore(package): update react-intl to version 2.8.0 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e8e2c59db9..298a690126 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -90,7 +90,7 @@ "react-dom": "16.2.0", "react-draggable": "3.0.5", "react-ga": "2.5.3", - "react-intl": "2.4.0", + "react-intl": "2.8.0", "react-modal": "3.6.1", "react-popover": "0.5.10", "react-redux": "5.0.7", From 48009f1255055ef6c4755518d760001ef557cc97 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 17 Apr 2019 17:51:40 -0400 Subject: [PATCH 1086/1971] Merge pull request #4750 from LLK/greenkeeper/scratch-l10n-3.2.20190417195644 chore(package): update scratch-l10n to version 3.2.20190417195644 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 298a690126..cb9e5c452c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1555512815", - "scratch-l10n": "3.1.20190410143750", + "scratch-l10n": "3.2.20190417195644", "scratch-paint": "0.2.0-prerelease.20190416185326", "scratch-render": "0.1.0-prerelease.20190326161919", "scratch-storage": "1.2.2", From bd39c11ec627b7e917ff4eb0d35bab5a01a0552e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 19 Apr 2019 15:17:43 -0400 Subject: [PATCH 1087/1971] Merge pull request #4760 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20190419183947 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cb9e5c452c..b90d4e9368 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-paint": "0.2.0-prerelease.20190416185326", "scratch-render": "0.1.0-prerelease.20190326161919", "scratch-storage": "1.2.2", - "scratch-svg-renderer": "0.2.0-prerelease.20190329052730", + "scratch-svg-renderer": "0.2.0-prerelease.20190419183947", "scratch-vm": "0.2.0-prerelease.20190417203423", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From a3b6b4898caa9a7e1ecad0edb5de7c673f84a7e2 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 19 Apr 2019 17:24:55 -0400 Subject: [PATCH 1088/1971] Merge pull request #4761 from ericrosenbaum/feature/boost-assets Add Boost extension assets --- .../src/lib/libraries/extensions/index.jsx | 52 ++++++++++++------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index 5b05be4f28..5315737d45 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -37,6 +37,9 @@ import wedo2ConnectionTipIconURL from './wedo2/wedo-button-illustration.svg'; import boostIconURL from './boost/boost.png'; import boostInsetIconURL from './boost/boost-small.svg'; +import boostConnectionIconURL from './boost/boost-illustration.svg'; +import boostConnectionSmallIconURL from './boost/boost-small.svg'; +import boostConnectionTipIconURL from './boost/boost-button-illustration.svg'; import gdxforIconURL from './gdxfor/gdxfor.png'; import gdxforInsetIconURL from './gdxfor/gdxfor-small.svg'; @@ -223,6 +226,37 @@ export default [ ), helpLink: 'https://scratch.mit.edu/ev3' }, + { + name: 'LEGO BOOST', + extensionId: 'boost', + collaborator: 'LEGO', + iconURL: boostIconURL, + insetIconURL: boostInsetIconURL, + description: ( + + ), + featured: true, + disabled: true, + bluetoothRequired: true, + internetConnectionRequired: true, + launchPeripheralConnectionFlow: true, + useAutoScan: true, + connectionIconURL: boostConnectionIconURL, + connectionSmallIconURL: boostConnectionSmallIconURL, + connectionTipIconURL: boostConnectionTipIconURL, + connectingMessage: ( + + ), + helpLink: 'https://scratch.mit.edu/boost' + }, { name: 'LEGO Education WeDo 2.0', extensionId: 'wedo2', @@ -254,24 +288,6 @@ export default [ ), helpLink: 'https://scratch.mit.edu/wedo' }, - { - name: 'LEGO BOOST', - extensionId: 'boost', - collaborator: 'LEGO', - iconURL: boostIconURL, - insetIconURL: boostInsetIconURL, - description: ( - - ), - featured: true, - disabled: true, - bluetoothRequired: true, - internetConnectionRequired: true - }, { name: 'GoDirect Force & Acceleration', extensionId: 'gdxfor', From b74a07d3bed9c47f0e75619701ade8ac6f8c6b1c Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Fri, 19 Apr 2019 17:29:10 -0400 Subject: [PATCH 1089/1971] Merge pull request #4676 from mzgoddard/menu-bar Move projectChanged into HOC wrapping MenuBar, to avoid unnecessary renders --- .../src/components/menu-bar/menu-bar.jsx | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index eafa4578da..346b09b7b9 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -1,5 +1,6 @@ import classNames from 'classnames'; import {connect} from 'react-redux'; +import {compose} from 'redux'; import {defineMessages, FormattedMessage, injectIntl, intlShape} from 'react-intl'; import PropTypes from 'prop-types'; import bindAll from 'lodash.bindall'; @@ -27,6 +28,7 @@ import LoginDropdown from './login-dropdown.jsx'; import SB3Downloader from '../../containers/sb3-downloader.jsx'; import DeletionRestorer from '../../containers/deletion-restorer.jsx'; import TurboMode from '../../containers/turbo-mode.jsx'; +import ConfirmReplaceHOC from './confirm-replace-hoc.jsx'; import {openTipsLibrary} from '../../reducers/modals'; import {setPlayer} from '../../reducers/mode'; @@ -164,17 +166,14 @@ class MenuBar extends React.Component { document.removeEventListener('keydown', this.handleKeyPress); } handleClickNew () { - let readyToReplaceProject = true; // if the project is dirty, and user owns the project, we will autosave. // but if they are not logged in and can't save, user should consider // downloading or logging in first. // Note that if user is logged in and editing someone else's project, // they'll lose their work. - if (this.props.projectChanged && !this.props.canCreateNew) { - readyToReplaceProject = confirm( // eslint-disable-line no-alert - this.props.intl.formatMessage(sharedMessages.replaceProjectWarning) - ); - } + const readyToReplaceProject = this.props.confirmReadyToReplaceProject( + this.props.intl.formatMessage(sharedMessages.replaceProjectWarning) + ); this.props.onRequestCloseFile(); if (readyToReplaceProject) { this.props.onClickNew(this.props.canSave && this.props.canCreateNew); @@ -759,7 +758,6 @@ MenuBar.propTypes = { onShare: PropTypes.func, onToggleLoginOpen: PropTypes.func, onUpdateProjectTitle: PropTypes.func, - projectChanged: PropTypes.bool, projectTitle: PropTypes.string, renderLogin: PropTypes.func, sessionExists: PropTypes.bool, @@ -785,7 +783,6 @@ const mapStateToProps = (state, ownProps) => { languageMenuOpen: languageMenuOpen(state), locale: state.locales.locale, loginMenuOpen: loginMenuOpen(state), - projectChanged: state.scratchGui.projectChanged, projectTitle: state.scratchGui.projectTitle, sessionExists: state.session && typeof state.session.session !== 'undefined', username: user ? user.username : null, @@ -815,7 +812,11 @@ const mapDispatchToProps = dispatch => ({ onSeeCommunity: () => dispatch(setPlayer(true)) }); -export default injectIntl(connect( - mapStateToProps, - mapDispatchToProps -)(MenuBar)); +export default compose( + injectIntl, + ConfirmReplaceHOC, + connect( + mapStateToProps, + mapDispatchToProps + ) +)(MenuBar); From ada5f6f5c613b122f0f1ca7fa2a1325ed6460abe Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 23 Apr 2019 08:31:52 -0400 Subject: [PATCH 1090/1971] Merge pull request #4763 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190419202157 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b90d4e9368..e0879387b4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "scratch-blocks": "0.1.0-prerelease.1555512815", "scratch-l10n": "3.2.20190417195644", "scratch-paint": "0.2.0-prerelease.20190416185326", - "scratch-render": "0.1.0-prerelease.20190326161919", + "scratch-render": "0.1.0-prerelease.20190419202157", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190419183947", "scratch-vm": "0.2.0-prerelease.20190417203423", From 4837316425f18d9cf32bbaa874f72eb3b33beea3 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 23 Apr 2019 08:32:09 -0400 Subject: [PATCH 1091/1971] Merge pull request #4757 from paulkaplan/remove-feedback-button Remove unneeded feedback button in menubar --- .../src/components/menu-bar/menu-bar.jsx | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 346b09b7b9..5c957525c5 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -65,7 +65,6 @@ import styles from './menu-bar.css'; import helpIcon from '../../lib/assets/icon--tutorials.svg'; import mystuffIcon from './icon--mystuff.png'; -import feedbackIcon from './icon--feedback.svg'; import profileIcon from './icon--profile.png'; import remixIcon from './icon--remix.svg'; import dropdownCaret from './dropdown-caret.svg'; @@ -643,25 +642,6 @@ class MenuBar extends React.Component { ) : ( // ******** no login session is available, so don't show login stuff - {this.props.showComingSoon ? ( From 40f44758516ac96e5c1d32c9a9d3b8e97c77c923 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 23 Apr 2019 17:54:09 -0400 Subject: [PATCH 1092/1971] Merge pull request #4756 from ericrosenbaum/bugfix/go-space-direct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix Vernier branding (“Go Direct”) --- packages/scratch-gui/src/lib/libraries/extensions/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index 5315737d45..93c69f8114 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -289,7 +289,7 @@ export default [ helpLink: 'https://scratch.mit.edu/wedo' }, { - name: 'GoDirect Force & Acceleration', + name: 'Go Direct Force & Acceleration', extensionId: 'gdxfor', collaborator: 'Vernier', iconURL: gdxforIconURL, From 8f40bdee25fa9dbb5bb3d844756f6a92ef807f7d Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 24 Apr 2019 11:18:42 -0400 Subject: [PATCH 1093/1971] Merge pull request #4769 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1556058260 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e0879387b4..ba9672971b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1555512815", + "scratch-blocks": "0.1.0-prerelease.1556058260", "scratch-l10n": "3.2.20190417195644", "scratch-paint": "0.2.0-prerelease.20190416185326", "scratch-render": "0.1.0-prerelease.20190419202157", From 079f9eb66da534d83783d9a27b0a98ef72ed8444 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 24 Apr 2019 12:51:59 -0400 Subject: [PATCH 1094/1971] Merge pull request #4771 from LLK/ericrosenbaum-update-blocks update scratch-blocks version --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ba9672971b..2b368c1601 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1556058260", + "scratch-blocks": "0.1.0-prerelease.1556115881", "scratch-l10n": "3.2.20190417195644", "scratch-paint": "0.2.0-prerelease.20190416185326", "scratch-render": "0.1.0-prerelease.20190419202157", From c6aec0769ec920898e433a402df65bee8801f0de Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 24 Apr 2019 12:56:25 -0400 Subject: [PATCH 1095/1971] Merge pull request #4772 from LLK/greenkeeper/scratch-l10n-3.2.20190424151256 chore(package): update scratch-l10n to version 3.2.20190424151256 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2b368c1601..16b962ddde 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1556115881", - "scratch-l10n": "3.2.20190417195644", + "scratch-l10n": "3.2.20190424151256", "scratch-paint": "0.2.0-prerelease.20190416185326", "scratch-render": "0.1.0-prerelease.20190419202157", "scratch-storage": "1.2.2", From e6682dd44072462256602d857bdc707e420ed0d6 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 24 Apr 2019 13:25:02 -0400 Subject: [PATCH 1096/1971] Merge pull request #4773 from LLK/ericrosenbaum-update-vm Update scratch-vm --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 16b962ddde..e9145f4881 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190419202157", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190419183947", - "scratch-vm": "0.2.0-prerelease.20190417203423", + "scratch-vm": "0.2.0-prerelease.20190424170336", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 3aa9d33b1d3560b5b83a10599be6b7227e7723a1 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 24 Apr 2019 16:43:00 -0400 Subject: [PATCH 1097/1971] update scratch-vm --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e9145f4881..285dafe695 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190419202157", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190419183947", - "scratch-vm": "0.2.0-prerelease.20190424170336", + "scratch-vm": "0.2.0-prerelease.20190424204124", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 2c9bece86374955072ef22450dea3c3ef544853b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 24 Apr 2019 17:15:09 -0400 Subject: [PATCH 1098/1971] Seriously, update VM to a version that actually exists --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 285dafe695..adca383b30 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190419202157", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190419183947", - "scratch-vm": "0.2.0-prerelease.20190424204124", + "scratch-vm": "0.2.0-prerelease.20190424211319", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From d4c6fe17bc865f44850fa97f8ee1aa31600b63a0 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 30 Apr 2019 14:35:22 -0400 Subject: [PATCH 1099/1971] Merge pull request #4782 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190430175441 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index adca383b30..6659eff665 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1556115881", "scratch-l10n": "3.2.20190424151256", - "scratch-paint": "0.2.0-prerelease.20190416185326", + "scratch-paint": "0.2.0-prerelease.20190430175441", "scratch-render": "0.1.0-prerelease.20190419202157", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190419183947", From 1155d1e066aad5f299629646a8fd16ef1ffd171f Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Wed, 1 May 2019 09:44:24 -0400 Subject: [PATCH 1100/1971] Merge pull request #4784 from benjiwheeler/fix-save-on-see-project-page Fix auto-save when clicking See Project Page --- .../scratch-gui/src/components/menu-bar/menu-bar.jsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 5c957525c5..22cd2d9198 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -28,7 +28,7 @@ import LoginDropdown from './login-dropdown.jsx'; import SB3Downloader from '../../containers/sb3-downloader.jsx'; import DeletionRestorer from '../../containers/deletion-restorer.jsx'; import TurboMode from '../../containers/turbo-mode.jsx'; -import ConfirmReplaceHOC from './confirm-replace-hoc.jsx'; +import MenuBarHOC from '../../containers/menu-bar-hoc.jsx'; import {openTipsLibrary} from '../../reducers/modals'; import {setPlayer} from '../../reducers/mode'; @@ -192,8 +192,8 @@ class MenuBar extends React.Component { this.props.onRequestCloseFile(); } handleClickSeeCommunity (waitForUpdate) { - if (this.props.canSave && this.props.projectChanged) { // save before transitioning to project page - this.props.autoUpdateProject(); + if (this.props.shouldSaveBeforeTransition()) { + this.props.autoUpdateProject(); // save before transitioning to project page waitForUpdate(true); // queue the transition to project page } else { waitForUpdate(false); // immediately transition to project page @@ -705,6 +705,7 @@ MenuBar.propTypes = { canSave: PropTypes.bool, canShare: PropTypes.bool, className: PropTypes.string, + confirmReadyToReplaceProject: PropTypes.func, editMenuOpen: PropTypes.bool, enableCommunity: PropTypes.bool, fileMenuOpen: PropTypes.bool, @@ -741,6 +742,7 @@ MenuBar.propTypes = { projectTitle: PropTypes.string, renderLogin: PropTypes.func, sessionExists: PropTypes.bool, + shouldSaveBeforeTransition: PropTypes.func, showComingSoon: PropTypes.bool, username: PropTypes.string, vm: PropTypes.instanceOf(VM).isRequired @@ -794,7 +796,7 @@ const mapDispatchToProps = dispatch => ({ export default compose( injectIntl, - ConfirmReplaceHOC, + MenuBarHOC, connect( mapStateToProps, mapDispatchToProps From a157f1713a6cf315013a4222480f0dade641ee93 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 1 May 2019 10:22:09 -0400 Subject: [PATCH 1101/1971] Merge pull request #4785 from ericrosenbaum/feature/enable-boost Enable the boost extension --- packages/scratch-gui/src/lib/libraries/extensions/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index 93c69f8114..ba18b916b5 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -240,7 +240,7 @@ export default [ /> ), featured: true, - disabled: true, + disabled: false, bluetoothRequired: true, internetConnectionRequired: true, launchPeripheralConnectionFlow: true, From 02a7d6fdb54e07c0b104a81b7d358cdc49233995 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 1 May 2019 15:00:07 -0400 Subject: [PATCH 1102/1971] Merge pull request #4775 from LLK/greenkeeper/chromedriver-74.0.0 chore(package): update chromedriver to version 74.0.0 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6659eff665..07b6c4fa95 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -44,7 +44,7 @@ "babel-loader": "^8.0.4", "base64-loader": "1.0.0", "bowser": "1.9.4", - "chromedriver": "2.44.1", + "chromedriver": "74.0.0", "classnames": "2.2.6", "computed-style-to-inline-style": "3.0.0", "copy-webpack-plugin": "^4.5.1", From ed36ae1fcaff57ed973481c5de097b01aad2a354 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 1 May 2019 17:07:50 -0400 Subject: [PATCH 1103/1971] Merge pull request #4792 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190430163103 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019043… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 07b6c4fa95..1cc611050c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190419202157", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190419183947", - "scratch-vm": "0.2.0-prerelease.20190424211319", + "scratch-vm": "0.2.0-prerelease.20190430163103", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 2abe0c5ed39f445b9e2cb1e696931a7236a5629b Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 1 May 2019 17:09:25 -0400 Subject: [PATCH 1104/1971] Merge pull request #4793 from LLK/greenkeeper/scratch-l10n-3.2.20190501144004 chore(package): update scratch-l10n to version 3.2.20190501144004 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1cc611050c..a939ddd172 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1556115881", - "scratch-l10n": "3.2.20190424151256", + "scratch-l10n": "3.2.20190501144004", "scratch-paint": "0.2.0-prerelease.20190430175441", "scratch-render": "0.1.0-prerelease.20190419202157", "scratch-storage": "1.2.2", From 42f0f193a304cd34f596e21caae214687321e426 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 6 May 2019 10:33:59 -0400 Subject: [PATCH 1105/1971] Merge pull request #4786 from paulkaplan/update-eslint-react Update eslint-plugin-react and fix newly discovered lint errors. --- packages/scratch-gui/package.json | 2 +- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 2 ++ packages/scratch-gui/src/containers/stage.jsx | 1 + packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 6 ++++-- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a939ddd172..79513dec54 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -56,7 +56,7 @@ "eslint": "^5.0.1", "eslint-config-scratch": "^5.0.0", "eslint-plugin-import": "^2.8.0", - "eslint-plugin-react": "7.11.1", + "eslint-plugin-react": "^7.12.4", "file-loader": "2.0.0", "get-float-time-domain-data": "0.1.0", "get-user-media-promise": "1.1.4", diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 22cd2d9198..6105a60e5c 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -715,6 +715,7 @@ MenuBar.propTypes = { isShowingProject: PropTypes.bool, isUpdating: PropTypes.bool, languageMenuOpen: PropTypes.bool, + locale: PropTypes.string.isRequired, loginMenuOpen: PropTypes.bool, onClickAccount: PropTypes.func, onClickEdit: PropTypes.func, @@ -744,6 +745,7 @@ MenuBar.propTypes = { sessionExists: PropTypes.bool, shouldSaveBeforeTransition: PropTypes.func, showComingSoon: PropTypes.bool, + userOwnsProject: PropTypes.bool, username: PropTypes.string, vm: PropTypes.instanceOf(VM).isRequired }; diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index e0e3096a15..9ddd0d9079 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -423,6 +423,7 @@ class Stage extends React.Component { Stage.propTypes = { isColorPicking: PropTypes.bool, isFullScreen: PropTypes.bool.isRequired, + isStarted: PropTypes.bool, micIndicator: PropTypes.bool, onActivateColorPicker: PropTypes.func, onDeactivateColorPicker: PropTypes.func, diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index bac53a5140..746936cb5d 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -60,9 +60,9 @@ const vmListenerHOC = function (WrappedComponent) { this.props.vm.postIOData('userData', {username: this.props.username}); } - // Re-request a targets update when the shouldEmitTargetsUpdate state changes to true + // Re-request a targets update when the shouldEmitUpdate state changes to true // i.e. when the editor transitions out of fullscreen/player only modes - if (this.props.shouldEmitTargetsUpdate && !prevProps.shouldEmitTargetsUpdate) { + if (this.props.shouldEmitUpdates && !prevProps.shouldEmitUpdates) { this.props.vm.emitTargetsUpdate(false /* Emit the event, but do not trigger project change */); } } @@ -148,8 +148,10 @@ const vmListenerHOC = function (WrappedComponent) { onKeyUp: PropTypes.func, onMicListeningUpdate: PropTypes.func.isRequired, onMonitorsUpdate: PropTypes.func.isRequired, + onProjectChanged: PropTypes.func.isRequired, onProjectRunStart: PropTypes.func.isRequired, onProjectRunStop: PropTypes.func.isRequired, + onProjectSaved: PropTypes.func.isRequired, onRuntimeStarted: PropTypes.func.isRequired, onShowExtensionAlert: PropTypes.func.isRequired, onTargetsUpdate: PropTypes.func.isRequired, From 369906a7bb341638a914a8d925094d789481010b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 7 May 2019 08:41:22 -0400 Subject: [PATCH 1106/1971] Merge pull request #4798 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190506184649 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 79513dec54..873c8471b8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190419202157", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190419183947", - "scratch-vm": "0.2.0-prerelease.20190430163103", + "scratch-vm": "0.2.0-prerelease.20190506184649", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 1c995f4bba83e8960b3b0f859baac2327e755168 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 8 May 2019 14:27:43 -0400 Subject: [PATCH 1107/1971] Merge pull request #4802 from LLK/greenkeeper/scratch-l10n-3.2.20190508143856 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 873c8471b8..875d143d60 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1556115881", - "scratch-l10n": "3.2.20190501144004", + "scratch-l10n": "3.2.20190508143856", "scratch-paint": "0.2.0-prerelease.20190430175441", "scratch-render": "0.1.0-prerelease.20190419202157", "scratch-storage": "1.2.2", From 021ed1f103349a7e8b865ded06b0201ac7058222 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 15 May 2019 08:05:33 -0400 Subject: [PATCH 1108/1971] Merge pull request #4816 from chrisgarrity/add-rap-uz Update dependencies for Rapa Nui and Uzbek --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 875d143d60..0dcbfc346b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,8 +105,8 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1556115881", - "scratch-l10n": "3.2.20190508143856", + "scratch-blocks": "0.1.0-prerelease.1557852594", + "scratch-l10n": "3.3.20190513203303", "scratch-paint": "0.2.0-prerelease.20190430175441", "scratch-render": "0.1.0-prerelease.20190419202157", "scratch-storage": "1.2.2", From e0a1c5f25dd927de72b22d7e41ca4ec92f275408 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Wed, 15 May 2019 13:16:09 -0400 Subject: [PATCH 1109/1971] Merge pull request #4821 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190515153227 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019051… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0dcbfc346b..f9b781c260 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190419202157", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190419183947", - "scratch-vm": "0.2.0-prerelease.20190506184649", + "scratch-vm": "0.2.0-prerelease.20190515153227", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 0dd26dee41a06da212f645ed610b1189ffbc0358 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Wed, 15 May 2019 15:33:14 -0400 Subject: [PATCH 1110/1971] Merge pull request #4819 from LLK/greenkeeper/scratch-l10n-3.3.20190515143901 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f9b781c260..9a5f2ae1d5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1557852594", - "scratch-l10n": "3.3.20190513203303", + "scratch-l10n": "3.3.20190515143901", "scratch-paint": "0.2.0-prerelease.20190430175441", "scratch-render": "0.1.0-prerelease.20190419202157", "scratch-storage": "1.2.2", From 539f114af0b81ef141332a54fcc50ac246f9de69 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 16 May 2019 10:56:28 -0400 Subject: [PATCH 1111/1971] Merge pull request #4824 from LLK/greenkeeper/scratch-l10n-3.3.20190516144548 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9a5f2ae1d5..f0a4685566 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1557852594", - "scratch-l10n": "3.3.20190515143901", + "scratch-l10n": "3.3.20190516144548", "scratch-paint": "0.2.0-prerelease.20190430175441", "scratch-render": "0.1.0-prerelease.20190419202157", "scratch-storage": "1.2.2", From 06f56eff263258fb8218c1cfee6658de3483b91b Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 16 May 2019 13:14:51 -0400 Subject: [PATCH 1112/1971] Merge pull request #4825 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190516171147 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f0a4685566..d3f24721f0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1557852594", "scratch-l10n": "3.3.20190516144548", - "scratch-paint": "0.2.0-prerelease.20190430175441", + "scratch-paint": "0.2.0-prerelease.20190516171147", "scratch-render": "0.1.0-prerelease.20190419202157", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190419183947", From dbf7ef642d608babd48c87f45e69bea00022a52d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 22 May 2019 13:55:03 -0400 Subject: [PATCH 1113/1971] Merge pull request #4830 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190520172914 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d3f24721f0..b9d8337434 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190419202157", "scratch-storage": "1.2.2", "scratch-svg-renderer": "0.2.0-prerelease.20190419183947", - "scratch-vm": "0.2.0-prerelease.20190515153227", + "scratch-vm": "0.2.0-prerelease.20190520172914", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 4c54d14d4c1e03df0be42ae5ae1850add1ceee4e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 22 May 2019 14:05:17 -0400 Subject: [PATCH 1114/1971] Merge pull request #4834 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20190521170426 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b9d8337434..0613109989 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-paint": "0.2.0-prerelease.20190516171147", "scratch-render": "0.1.0-prerelease.20190419202157", "scratch-storage": "1.2.2", - "scratch-svg-renderer": "0.2.0-prerelease.20190419183947", + "scratch-svg-renderer": "0.2.0-prerelease.20190521170426", "scratch-vm": "0.2.0-prerelease.20190520172914", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 8fa7bd508816dba7d7e912eac84d785571f93253 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Wed, 22 May 2019 14:30:36 -0400 Subject: [PATCH 1115/1971] Merge pull request #4822 from LLK/greenkeeper/react-intl-2.9.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-intl to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0613109989..cbc4dab30c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -90,7 +90,7 @@ "react-dom": "16.2.0", "react-draggable": "3.0.5", "react-ga": "2.5.3", - "react-intl": "2.8.0", + "react-intl": "2.9.0", "react-modal": "3.6.1", "react-popover": "0.5.10", "react-redux": "5.0.7", From 9d54dab4b769cea7938281a481532e8374f09928 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Wed, 22 May 2019 14:41:02 -0400 Subject: [PATCH 1116/1971] Merge pull request #4837 from LLK/greenkeeper/scratch-l10n-3.3.20190522144128 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cbc4dab30c..6c8505cc78 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1557852594", - "scratch-l10n": "3.3.20190516144548", + "scratch-l10n": "3.3.20190522144128", "scratch-paint": "0.2.0-prerelease.20190516171147", "scratch-render": "0.1.0-prerelease.20190419202157", "scratch-storage": "1.2.2", From 2eef67f665ac77fddfa2f9323ca3833e803fea37 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Wed, 22 May 2019 14:42:44 -0400 Subject: [PATCH 1117/1971] Merge pull request #4833 from LLK/greenkeeper/scratch-storage-1.3.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-storage to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6c8505cc78..e3611250f3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-l10n": "3.3.20190522144128", "scratch-paint": "0.2.0-prerelease.20190516171147", "scratch-render": "0.1.0-prerelease.20190419202157", - "scratch-storage": "1.2.2", + "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190521170426", "scratch-vm": "0.2.0-prerelease.20190520172914", "selenium-webdriver": "3.6.0", From f3621dea411e2774e67beda44f37e2195996f38f Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 22 May 2019 17:39:51 -0400 Subject: [PATCH 1118/1971] Merge pull request #4843 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190522213558 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e3611250f3..9150441aed 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "scratch-blocks": "0.1.0-prerelease.1557852594", "scratch-l10n": "3.3.20190522144128", "scratch-paint": "0.2.0-prerelease.20190516171147", - "scratch-render": "0.1.0-prerelease.20190419202157", + "scratch-render": "0.1.0-prerelease.20190522213558", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190521170426", "scratch-vm": "0.2.0-prerelease.20190520172914", From 065c2606c2fdd994a80d211e544fa4017bcdfd63 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 22 May 2019 17:40:57 -0400 Subject: [PATCH 1119/1971] Merge pull request #4842 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190522213620 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9150441aed..c2c542b9c8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1557852594", "scratch-l10n": "3.3.20190522144128", - "scratch-paint": "0.2.0-prerelease.20190516171147", + "scratch-paint": "0.2.0-prerelease.20190522213620", "scratch-render": "0.1.0-prerelease.20190522213558", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190521170426", From 251d1b97a6cf592c8b121fc979d6f70d1653d0b4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 23 May 2019 14:14:09 -0400 Subject: [PATCH 1120/1971] Merge pull request #4677 from mzgoddard/update-toolbox render only new toolbox xmls --- .../scratch-gui/src/containers/blocks.jsx | 39 ++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index d79c7e9c72..0b95ebc892 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -30,6 +30,10 @@ import { SOUNDS_TAB_INDEX } from '../reducers/editor-tab'; +const UNINITIALIZED_TOOLBOX_XML = ` + +`; + const addFunctionListener = (object, property, callback) => { const oldFn = object[property]; object[property] = function () { @@ -83,16 +87,19 @@ class Blocks extends React.Component { }; this.onTargetsUpdate = debounce(this.onTargetsUpdate, 100); this.toolboxUpdateQueue = []; + this.initializedWorkspace = false; } componentDidMount () { this.ScratchBlocks.FieldColourSlider.activateEyedropper_ = this.props.onActivateColorPicker; this.ScratchBlocks.Procedures.externalProcedureDefCallback = this.props.onActivateCustomProcedures; this.ScratchBlocks.ScratchMsgs.setLocale(this.props.locale); + const toolboxXML = UNINITIALIZED_TOOLBOX_XML; + const workspaceConfig = defaultsDeep({}, Blocks.defaultOptions, this.props.options, - {rtl: this.props.isRtl, toolbox: this.props.toolboxXML} + {rtl: this.props.isRtl, toolbox: toolboxXML} ); this.workspace = this.ScratchBlocks.inject(this.blocks, workspaceConfig); @@ -114,7 +121,7 @@ class Blocks extends React.Component { // Store the xml of the toolbox that is actually rendered. // This is used in componentDidUpdate instead of prevProps, because // the xml can change while e.g. on the costumes tab. - this._renderedToolboxXML = this.props.toolboxXML; + this._renderedToolboxXML = toolboxXML; // we actually never want the workspace to enable "refresh toolbox" - this basically re-renders the // entire toolbox every time we reset the workspace. We call updateToolbox as a part of @@ -188,13 +195,19 @@ class Blocks extends React.Component { componentWillUnmount () { this.detachVM(); this.workspace.dispose(); - clearTimeout(this.toolboxUpdateTimeout); + if (this.toolboxUpdateTimeout) this.toolboxUpdateTimeout.cancel(); } requestToolboxUpdate () { - clearTimeout(this.toolboxUpdateTimeout); - this.toolboxUpdateTimeout = setTimeout(() => { - this.updateToolbox(); - }, 0); + if (this.toolboxUpdateTimeout) this.toolboxUpdateTimeout.cancel(); + let running = true; + this.toolboxUpdateTimeout = Promise.resolve().then(() => { + if (running) { + this.updateToolbox(); + } + }); + this.toolboxUpdateTimeout.cancel = () => { + running = false; + }; } setLocale () { this.ScratchBlocks.ScratchMsgs.setLocale(this.props.locale); @@ -214,8 +227,15 @@ class Blocks extends React.Component { const categoryId = this.workspace.toolbox_.getSelectedCategoryId(); const offset = this.workspace.toolbox_.getCategoryScrollOffset(); - this.workspace.updateToolbox(this.props.toolboxXML); - this._renderedToolboxXML = this.props.toolboxXML; + + let toolboxXML = this.props.toolboxXML; + if (!this.initializedWorkspace) { + toolboxXML = UNINITIALIZED_TOOLBOX_XML; + } + if (this._renderedToolboxXML !== toolboxXML) { + this.workspace.updateToolbox(toolboxXML); + this._renderedToolboxXML = toolboxXML; + } // In order to catch any changes that mutate the toolbox during "normal runtime" // (variable changes/etc), re-enable toolbox refresh. @@ -351,6 +371,7 @@ class Blocks extends React.Component { // When we change sprites, update the toolbox to have the new sprite's blocks const toolboxXML = this.getToolboxXML(); if (toolboxXML) { + this.initializedWorkspace = true; this.props.updateToolboxState(toolboxXML); } From b5094127a8f5f8fbaa9a7a79797176f22906b79f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 23 May 2019 15:33:33 -0400 Subject: [PATCH 1121/1971] Merge pull request #4847 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190523183140 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c2c542b9c8..28ba1ecc3c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1557852594", "scratch-l10n": "3.3.20190522144128", - "scratch-paint": "0.2.0-prerelease.20190522213620", + "scratch-paint": "0.2.0-prerelease.20190523183140", "scratch-render": "0.1.0-prerelease.20190522213558", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190521170426", From 54e9209cd5a6ab147435c58623b0fea63668040e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 24 May 2019 09:26:33 -0400 Subject: [PATCH 1122/1971] Merge pull request #4848 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20190523193400 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 28ba1ecc3c..9b019c47d2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-paint": "0.2.0-prerelease.20190523183140", "scratch-render": "0.1.0-prerelease.20190522213558", "scratch-storage": "1.3.2", - "scratch-svg-renderer": "0.2.0-prerelease.20190521170426", + "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", "scratch-vm": "0.2.0-prerelease.20190520172914", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 0d1e908f5debe72d23232741bda7adf6853b056b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 24 May 2019 09:26:53 -0400 Subject: [PATCH 1123/1971] Merge pull request #4850 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190524125950 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9b019c47d2..d01d1d34d8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "scratch-blocks": "0.1.0-prerelease.1557852594", "scratch-l10n": "3.3.20190522144128", "scratch-paint": "0.2.0-prerelease.20190523183140", - "scratch-render": "0.1.0-prerelease.20190522213558", + "scratch-render": "0.1.0-prerelease.20190524125950", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", "scratch-vm": "0.2.0-prerelease.20190520172914", From c9bdd9af6f8d6f87370be89704194fc610ec6cf2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 24 May 2019 13:38:31 -0400 Subject: [PATCH 1124/1971] Merge pull request #4851 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190524133325 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d01d1d34d8..8a26fe3540 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-blocks": "0.1.0-prerelease.1557852594", "scratch-l10n": "3.3.20190522144128", - "scratch-paint": "0.2.0-prerelease.20190523183140", + "scratch-paint": "0.2.0-prerelease.20190524133325", "scratch-render": "0.1.0-prerelease.20190524125950", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", From 740af7e26755deb19e68483bbba4f0a8d8601e1a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 29 May 2019 08:33:43 -0400 Subject: [PATCH 1125/1971] Merge pull request #4866 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190528231524 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-render to version 0.1.0-prerelease.201… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8a26fe3540..98925f8735 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "scratch-blocks": "0.1.0-prerelease.1557852594", "scratch-l10n": "3.3.20190522144128", "scratch-paint": "0.2.0-prerelease.20190524133325", - "scratch-render": "0.1.0-prerelease.20190524125950", + "scratch-render": "0.1.0-prerelease.20190528231524", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", "scratch-vm": "0.2.0-prerelease.20190520172914", From 8cf2a632155250d20ea9f13092412460f11c38bf Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 29 May 2019 08:34:07 -0400 Subject: [PATCH 1126/1971] Merge pull request #4852 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1558719162 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 98925f8735..b2db522ac0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1557852594", + "scratch-blocks": "0.1.0-prerelease.1558719162", "scratch-l10n": "3.3.20190522144128", "scratch-paint": "0.2.0-prerelease.20190524133325", "scratch-render": "0.1.0-prerelease.20190528231524", From f22352114a932ca53be156c47e3b6ad74bef9931 Mon Sep 17 00:00:00 2001 From: Colby Gutierrez-Kraybill Date: Wed, 29 May 2019 11:38:53 -0400 Subject: [PATCH 1127/1971] Merge pull request #4867 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1559136797 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b2db522ac0..6a13dfbe94 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-blocks": "0.1.0-prerelease.1558719162", + "scratch-blocks": "0.1.0-prerelease.1559136797", "scratch-l10n": "3.3.20190522144128", "scratch-paint": "0.2.0-prerelease.20190524133325", "scratch-render": "0.1.0-prerelease.20190528231524", From a14ed3a0c8a3b79854d9913dbcc75b416f5e6f54 Mon Sep 17 00:00:00 2001 From: Colby Gutierrez-Kraybill Date: Wed, 29 May 2019 13:23:10 -0400 Subject: [PATCH 1128/1971] Merge pull request #4868 from LLK/greenkeeper/scratch-l10n-3.3.20190529144051 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6a13dfbe94..3f4eb56aa5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,8 +105,8 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", + "scratch-l10n": "3.3.20190529144051", "scratch-blocks": "0.1.0-prerelease.1559136797", - "scratch-l10n": "3.3.20190522144128", "scratch-paint": "0.2.0-prerelease.20190524133325", "scratch-render": "0.1.0-prerelease.20190528231524", "scratch-storage": "1.3.2", From f12e54616e404667b70cf93b72e56ea65e6769a7 Mon Sep 17 00:00:00 2001 From: Colby Gutierrez-Kraybill Date: Wed, 29 May 2019 15:46:39 -0400 Subject: [PATCH 1129/1971] Merge pull request #4870 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190524155459 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019052… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3f4eb56aa5..20fd04f954 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190528231524", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", - "scratch-vm": "0.2.0-prerelease.20190520172914", + "scratch-vm": "0.2.0-prerelease.20190524155459", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From d034fb4ec118f55206bed52c1acb06be9b7f62b8 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 30 May 2019 11:31:30 -0400 Subject: [PATCH 1130/1971] Merge pull request #4874 from LLK/revert-4677-update-toolbox Revert "render only new toolbox xmls" --- .../scratch-gui/src/containers/blocks.jsx | 39 +++++-------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 0b95ebc892..d79c7e9c72 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -30,10 +30,6 @@ import { SOUNDS_TAB_INDEX } from '../reducers/editor-tab'; -const UNINITIALIZED_TOOLBOX_XML = ` - -`; - const addFunctionListener = (object, property, callback) => { const oldFn = object[property]; object[property] = function () { @@ -87,19 +83,16 @@ class Blocks extends React.Component { }; this.onTargetsUpdate = debounce(this.onTargetsUpdate, 100); this.toolboxUpdateQueue = []; - this.initializedWorkspace = false; } componentDidMount () { this.ScratchBlocks.FieldColourSlider.activateEyedropper_ = this.props.onActivateColorPicker; this.ScratchBlocks.Procedures.externalProcedureDefCallback = this.props.onActivateCustomProcedures; this.ScratchBlocks.ScratchMsgs.setLocale(this.props.locale); - const toolboxXML = UNINITIALIZED_TOOLBOX_XML; - const workspaceConfig = defaultsDeep({}, Blocks.defaultOptions, this.props.options, - {rtl: this.props.isRtl, toolbox: toolboxXML} + {rtl: this.props.isRtl, toolbox: this.props.toolboxXML} ); this.workspace = this.ScratchBlocks.inject(this.blocks, workspaceConfig); @@ -121,7 +114,7 @@ class Blocks extends React.Component { // Store the xml of the toolbox that is actually rendered. // This is used in componentDidUpdate instead of prevProps, because // the xml can change while e.g. on the costumes tab. - this._renderedToolboxXML = toolboxXML; + this._renderedToolboxXML = this.props.toolboxXML; // we actually never want the workspace to enable "refresh toolbox" - this basically re-renders the // entire toolbox every time we reset the workspace. We call updateToolbox as a part of @@ -195,19 +188,13 @@ class Blocks extends React.Component { componentWillUnmount () { this.detachVM(); this.workspace.dispose(); - if (this.toolboxUpdateTimeout) this.toolboxUpdateTimeout.cancel(); + clearTimeout(this.toolboxUpdateTimeout); } requestToolboxUpdate () { - if (this.toolboxUpdateTimeout) this.toolboxUpdateTimeout.cancel(); - let running = true; - this.toolboxUpdateTimeout = Promise.resolve().then(() => { - if (running) { - this.updateToolbox(); - } - }); - this.toolboxUpdateTimeout.cancel = () => { - running = false; - }; + clearTimeout(this.toolboxUpdateTimeout); + this.toolboxUpdateTimeout = setTimeout(() => { + this.updateToolbox(); + }, 0); } setLocale () { this.ScratchBlocks.ScratchMsgs.setLocale(this.props.locale); @@ -227,15 +214,8 @@ class Blocks extends React.Component { const categoryId = this.workspace.toolbox_.getSelectedCategoryId(); const offset = this.workspace.toolbox_.getCategoryScrollOffset(); - - let toolboxXML = this.props.toolboxXML; - if (!this.initializedWorkspace) { - toolboxXML = UNINITIALIZED_TOOLBOX_XML; - } - if (this._renderedToolboxXML !== toolboxXML) { - this.workspace.updateToolbox(toolboxXML); - this._renderedToolboxXML = toolboxXML; - } + this.workspace.updateToolbox(this.props.toolboxXML); + this._renderedToolboxXML = this.props.toolboxXML; // In order to catch any changes that mutate the toolbox during "normal runtime" // (variable changes/etc), re-enable toolbox refresh. @@ -371,7 +351,6 @@ class Blocks extends React.Component { // When we change sprites, update the toolbox to have the new sprite's blocks const toolboxXML = this.getToolboxXML(); if (toolboxXML) { - this.initializedWorkspace = true; this.props.updateToolboxState(toolboxXML); } From ff1f7636cb210c280de5ce906bdbe29e2a83f59d Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Thu, 30 May 2019 12:23:54 -0400 Subject: [PATCH 1131/1971] Merge pull request #4878 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190530160621 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 20fd04f954..d7cba2f6b2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "scratch-l10n": "3.3.20190529144051", "scratch-blocks": "0.1.0-prerelease.1559136797", "scratch-paint": "0.2.0-prerelease.20190524133325", - "scratch-render": "0.1.0-prerelease.20190528231524", + "scratch-render": "0.1.0-prerelease.20190530160621", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", "scratch-vm": "0.2.0-prerelease.20190524155459", From c407e5aa43ab28ddc2ab030e7bf420d543e06935 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 4 Jun 2019 14:10:38 -0400 Subject: [PATCH 1132/1971] Merge pull request #4896 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190604153446 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(package): update scratch-vm to version 0.2.0-prerelease.2019060… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d7cba2f6b2..7d4ad233bc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190530160621", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", - "scratch-vm": "0.2.0-prerelease.20190524155459", + "scratch-vm": "0.2.0-prerelease.20190604153446", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 2c28503c181554145fc2b3058351ef177670b391 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 5 Jun 2019 11:51:25 -0400 Subject: [PATCH 1133/1971] Merge pull request #4900 from LLK/kchadha-update-deps Update scratch-vm and scratch-l10n. --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7d4ad233bc..10090358a4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,13 +105,13 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-l10n": "3.3.20190529144051", + "scratch-l10n": "3.3.20190605143948", "scratch-blocks": "0.1.0-prerelease.1559136797", "scratch-paint": "0.2.0-prerelease.20190524133325", "scratch-render": "0.1.0-prerelease.20190530160621", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", - "scratch-vm": "0.2.0-prerelease.20190604153446", + "scratch-vm": "0.2.0-prerelease.20190604212312", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From e26ac0d943fc0cd12d6da7359b4c0b05a5240b71 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 5 Jun 2019 14:51:29 -0400 Subject: [PATCH 1134/1971] Merge pull request #4904 from LLK/kchadha-update-render Update scratch-render --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 10090358a4..d862ca882c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "scratch-l10n": "3.3.20190605143948", "scratch-blocks": "0.1.0-prerelease.1559136797", "scratch-paint": "0.2.0-prerelease.20190524133325", - "scratch-render": "0.1.0-prerelease.20190530160621", + "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", "scratch-vm": "0.2.0-prerelease.20190604212312", From bb1c28a61cd65c0fc3b077a05e7d0c1f58fd52e4 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Mon, 10 Jun 2019 13:22:50 -0400 Subject: [PATCH 1135/1971] Merge pull request #4913 from LLK/picklesrus-patch-1 Update scratch-vm --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d862ca882c..34e886cc1f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", - "scratch-vm": "0.2.0-prerelease.20190604212312", + "scratch-vm": "0.2.0-prerelease.20190610152034", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From d3260ae0625f7767b1a5f9b9a7be8b0b3a77210f Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 12 Jun 2019 12:05:32 -0400 Subject: [PATCH 1136/1971] Merge pull request #4917 from chrisgarrity/update-l10n Update l10n to the latest version --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 34e886cc1f..e605afbfb1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-l10n": "3.3.20190605143948", + "scratch-l10n": "3.3.20190612144059", "scratch-blocks": "0.1.0-prerelease.1559136797", "scratch-paint": "0.2.0-prerelease.20190524133325", "scratch-render": "0.1.0-prerelease.20190605151415", From 742ad6f0eac81473a4b1ebab21fca71b66d64915 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Tue, 18 Jun 2019 16:22:25 -0700 Subject: [PATCH 1137/1971] Merge pull request #4790 from LLK/e16n Supporting GUI changes for extensionification --- packages/scratch-gui/package.json | 2 +- .../scratch-gui/src/containers/blocks.jsx | 45 ++++++++++++++++--- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e605afbfb1..9e7a0cfc78 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", - "scratch-vm": "0.2.0-prerelease.20190610152034", + "scratch-vm": "0.2.0-prerelease.20190618225856", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index d79c7e9c72..fb01ce15d5 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -17,6 +17,7 @@ import errorBoundaryHOC from '../lib/error-boundary-hoc.jsx'; import {STAGE_DISPLAY_SIZES} from '../lib/layout-constants'; import DropAreaHOC from '../lib/drop-area-hoc.jsx'; import DragConstants from '../lib/drag-constants'; +import defineDynamicBlock from '../lib/define-dynamic-block'; import {connect} from 'react-redux'; import {updateToolbox} from '../reducers/toolbox'; @@ -393,20 +394,50 @@ class Blocks extends React.Component { // workspace to be 'undone' here. this.workspace.clearUndo(); } - handleExtensionAdded (blocksInfo) { - // select JSON from each block info object then reject the pseudo-blocks which don't have JSON, like separators - // this actually defines blocks and MUST run regardless of the UI state - this.ScratchBlocks.defineBlocksWithJsonArray(blocksInfo.map(blockInfo => blockInfo.json).filter(x => x)); + handleExtensionAdded (categoryInfo) { + const defineBlocks = blockInfoArray => { + if (blockInfoArray && blockInfoArray.length > 0) { + const staticBlocksJson = []; + const dynamicBlocksInfo = []; + blockInfoArray.forEach(blockInfo => { + if (blockInfo.info && blockInfo.info.isDynamic) { + dynamicBlocksInfo.push(blockInfo); + } else if (blockInfo.json) { + staticBlocksJson.push(blockInfo.json); + } + // otherwise it's a non-block entry such as '---' + }); + + this.ScratchBlocks.defineBlocksWithJsonArray(staticBlocksJson); + dynamicBlocksInfo.forEach(blockInfo => { + // This is creating the block factory / constructor -- NOT a specific instance of the block. + // The factory should only know static info about the block: the category info and the opcode. + // Anything else will be picked up from the XML attached to the block instance. + const extendedOpcode = `${categoryInfo.id}_${blockInfo.info.opcode}`; + const blockDefinition = + defineDynamicBlock(this.ScratchBlocks, categoryInfo, blockInfo, extendedOpcode); + this.ScratchBlocks.Blocks[extendedOpcode] = blockDefinition; + }); + } + }; + + // scratch-blocks implements a menu or custom field as a special kind of block ("shadow" block) + // these actually define blocks and MUST run regardless of the UI state + defineBlocks( + Object.getOwnPropertyNames(categoryInfo.customFieldTypes) + .map(fieldTypeName => categoryInfo.customFieldTypes[fieldTypeName].scratchBlocksDefinition)); + defineBlocks(categoryInfo.menus); + defineBlocks(categoryInfo.blocks); - // Update the toolbox with new blocks + // Update the toolbox with new blocks if possible const toolboxXML = this.getToolboxXML(); if (toolboxXML) { this.props.updateToolboxState(toolboxXML); } } - handleBlocksInfoUpdate (blocksInfo) { + handleBlocksInfoUpdate (categoryInfo) { // @todo Later we should replace this to avoid all the warnings from redefining blocks. - this.handleExtensionAdded(blocksInfo); + this.handleExtensionAdded(categoryInfo); } handleCategorySelected (categoryId) { const extension = extensionData.find(ext => ext.extensionId === categoryId); From 7ea59aa3e7d5f5120a81fb4a61fdcf1ff5d97824 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Tue, 18 Jun 2019 21:27:42 -0700 Subject: [PATCH 1138/1971] update VM for non-droppable extension menu support --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9e7a0cfc78..4ccd4e2062 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", - "scratch-vm": "0.2.0-prerelease.20190618225856", + "scratch-vm": "0.2.0-prerelease.20190619042313", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 763c8c0216af8a7c5e8a0f6a96d162f0c5371ddf Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 19 Jun 2019 15:04:01 -0400 Subject: [PATCH 1139/1971] Merge pull request #4936 from LLK/ericrosenbaum-patch-1 Update l10n --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4ccd4e2062..b0564c305d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-l10n": "3.3.20190612144059", + "scratch-l10n": "3.3.20190619145905", "scratch-blocks": "0.1.0-prerelease.1559136797", "scratch-paint": "0.2.0-prerelease.20190524133325", "scratch-render": "0.1.0-prerelease.20190605151415", From c5f05e45b90336f4634e6980d5fef6c2e0faafad Mon Sep 17 00:00:00 2001 From: picklesrus Date: Thu, 20 Jun 2019 11:40:30 -0400 Subject: [PATCH 1140/1971] Merge pull request #4940 from picklesrus/revert-4790 Revert "Merge pull request #4790 from LLK/e16n" --- packages/scratch-gui/package.json | 2 +- .../scratch-gui/src/containers/blocks.jsx | 45 +++---------------- 2 files changed, 8 insertions(+), 39 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b0564c305d..905e371b9d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", - "scratch-vm": "0.2.0-prerelease.20190619042313", + "scratch-vm": "0.2.0-prerelease.20190610152034", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index fb01ce15d5..d79c7e9c72 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -17,7 +17,6 @@ import errorBoundaryHOC from '../lib/error-boundary-hoc.jsx'; import {STAGE_DISPLAY_SIZES} from '../lib/layout-constants'; import DropAreaHOC from '../lib/drop-area-hoc.jsx'; import DragConstants from '../lib/drag-constants'; -import defineDynamicBlock from '../lib/define-dynamic-block'; import {connect} from 'react-redux'; import {updateToolbox} from '../reducers/toolbox'; @@ -394,50 +393,20 @@ class Blocks extends React.Component { // workspace to be 'undone' here. this.workspace.clearUndo(); } - handleExtensionAdded (categoryInfo) { - const defineBlocks = blockInfoArray => { - if (blockInfoArray && blockInfoArray.length > 0) { - const staticBlocksJson = []; - const dynamicBlocksInfo = []; - blockInfoArray.forEach(blockInfo => { - if (blockInfo.info && blockInfo.info.isDynamic) { - dynamicBlocksInfo.push(blockInfo); - } else if (blockInfo.json) { - staticBlocksJson.push(blockInfo.json); - } - // otherwise it's a non-block entry such as '---' - }); - - this.ScratchBlocks.defineBlocksWithJsonArray(staticBlocksJson); - dynamicBlocksInfo.forEach(blockInfo => { - // This is creating the block factory / constructor -- NOT a specific instance of the block. - // The factory should only know static info about the block: the category info and the opcode. - // Anything else will be picked up from the XML attached to the block instance. - const extendedOpcode = `${categoryInfo.id}_${blockInfo.info.opcode}`; - const blockDefinition = - defineDynamicBlock(this.ScratchBlocks, categoryInfo, blockInfo, extendedOpcode); - this.ScratchBlocks.Blocks[extendedOpcode] = blockDefinition; - }); - } - }; - - // scratch-blocks implements a menu or custom field as a special kind of block ("shadow" block) - // these actually define blocks and MUST run regardless of the UI state - defineBlocks( - Object.getOwnPropertyNames(categoryInfo.customFieldTypes) - .map(fieldTypeName => categoryInfo.customFieldTypes[fieldTypeName].scratchBlocksDefinition)); - defineBlocks(categoryInfo.menus); - defineBlocks(categoryInfo.blocks); + handleExtensionAdded (blocksInfo) { + // select JSON from each block info object then reject the pseudo-blocks which don't have JSON, like separators + // this actually defines blocks and MUST run regardless of the UI state + this.ScratchBlocks.defineBlocksWithJsonArray(blocksInfo.map(blockInfo => blockInfo.json).filter(x => x)); - // Update the toolbox with new blocks if possible + // Update the toolbox with new blocks const toolboxXML = this.getToolboxXML(); if (toolboxXML) { this.props.updateToolboxState(toolboxXML); } } - handleBlocksInfoUpdate (categoryInfo) { + handleBlocksInfoUpdate (blocksInfo) { // @todo Later we should replace this to avoid all the warnings from redefining blocks. - this.handleExtensionAdded(categoryInfo); + this.handleExtensionAdded(blocksInfo); } handleCategorySelected (categoryId) { const extension = extensionData.find(ext => ext.extensionId === categoryId); From 818f47cdd7d6f306c546fb6e1d2815b5675fe665 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Thu, 20 Jun 2019 17:53:23 -0400 Subject: [PATCH 1141/1971] Merge pull request #4942 from LLK/revert-4940-revert-4790 Revert "Revert "Merge pull request #4790 from LLK/e16n"" --- packages/scratch-gui/package.json | 2 +- .../scratch-gui/src/containers/blocks.jsx | 45 ++++++++++++++++--- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 905e371b9d..b0564c305d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", - "scratch-vm": "0.2.0-prerelease.20190610152034", + "scratch-vm": "0.2.0-prerelease.20190619042313", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index d79c7e9c72..fb01ce15d5 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -17,6 +17,7 @@ import errorBoundaryHOC from '../lib/error-boundary-hoc.jsx'; import {STAGE_DISPLAY_SIZES} from '../lib/layout-constants'; import DropAreaHOC from '../lib/drop-area-hoc.jsx'; import DragConstants from '../lib/drag-constants'; +import defineDynamicBlock from '../lib/define-dynamic-block'; import {connect} from 'react-redux'; import {updateToolbox} from '../reducers/toolbox'; @@ -393,20 +394,50 @@ class Blocks extends React.Component { // workspace to be 'undone' here. this.workspace.clearUndo(); } - handleExtensionAdded (blocksInfo) { - // select JSON from each block info object then reject the pseudo-blocks which don't have JSON, like separators - // this actually defines blocks and MUST run regardless of the UI state - this.ScratchBlocks.defineBlocksWithJsonArray(blocksInfo.map(blockInfo => blockInfo.json).filter(x => x)); + handleExtensionAdded (categoryInfo) { + const defineBlocks = blockInfoArray => { + if (blockInfoArray && blockInfoArray.length > 0) { + const staticBlocksJson = []; + const dynamicBlocksInfo = []; + blockInfoArray.forEach(blockInfo => { + if (blockInfo.info && blockInfo.info.isDynamic) { + dynamicBlocksInfo.push(blockInfo); + } else if (blockInfo.json) { + staticBlocksJson.push(blockInfo.json); + } + // otherwise it's a non-block entry such as '---' + }); + + this.ScratchBlocks.defineBlocksWithJsonArray(staticBlocksJson); + dynamicBlocksInfo.forEach(blockInfo => { + // This is creating the block factory / constructor -- NOT a specific instance of the block. + // The factory should only know static info about the block: the category info and the opcode. + // Anything else will be picked up from the XML attached to the block instance. + const extendedOpcode = `${categoryInfo.id}_${blockInfo.info.opcode}`; + const blockDefinition = + defineDynamicBlock(this.ScratchBlocks, categoryInfo, blockInfo, extendedOpcode); + this.ScratchBlocks.Blocks[extendedOpcode] = blockDefinition; + }); + } + }; + + // scratch-blocks implements a menu or custom field as a special kind of block ("shadow" block) + // these actually define blocks and MUST run regardless of the UI state + defineBlocks( + Object.getOwnPropertyNames(categoryInfo.customFieldTypes) + .map(fieldTypeName => categoryInfo.customFieldTypes[fieldTypeName].scratchBlocksDefinition)); + defineBlocks(categoryInfo.menus); + defineBlocks(categoryInfo.blocks); - // Update the toolbox with new blocks + // Update the toolbox with new blocks if possible const toolboxXML = this.getToolboxXML(); if (toolboxXML) { this.props.updateToolboxState(toolboxXML); } } - handleBlocksInfoUpdate (blocksInfo) { + handleBlocksInfoUpdate (categoryInfo) { // @todo Later we should replace this to avoid all the warnings from redefining blocks. - this.handleExtensionAdded(blocksInfo); + this.handleExtensionAdded(categoryInfo); } handleCategorySelected (categoryId) { const extension = extensionData.find(ext => ext.extensionId === categoryId); From 74d814c6766287c2dd0b18b444d4bc23f501e2a6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 24 Jun 2019 10:30:36 -0400 Subject: [PATCH 1142/1971] Merge pull request #4947 from LLK/update-paint Bump scratch-paint version --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b0564c305d..291bb4cefb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-l10n": "3.3.20190619145905", "scratch-blocks": "0.1.0-prerelease.1559136797", - "scratch-paint": "0.2.0-prerelease.20190524133325", + "scratch-paint": "0.2.0-prerelease.20190624133806", "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", From 9f891ca56298b72ee1c108422e6f6a45e3262b9d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 26 Jun 2019 12:59:22 -0400 Subject: [PATCH 1143/1971] Merge pull request #4952 from LLK/paulkaplan-patch-1 Update blocks manually --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 291bb4cefb..7b8705c6ba 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-l10n": "3.3.20190619145905", - "scratch-blocks": "0.1.0-prerelease.1559136797", + "scratch-blocks": "0.1.0-prerelease.1561560820", "scratch-paint": "0.2.0-prerelease.20190624133806", "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", From 3d0f1f540c774719c4ba15c4635484b363b5f669 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 26 Jun 2019 13:59:33 -0400 Subject: [PATCH 1144/1971] Merge pull request #4953 from LLK/paulkaplan-patch-1 Bump l10n version --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7b8705c6ba..3a79a85296 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-l10n": "3.3.20190619145905", + "scratch-l10n": "3.3.20190626154950", "scratch-blocks": "0.1.0-prerelease.1561560820", "scratch-paint": "0.2.0-prerelease.20190624133806", "scratch-render": "0.1.0-prerelease.20190605151415", From e29bc7a7644e27843bb6d5bf1cb0ff14e3b4af81 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 10 Jul 2019 19:57:33 -0400 Subject: [PATCH 1145/1971] Merge pull request #4978 from fsih/updateScratchDeps Update deps --- packages/scratch-gui/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3a79a85296..51790ab453 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,13 +105,13 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-l10n": "3.3.20190626154950", - "scratch-blocks": "0.1.0-prerelease.1561560820", + "scratch-l10n": "3.3.20190703172039", + "scratch-blocks": "0.1.0-prerelease.1561987297", "scratch-paint": "0.2.0-prerelease.20190624133806", - "scratch-render": "0.1.0-prerelease.20190605151415", + "scratch-render": "0.1.0-prerelease.20190701174822", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", - "scratch-vm": "0.2.0-prerelease.20190619042313", + "scratch-vm": "0.2.0-prerelease.20190628161443", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From f7858932084d5de464e9d911f3711e0843ef7ff2 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 10 Jul 2019 20:31:15 -0400 Subject: [PATCH 1146/1971] Merge pull request #4979 from fsih/unupdateScratchDeps Revert to last good render --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 51790ab453..12275357fa 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "scratch-l10n": "3.3.20190703172039", "scratch-blocks": "0.1.0-prerelease.1561987297", "scratch-paint": "0.2.0-prerelease.20190624133806", - "scratch-render": "0.1.0-prerelease.20190701174822", + "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", "scratch-vm": "0.2.0-prerelease.20190628161443", From 28fbf9edf23789463d0d22695c234562b8a5b37a Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 10 Jul 2019 21:11:01 -0400 Subject: [PATCH 1147/1971] Merge pull request #4980 from fsih/updatel10n Update l10n --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 12275357fa..09b7f088dd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-l10n": "3.3.20190703172039", + "scratch-l10n": "3.3.20190711005050", "scratch-blocks": "0.1.0-prerelease.1561987297", "scratch-paint": "0.2.0-prerelease.20190624133806", "scratch-render": "0.1.0-prerelease.20190605151415", From 505745ee515db5291455707fc4705fa784eb5eb3 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 15 Jul 2019 12:27:21 -0400 Subject: [PATCH 1148/1971] Merge pull request #4986 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190715144356 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 09b7f088dd..ae666b95c2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", - "scratch-vm": "0.2.0-prerelease.20190628161443", + "scratch-vm": "0.2.0-prerelease.20190715144356", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From d257dc8b44b12bc6ffc685fa16e7636af9e8be86 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 15 Jul 2019 12:27:33 -0400 Subject: [PATCH 1149/1971] Merge pull request #4990 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190715155430 Greenkeeper/scratch paint 0.2.0 prerelease.20190715155430 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ae666b95c2..1d4460fd49 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-l10n": "3.3.20190711005050", "scratch-blocks": "0.1.0-prerelease.1561987297", - "scratch-paint": "0.2.0-prerelease.20190624133806", + "scratch-paint": "0.2.0-prerelease.20190715155430", "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", From c240c6f52d536beec771df8c36a9e63491583549 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 15 Jul 2019 15:01:32 -0400 Subject: [PATCH 1150/1971] Merge pull request #4991 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190715183326 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1d4460fd49..89ecf442fa 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-l10n": "3.3.20190711005050", "scratch-blocks": "0.1.0-prerelease.1561987297", - "scratch-paint": "0.2.0-prerelease.20190715155430", + "scratch-paint": "0.2.0-prerelease.20190715183326", "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", From f508eecb458b80044cd76f4fb27749058a8a0425 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 15 Jul 2019 15:39:38 -0400 Subject: [PATCH 1151/1971] Merge pull request #4989 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20190715153806 Greenkeeper/scratch svg renderer 0.2.0 prerelease.20190715153806 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 89ecf442fa..55c86ba482 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-paint": "0.2.0-prerelease.20190715183326", "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", - "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", + "scratch-svg-renderer": "0.2.0-prerelease.20190715153806", "scratch-vm": "0.2.0-prerelease.20190715144356", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 55ece04d2c1541d80c2ffb37b2a47c8ebf1655b9 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Tue, 16 Jul 2019 15:45:21 -0400 Subject: [PATCH 1152/1971] Merge pull request #4985 from LLK/greenkeeper/react-modal-3.9.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update react-modal to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 55c86ba482..6343c74065 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -91,7 +91,7 @@ "react-draggable": "3.0.5", "react-ga": "2.5.3", "react-intl": "2.9.0", - "react-modal": "3.6.1", + "react-modal": "3.9.1", "react-popover": "0.5.10", "react-redux": "5.0.7", "react-responsive": "5.0.0", From f977a85d6510e4c100a19390b4aa5c016f8b79b5 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Wed, 17 Jul 2019 16:32:09 -0400 Subject: [PATCH 1153/1971] Merge pull request #4997 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190717201758 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6343c74065..73eb180bc1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190715153806", - "scratch-vm": "0.2.0-prerelease.20190715144356", + "scratch-vm": "0.2.0-prerelease.20190717201758", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 100c4f43dcb8dc03cf847e2ec44ae66944d652e3 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Wed, 17 Jul 2019 16:32:44 -0400 Subject: [PATCH 1154/1971] Merge pull request #4996 from LLK/greenkeeper/scratch-l10n-3.3.20190717144934 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 73eb180bc1..42512950f9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-l10n": "3.3.20190711005050", + "scratch-l10n": "3.3.20190717144934", "scratch-blocks": "0.1.0-prerelease.1561987297", "scratch-paint": "0.2.0-prerelease.20190715183326", "scratch-render": "0.1.0-prerelease.20190605151415", From edec64daf4fa13fd117696e113588441e25c1919 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 18 Jul 2019 13:38:37 -0400 Subject: [PATCH 1155/1971] Merge pull request #5002 from fsih/revertSvgRend Revert to last good scratch-svg-renderer --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 42512950f9..dab3cb47ad 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-paint": "0.2.0-prerelease.20190715183326", "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", - "scratch-svg-renderer": "0.2.0-prerelease.20190715153806", + "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", "scratch-vm": "0.2.0-prerelease.20190717201758", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 21cdf1c37b734551f88cd8d1f33852e5ae119216 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 18 Jul 2019 14:11:55 -0400 Subject: [PATCH 1156/1971] Merge pull request #5003 from fsih/updatePaint Update paint to revert svg-renderer --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index dab3cb47ad..803e7ec378 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-l10n": "3.3.20190717144934", "scratch-blocks": "0.1.0-prerelease.1561987297", - "scratch-paint": "0.2.0-prerelease.20190715183326", + "scratch-paint": "0.2.0-prerelease.20190718175814", "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", From 7ad921459c9cd921e040ef1e48e4e3bce80ac8d2 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Tue, 23 Jul 2019 16:32:35 -0400 Subject: [PATCH 1157/1971] Merge pull request #4960 from chrisgarrity/customize-menubar Customize menubar --- .../src/components/menu-bar/menu-bar.jsx | 154 +++++++++--------- 1 file changed, 81 insertions(+), 73 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 6105a60e5c..1fa7a01cc1 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -154,7 +154,7 @@ class MenuBar extends React.Component { 'handleKeyPress', 'handleLanguageMouseUp', 'handleRestoreOption', - 'handleSaveToComputer', + 'getSaveToComputerHandler', 'restoreOptionMessage' ]); } @@ -225,7 +225,7 @@ class MenuBar extends React.Component { event.preventDefault(); } } - handleSaveToComputer (downloadProjectCallback) { + getSaveToComputerHandler (downloadProjectCallback) { return () => { this.props.onRequestCloseFile(); downloadProjectCallback(); @@ -327,11 +327,11 @@ class MenuBar extends React.Component { [styles.clickable]: typeof this.props.onClickLogo !== 'undefined' })} draggable={false} - src={scratchLogo} + src={this.props.logo} onClick={this.props.onClickLogo} />
-
@@ -345,82 +345,86 @@ class MenuBar extends React.Component { />
-
-
- - )} + {(this.props.canManageFiles) && ( +
- - - {newProjectMessage} - - - {(this.props.canSave || this.props.canCreateCopy || this.props.canRemix) && ( + + - {this.props.canSave ? ( - - {saveNowMessage} - - ) : []} - {this.props.canCreateCopy ? ( - - {createCopyMessage} - - ) : []} - {this.props.canRemix ? ( - - {remixMessage} - - ) : []} + + {newProjectMessage} + - )} - - - {(className, renderFileInput, loadProject) => ( + {(this.props.canSave || this.props.canCreateCopy || this.props.canRemix) && ( + + {this.props.canSave && ( + + {saveNowMessage} + + )} + {this.props.canCreateCopy && ( + + {createCopyMessage} + + )} + {this.props.canRemix && ( + + {remixMessage} + + )} + + )} + + + {(className, renderFileInput, handleLoadProject) => ( + + {/* eslint-disable max-len */} + {this.props.intl.formatMessage(sharedMessages.loadFromComputerTitle)} + {/* eslint-enable max-len */} + {renderFileInput()} + + )} + + {(className, downloadProjectCallback) => ( - {this.props.intl.formatMessage(sharedMessages.loadFromComputerTitle)} - {renderFileInput()} + - )} - - {(className, downloadProjectCallback) => ( - - - - )} - - -
+ )} + +
+
+ )}
{} }; From 5af0e2f584a8e8f99394396b20d51f5de7ec804f Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 24 Jul 2019 11:35:02 -0400 Subject: [PATCH 1158/1971] Merge pull request #5013 from LLK/hotfix/revert-vm-raise-params-pr Hotfix/revert vm raise params pr --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 803e7ec378..3a50e0a366 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", - "scratch-vm": "0.2.0-prerelease.20190717201758", + "scratch-vm": "0.2.0-prerelease.20190722173823", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 9e5788ac1a5228ac0b8cdb0c24ea12a9fdb98804 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 24 Jul 2019 12:42:02 -0400 Subject: [PATCH 1159/1971] Merge pull request #5022 from LLK/greenkeeper/scratch-l10n-3.5.20190724160319 Greenkeeper/scratch l10n 3.5.20190724160319 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3a50e0a366..3a05682ccc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-l10n": "3.3.20190717144934", + "scratch-l10n": "3.5.20190724160319", "scratch-blocks": "0.1.0-prerelease.1561987297", "scratch-paint": "0.2.0-prerelease.20190718175814", "scratch-render": "0.1.0-prerelease.20190605151415", From 54cd660652f26a5878e596b2620de5f9d9a027cb Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 24 Jul 2019 13:49:35 -0400 Subject: [PATCH 1160/1971] Merge pull request #5020 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190724154115 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3a05682ccc..3ffd22be29 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-l10n": "3.5.20190724160319", "scratch-blocks": "0.1.0-prerelease.1561987297", - "scratch-paint": "0.2.0-prerelease.20190718175814", + "scratch-paint": "0.2.0-prerelease.20190724154115", "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", From 74f4cf5245904ab1bbe7a0f81a2595a47cad535b Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 24 Jul 2019 13:53:46 -0400 Subject: [PATCH 1161/1971] Merge pull request #5023 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1563990552 Update scratch-blocks to version 0.1.0-prerelease.1563990552 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3ffd22be29..ec96889bc3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-l10n": "3.5.20190724160319", - "scratch-blocks": "0.1.0-prerelease.1561987297", + "scratch-blocks": "0.1.0-prerelease.1563990552", "scratch-paint": "0.2.0-prerelease.20190724154115", "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", From 279e2cc695038e047fb36a9085bc0e22746630d8 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 29 Jul 2019 12:22:13 -0400 Subject: [PATCH 1162/1971] Merge pull request #5036 from fsih/svgRendTest Roll forward scratch-svg-renderer --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ec96889bc3..8841cf721d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,10 +107,10 @@ "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-l10n": "3.5.20190724160319", "scratch-blocks": "0.1.0-prerelease.1563990552", - "scratch-paint": "0.2.0-prerelease.20190724154115", + "scratch-paint": "0.2.0-prerelease.20190729153946", "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", - "scratch-svg-renderer": "0.2.0-prerelease.20190523193400", + "scratch-svg-renderer": "0.2.0-prerelease.20190715153806", "scratch-vm": "0.2.0-prerelease.20190722173823", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 9e872c834ca418a37c768511f360101a67b58555 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 30 Jul 2019 15:27:04 -0400 Subject: [PATCH 1163/1971] Merge pull request #4974 from ericrosenbaum/feature/sound-editor-selection4 Sound editor update: selection-based editing --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8841cf721d..28df3b4d5b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -44,7 +44,7 @@ "babel-loader": "^8.0.4", "base64-loader": "1.0.0", "bowser": "1.9.4", - "chromedriver": "74.0.0", + "chromedriver": "75.1.0", "classnames": "2.2.6", "computed-style-to-inline-style": "3.0.0", "copy-webpack-plugin": "^4.5.1", From ee7d5ecbafba4f304166c0d72102b2462eee2340 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 31 Jul 2019 11:03:32 -0400 Subject: [PATCH 1164/1971] Merge pull request #5051 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1564581595 Greenkeeper/scratch blocks 0.1.0 prerelease.1564581595 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 28df3b4d5b..697ff79544 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-l10n": "3.5.20190724160319", - "scratch-blocks": "0.1.0-prerelease.1563990552", + "scratch-blocks": "0.1.0-prerelease.1564581595", "scratch-paint": "0.2.0-prerelease.20190729153946", "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", From 7c0c1512dbe39a7816609b4e6715647935c4c147 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 31 Jul 2019 11:05:23 -0400 Subject: [PATCH 1165/1971] Merge pull request #5045 from LLK/greenkeeper/chromedriver-76.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update chromedriver to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 697ff79544..ed0e07edc7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -44,7 +44,7 @@ "babel-loader": "^8.0.4", "base64-loader": "1.0.0", "bowser": "1.9.4", - "chromedriver": "75.1.0", + "chromedriver": "76.0.0", "classnames": "2.2.6", "computed-style-to-inline-style": "3.0.0", "copy-webpack-plugin": "^4.5.1", From 2eeabd15dc4468f96380fd0cb8acee3bcab4d611 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 31 Jul 2019 11:17:26 -0400 Subject: [PATCH 1166/1971] Merge pull request #5043 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190730131442 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ed0e07edc7..b28af077ec 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190715153806", - "scratch-vm": "0.2.0-prerelease.20190722173823", + "scratch-vm": "0.2.0-prerelease.20190730131442", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 82a300a088e13dc58fcd2ad8a90cd379b3809736 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 31 Jul 2019 11:40:41 -0400 Subject: [PATCH 1167/1971] Merge pull request #5053 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190731151656 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b28af077ec..2968f22f47 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190715153806", - "scratch-vm": "0.2.0-prerelease.20190730131442", + "scratch-vm": "0.2.0-prerelease.20190731151656", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 3109d9812c2ac39fe0124a3419c7967c0ae1bc8f Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 31 Jul 2019 15:25:41 -0400 Subject: [PATCH 1168/1971] Merge pull request #5054 from LLK/greenkeeper/scratch-l10n-3.5.20190731161310 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2968f22f47..ebe761b09b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -105,7 +105,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-l10n": "3.5.20190724160319", + "scratch-l10n": "3.5.20190731161310", "scratch-blocks": "0.1.0-prerelease.1564581595", "scratch-paint": "0.2.0-prerelease.20190729153946", "scratch-render": "0.1.0-prerelease.20190605151415", From 4324ae289ba9b2e46a9a678a9ff17aeead1d3cd3 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 1 Aug 2019 13:03:12 -0400 Subject: [PATCH 1169/1971] Merge pull request #5057 from rschamp/lint-tests Add Jest-specific linting rules --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ebe761b09b..f4ca664c22 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -56,6 +56,7 @@ "eslint": "^5.0.1", "eslint-config-scratch": "^5.0.0", "eslint-plugin-import": "^2.8.0", + "eslint-plugin-jest": "^22.14.1", "eslint-plugin-react": "^7.12.4", "file-loader": "2.0.0", "get-float-time-domain-data": "0.1.0", From 68a38e41b1c660a61f919180b97b646698bb5e23 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 2 Aug 2019 14:54:11 -0400 Subject: [PATCH 1170/1971] Merge pull request #5061 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1564769819 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f4ca664c22..7f1b956866 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-l10n": "3.5.20190731161310", - "scratch-blocks": "0.1.0-prerelease.1564581595", + "scratch-blocks": "0.1.0-prerelease.1564769819", "scratch-paint": "0.2.0-prerelease.20190729153946", "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", From c4dc9b0dafb8fb0d623e254e2438536c3e2334ca Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 6 Aug 2019 12:21:53 -0400 Subject: [PATCH 1171/1971] Merge pull request #5059 from paulkaplan/fix-save-after-recording Fix saving after recording a sound --- .../scratch-gui/src/lib/vm-listener-hoc.jsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 746936cb5d..79a21210e7 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -60,9 +60,9 @@ const vmListenerHOC = function (WrappedComponent) { this.props.vm.postIOData('userData', {username: this.props.username}); } - // Re-request a targets update when the shouldEmitUpdate state changes to true + // Re-request a targets update when the shouldUpdateTargets state changes to true // i.e. when the editor transitions out of fullscreen/player only modes - if (this.props.shouldEmitUpdates && !prevProps.shouldEmitUpdates) { + if (this.props.shouldUpdateTargets && !prevProps.shouldUpdateTargets) { this.props.vm.emitTargetsUpdate(false /* Emit the event, but do not trigger project change */); } } @@ -74,12 +74,12 @@ const vmListenerHOC = function (WrappedComponent) { } } handleProjectChanged () { - if (this.props.shouldEmitUpdates && !this.props.projectChanged) { + if (this.props.shouldUpdateProjectChanged && !this.props.projectChanged) { this.props.onProjectChanged(); } } handleTargetsUpdate (data) { - if (this.props.shouldEmitUpdates) { + if (this.props.shouldUpdateTargets) { this.props.onTargetsUpdate(data); } } @@ -118,7 +118,8 @@ const vmListenerHOC = function (WrappedComponent) { /* eslint-disable no-unused-vars */ attachKeyboardEvents, projectChanged, - shouldEmitUpdates, + shouldUpdateTargets, + shouldUpdateProjectChanged, onBlockDragUpdate, onGreenFlag, onKeyDown, @@ -158,7 +159,8 @@ const vmListenerHOC = function (WrappedComponent) { onTurboModeOff: PropTypes.func.isRequired, onTurboModeOn: PropTypes.func.isRequired, projectChanged: PropTypes.bool, - shouldEmitUpdates: PropTypes.bool, + shouldUpdateTargets: PropTypes.bool, + shouldUpdateProjectChanged: PropTypes.bool, username: PropTypes.string, vm: PropTypes.instanceOf(VM).isRequired }; @@ -170,8 +172,10 @@ const vmListenerHOC = function (WrappedComponent) { projectChanged: state.scratchGui.projectChanged, // Do not emit target or project updates in fullscreen or player only mode // or when recording sounds (it leads to garbled recordings on low-power machines) - shouldEmitUpdates: !state.scratchGui.mode.isFullScreen && !state.scratchGui.mode.isPlayerOnly && + shouldUpdateTargets: !state.scratchGui.mode.isFullScreen && !state.scratchGui.mode.isPlayerOnly && !state.scratchGui.modals.soundRecorder, + // Do not update the projectChanged state in fullscreen or player only mode + shouldUpdateProjectChanged: !state.scratchGui.mode.isFullScreen && !state.scratchGui.mode.isPlayerOnly, vm: state.scratchGui.vm, username: state.session && state.session.session && state.session.session.user ? state.session.session.user.username : '' From 5c96609c9246f1e41de88a247d98c775b7412a2c Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 7 Aug 2019 12:00:52 -0400 Subject: [PATCH 1172/1971] Merge pull request #5069 from fsih/updatePaint Update paint --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7f1b956866..f5bd3b608c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-l10n": "3.5.20190731161310", "scratch-blocks": "0.1.0-prerelease.1564769819", - "scratch-paint": "0.2.0-prerelease.20190729153946", + "scratch-paint": "0.2.0-prerelease.20190807143414", "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190715153806", From 0fb01b6999d05dccaa44d705c8accc3a603cae2b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 7 Aug 2019 15:38:57 -0400 Subject: [PATCH 1173/1971] Merge pull request #5072 from LLK/greenkeeper/scratch-l10n-3.5.20190807144510 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f5bd3b608c..8013ff6741 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-l10n": "3.5.20190731161310", + "scratch-l10n": "3.5.20190807144510", "scratch-blocks": "0.1.0-prerelease.1564769819", "scratch-paint": "0.2.0-prerelease.20190807143414", "scratch-render": "0.1.0-prerelease.20190605151415", From ab8e7519bceb27f630b02d0f23b8cbab0daa3d6d Mon Sep 17 00:00:00 2001 From: Colby Gutierrez-Kraybill Date: Wed, 7 Aug 2019 23:13:15 -0400 Subject: [PATCH 1174/1971] Merge pull request #5064 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190805150531 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8013ff6741..41ef169ea2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -112,7 +112,7 @@ "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190715153806", - "scratch-vm": "0.2.0-prerelease.20190731151656", + "scratch-vm": "0.2.0-prerelease.20190805150531", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From e690032c7eca884e4c95ae37f6411173a4e70ee1 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 8 Aug 2019 16:08:04 -0400 Subject: [PATCH 1175/1971] Merge pull request #5071 from fsih/webpackResolver Add webpack resolver to eslintrc so linter doesn't give false positive noUnresolved errors --- packages/scratch-gui/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 41ef169ea2..cc04595eaa 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -55,7 +55,8 @@ "es6-object-assign": "1.1.0", "eslint": "^5.0.1", "eslint-config-scratch": "^5.0.0", - "eslint-plugin-import": "^2.8.0", + "eslint-import-resolver-webpack": "^0.11.1", + "eslint-plugin-import": "^2.18.2", "eslint-plugin-jest": "^22.14.1", "eslint-plugin-react": "^7.12.4", "file-loader": "2.0.0", From 34cc538e58b817dd276a98d37c50173013ae6b47 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Tue, 13 Aug 2019 13:19:35 -0400 Subject: [PATCH 1176/1971] Merge pull request #5093 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190813154945 Greenkeeper/scratch render 0.1.0 prerelease.20190813154945 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cc04595eaa..f963255f0f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-l10n": "3.5.20190807144510", "scratch-blocks": "0.1.0-prerelease.1564769819", "scratch-paint": "0.2.0-prerelease.20190807143414", - "scratch-render": "0.1.0-prerelease.20190605151415", + "scratch-render": "0.1.0-prerelease.20190813154945", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190715153806", "scratch-vm": "0.2.0-prerelease.20190805150531", From 50687135882f3dd15ba4c45cf0511fc2ea309596 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 13 Aug 2019 16:25:05 -0400 Subject: [PATCH 1177/1971] Merge pull request #5095 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1565724757 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f963255f0f..8702fb09d1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-l10n": "3.5.20190807144510", - "scratch-blocks": "0.1.0-prerelease.1564769819", + "scratch-blocks": "0.1.0-prerelease.1565724757", "scratch-paint": "0.2.0-prerelease.20190807143414", "scratch-render": "0.1.0-prerelease.20190813154945", "scratch-storage": "1.3.2", From 37364d5b9829086b6c00d10069abe12db4388764 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Tue, 13 Aug 2019 18:10:38 -0400 Subject: [PATCH 1178/1971] Merge pull request #5094 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190813192748 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8702fb09d1..8b10825ee9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -113,7 +113,7 @@ "scratch-render": "0.1.0-prerelease.20190813154945", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190715153806", - "scratch-vm": "0.2.0-prerelease.20190805150531", + "scratch-vm": "0.2.0-prerelease.20190813192748", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 1016f9336267eb8db2b827f0d23643c0529f1d33 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Wed, 14 Aug 2019 17:54:21 -0400 Subject: [PATCH 1179/1971] Merge pull request #5097 from LLK/greenkeeper/scratch-l10n-3.5.20190813223429 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8b10825ee9..eaa07f1743 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-l10n": "3.5.20190807144510", + "scratch-l10n": "3.5.20190813223429", "scratch-blocks": "0.1.0-prerelease.1565724757", "scratch-paint": "0.2.0-prerelease.20190807143414", "scratch-render": "0.1.0-prerelease.20190813154945", From 385d4e915502a9bf6d60d99f7a94742210ecacc6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 19 Aug 2019 11:00:20 -0400 Subject: [PATCH 1180/1971] Merge pull request #5106 from fsih/revertRender Revert render to last good version --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index eaa07f1743..a977b3387f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-l10n": "3.5.20190813223429", "scratch-blocks": "0.1.0-prerelease.1565724757", "scratch-paint": "0.2.0-prerelease.20190807143414", - "scratch-render": "0.1.0-prerelease.20190813154945", + "scratch-render": "0.1.0-prerelease.20190605151415", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190715153806", "scratch-vm": "0.2.0-prerelease.20190813192748", From 2f01ad8ecc2d0b101338185f71c81b7f8643b4a4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 20 Aug 2019 15:14:07 -0400 Subject: [PATCH 1181/1971] Merge pull request #5108 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190819154958 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a977b3387f..d5e2b343a7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-l10n": "3.5.20190813223429", "scratch-blocks": "0.1.0-prerelease.1565724757", "scratch-paint": "0.2.0-prerelease.20190807143414", - "scratch-render": "0.1.0-prerelease.20190605151415", + "scratch-render": "0.1.0-prerelease.20190819154958", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190715153806", "scratch-vm": "0.2.0-prerelease.20190813192748", From 2b6e2b2c0b643b7ef39bf1acc87ec3b14ce2c238 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 20 Aug 2019 15:14:26 -0400 Subject: [PATCH 1182/1971] Merge pull request #5111 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20190820171249 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d5e2b343a7..91b34c5cd0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -112,7 +112,7 @@ "scratch-paint": "0.2.0-prerelease.20190807143414", "scratch-render": "0.1.0-prerelease.20190819154958", "scratch-storage": "1.3.2", - "scratch-svg-renderer": "0.2.0-prerelease.20190715153806", + "scratch-svg-renderer": "0.2.0-prerelease.20190820171249", "scratch-vm": "0.2.0-prerelease.20190813192748", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 07eb36de6afd0704287bf95c94c1d5aed4d8710f Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 22 Aug 2019 16:46:32 -0400 Subject: [PATCH 1183/1971] Merge pull request #5118 from fsih/updateVm Update vm --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 91b34c5cd0..e7b5b29c21 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -113,7 +113,7 @@ "scratch-render": "0.1.0-prerelease.20190819154958", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190820171249", - "scratch-vm": "0.2.0-prerelease.20190813192748", + "scratch-vm": "0.2.0-prerelease.20190822194548", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 435b44d7e6d3e0a39f3c981aa38c98ffe47f1716 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 23 Aug 2019 11:55:36 -0400 Subject: [PATCH 1184/1971] Merge pull request #5119 from fsih/updateSvgRend Update svg renderer --- packages/scratch-gui/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e7b5b29c21..16f877007f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,10 +109,10 @@ "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-l10n": "3.5.20190813223429", "scratch-blocks": "0.1.0-prerelease.1565724757", - "scratch-paint": "0.2.0-prerelease.20190807143414", - "scratch-render": "0.1.0-prerelease.20190819154958", + "scratch-paint": "0.2.0-prerelease.20190822205009", + "scratch-render": "0.1.0-prerelease.20190822204747", "scratch-storage": "1.3.2", - "scratch-svg-renderer": "0.2.0-prerelease.20190820171249", + "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", "scratch-vm": "0.2.0-prerelease.20190822194548", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From c96df982495b90f2233c5656f410208426b6d57c Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 28 Aug 2019 11:14:40 +0100 Subject: [PATCH 1185/1971] Merge pull request #5127 from LLK/greenkeeper/scratch-l10n-3.5.20190827223456 Greenkeeper/scratch l10n 3.5.20190827223456 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 16f877007f..3e06eb8727 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-l10n": "3.5.20190813223429", + "scratch-l10n": "3.5.20190827223456", "scratch-blocks": "0.1.0-prerelease.1565724757", "scratch-paint": "0.2.0-prerelease.20190822205009", "scratch-render": "0.1.0-prerelease.20190822204747", From 5e69ede6e7d586ba9352ee893fc320cb3f313f7c Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 28 Aug 2019 11:28:44 +0100 Subject: [PATCH 1186/1971] Merge pull request #5128 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1566984014 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3e06eb8727..fe7904e2db 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-l10n": "3.5.20190827223456", - "scratch-blocks": "0.1.0-prerelease.1565724757", + "scratch-blocks": "0.1.0-prerelease.1566984014", "scratch-paint": "0.2.0-prerelease.20190822205009", "scratch-render": "0.1.0-prerelease.20190822204747", "scratch-storage": "1.3.2", From 184cc5bc0c60415a97207f684afebc004051949c Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 30 Aug 2019 12:03:36 -0400 Subject: [PATCH 1187/1971] Merge pull request #5137 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190830153254 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index fe7904e2db..2d2595ca43 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-l10n": "3.5.20190827223456", "scratch-blocks": "0.1.0-prerelease.1566984014", "scratch-paint": "0.2.0-prerelease.20190822205009", - "scratch-render": "0.1.0-prerelease.20190822204747", + "scratch-render": "0.1.0-prerelease.20190830153254", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", "scratch-vm": "0.2.0-prerelease.20190822194548", From ceda176967a31b805ebbd122f4aec2318b69dec2 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Tue, 3 Sep 2019 10:46:28 -0400 Subject: [PATCH 1188/1971] Merge pull request #5146 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190830194018 Greenkeeper/scratch render 0.1.0 prerelease.20190830194018 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2d2595ca43..9cb1917828 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-l10n": "3.5.20190827223456", "scratch-blocks": "0.1.0-prerelease.1566984014", "scratch-paint": "0.2.0-prerelease.20190822205009", - "scratch-render": "0.1.0-prerelease.20190830153254", + "scratch-render": "0.1.0-prerelease.20190830194018", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", "scratch-vm": "0.2.0-prerelease.20190822194548", From 40eafa9d6b0953be6722b531a30c1a7db9a21827 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 4 Sep 2019 18:42:44 -0400 Subject: [PATCH 1189/1971] Merge pull request #5153 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190904212449 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9cb1917828..920fc7c766 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-l10n": "3.5.20190827223456", "scratch-blocks": "0.1.0-prerelease.1566984014", "scratch-paint": "0.2.0-prerelease.20190822205009", - "scratch-render": "0.1.0-prerelease.20190830194018", + "scratch-render": "0.1.0-prerelease.20190904212449", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", "scratch-vm": "0.2.0-prerelease.20190822194548", From 383e6606b63fc7d5076c0071ec507a30ff031f5a Mon Sep 17 00:00:00 2001 From: picklesrus Date: Mon, 9 Sep 2019 19:57:28 -0400 Subject: [PATCH 1190/1971] Merge pull request #5157 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1568071968 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 920fc7c766..1a8f0e8493 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-l10n": "3.5.20190827223456", - "scratch-blocks": "0.1.0-prerelease.1566984014", + "scratch-blocks": "0.1.0-prerelease.1568071968", "scratch-paint": "0.2.0-prerelease.20190822205009", "scratch-render": "0.1.0-prerelease.20190904212449", "scratch-storage": "1.3.2", From 2d2f23763a0b0eee7ea01b1b8596758eeb7e99b4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 11 Sep 2019 09:12:40 -0400 Subject: [PATCH 1191/1971] Merge pull request #5115 from paulkaplan/fix-bluetooth-keyboards Fix bluetooth keyboards with iOS keypress blocks --- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 79a21210e7..b6207a5b7d 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -87,9 +87,9 @@ const vmListenerHOC = function (WrappedComponent) { // Don't capture keys intended for Blockly inputs. if (e.target !== document && e.target !== document.body) return; + const key = (!e.key || e.key === 'Dead') ? e.keyCode : e.key; this.props.vm.postIOData('keyboard', { - keyCode: e.keyCode, - key: e.key, + key: key, isDown: true }); @@ -102,9 +102,9 @@ const vmListenerHOC = function (WrappedComponent) { handleKeyUp (e) { // Always capture up events, // even those that have switched to other targets. + const key = (!e.key || e.key === 'Dead') ? e.keyCode : e.key; this.props.vm.postIOData('keyboard', { - keyCode: e.keyCode, - key: e.key, + key: key, isDown: false }); From 11fbfc1b1d2209aef8321841f1066f6ef0839274 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 11 Sep 2019 10:16:16 -0400 Subject: [PATCH 1192/1971] Merge pull request #5154 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190905011937 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1a8f0e8493..a97b34647e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-l10n": "3.5.20190827223456", "scratch-blocks": "0.1.0-prerelease.1568071968", "scratch-paint": "0.2.0-prerelease.20190822205009", - "scratch-render": "0.1.0-prerelease.20190904212449", + "scratch-render": "0.1.0-prerelease.20190905011937", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", "scratch-vm": "0.2.0-prerelease.20190822194548", From a6c524b910fa1fc337ba19d0fba61882e0f7eb84 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 11 Sep 2019 11:56:30 -0400 Subject: [PATCH 1193/1971] Merge pull request #5161 from LLK/greenkeeper/scratch-l10n-3.5.20190910223701 Greenkeeper/scratch l10n 3.5.20190910223701 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a97b34647e..35d24a4ecc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-l10n": "3.5.20190827223456", + "scratch-l10n": "3.5.20190910223701", "scratch-blocks": "0.1.0-prerelease.1568071968", "scratch-paint": "0.2.0-prerelease.20190822205009", "scratch-render": "0.1.0-prerelease.20190905011937", From 446bc3e48d71ad6a34183f2a95f3f84d7e757f5b Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 11 Sep 2019 12:41:30 -0400 Subject: [PATCH 1194/1971] Merge pull request #5151 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190904154449 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 35d24a4ecc..90a93aa55e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -113,7 +113,7 @@ "scratch-render": "0.1.0-prerelease.20190905011937", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", - "scratch-vm": "0.2.0-prerelease.20190822194548", + "scratch-vm": "0.2.0-prerelease.20190904154449", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From f2de39499a2dc7ffa62ef0994aed84c9ec091194 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 12 Sep 2019 11:50:41 -0400 Subject: [PATCH 1195/1971] Merge pull request #5163 from rschamp/bump-chromedriver Ugly hack to keep being able to publish --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 90a93aa55e..3355e081f1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -12,7 +12,7 @@ "i18n:src": "rimraf ./translations/messages/src && babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/src ./translations/ && npm run i18n:push", "start": "webpack-dev-server", "test": "npm run test:lint && npm run test:unit && npm run build && npm run test:integration", - "test:integration": "jest --runInBand test[\\\\/]integration", + "test:integration": "jest --runInBand test[\\\\/]integration || true", "test:lint": "eslint . --ext .js,.jsx", "test:unit": "jest test[\\\\/]unit", "test:smoke": "jest --runInBand test[\\\\/]smoke", From 04f6b3a9224f6b6899266ce449298a13920b8fc5 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 12 Sep 2019 13:59:54 -0400 Subject: [PATCH 1196/1971] Merge pull request #5165 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190912174749 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3355e081f1..4cc459e2c2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-l10n": "3.5.20190910223701", "scratch-blocks": "0.1.0-prerelease.1568071968", "scratch-paint": "0.2.0-prerelease.20190822205009", - "scratch-render": "0.1.0-prerelease.20190905011937", + "scratch-render": "0.1.0-prerelease.20190912174749", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", "scratch-vm": "0.2.0-prerelease.20190904154449", From 399c74410fbe09475dbbf89e1cafac1823d1e93e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 13 Sep 2019 09:51:54 -0400 Subject: [PATCH 1197/1971] Merge pull request #5114 from LLK/default-last-asset Default blocks to last asset instead of first --- packages/scratch-gui/src/containers/blocks.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index fb01ce15d5..b659cc7145 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -340,9 +340,9 @@ class Blocks extends React.Component { const targetSounds = target.getSounds(); const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(); return makeToolboxXML(target.isStage, target.id, dynamicBlocksXML, - targetCostumes[0].name, - stageCostumes[0].name, - targetSounds.length > 0 ? targetSounds[0].name : '' + targetCostumes[targetCostumes.length - 1].name, + stageCostumes[stageCostumes.length - 1].name, + targetSounds.length > 0 ? targetSounds[targetSounds.length - 1].name : '' ); } catch { return null; From 9411a4e563a39a4264f3a10f5e77fb9875b743f7 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 16 Sep 2019 10:53:12 -0400 Subject: [PATCH 1198/1971] Merge pull request #5166 from rschamp/debug-chromedriver Use not broken Travis VM distribution --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4cc459e2c2..c71f950207 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -12,7 +12,7 @@ "i18n:src": "rimraf ./translations/messages/src && babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/src ./translations/ && npm run i18n:push", "start": "webpack-dev-server", "test": "npm run test:lint && npm run test:unit && npm run build && npm run test:integration", - "test:integration": "jest --runInBand test[\\\\/]integration || true", + "test:integration": "jest --runInBand test[\\\\/]integration", "test:lint": "eslint . --ext .js,.jsx", "test:unit": "jest test[\\\\/]unit", "test:smoke": "jest --runInBand test[\\\\/]smoke", From 92e1ec869463bfd8847a1b245e6645e1fd24b610 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 16 Sep 2019 18:56:29 -0400 Subject: [PATCH 1199/1971] Merge pull request #5170 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190916185023 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c71f950207..5ce3a09b2a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -113,7 +113,7 @@ "scratch-render": "0.1.0-prerelease.20190912174749", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", - "scratch-vm": "0.2.0-prerelease.20190904154449", + "scratch-vm": "0.2.0-prerelease.20190916185023", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 8244038432493d2dedf3d17c8f4cae38e0583c02 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 17 Sep 2019 10:54:50 -0400 Subject: [PATCH 1200/1971] Merge pull request #5175 from LLK/greenkeeper/chromedriver-77.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update chromedriver to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5ce3a09b2a..0bdf1f5ba4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -44,7 +44,7 @@ "babel-loader": "^8.0.4", "base64-loader": "1.0.0", "bowser": "1.9.4", - "chromedriver": "76.0.0", + "chromedriver": "77.0.0", "classnames": "2.2.6", "computed-style-to-inline-style": "3.0.0", "copy-webpack-plugin": "^4.5.1", From 2459b3f20fc9c67f87a682332bcf246bf37850ee Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 17 Sep 2019 14:07:04 -0400 Subject: [PATCH 1201/1971] Merge pull request #5180 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190917163837 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0bdf1f5ba4..a7448b0ba8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -113,7 +113,7 @@ "scratch-render": "0.1.0-prerelease.20190912174749", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", - "scratch-vm": "0.2.0-prerelease.20190916185023", + "scratch-vm": "0.2.0-prerelease.20190917163837", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 19745330565bb9e3dddd33510dd665f7af57710e Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 18 Sep 2019 11:10:26 -0400 Subject: [PATCH 1202/1971] Merge pull request #5182 from LLK/greenkeeper/scratch-l10n-3.5.20190917223712 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a7448b0ba8..720c1f9749 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-l10n": "3.5.20190910223701", + "scratch-l10n": "3.5.20190917223712", "scratch-blocks": "0.1.0-prerelease.1568071968", "scratch-paint": "0.2.0-prerelease.20190822205009", "scratch-render": "0.1.0-prerelease.20190912174749", From 75347f517461f6380c206424e3302d74bb6b38a2 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 18 Sep 2019 09:34:28 -0700 Subject: [PATCH 1203/1971] Merge pull request #5183 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190918022946 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 720c1f9749..54671c9f68 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -113,7 +113,7 @@ "scratch-render": "0.1.0-prerelease.20190912174749", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", - "scratch-vm": "0.2.0-prerelease.20190917163837", + "scratch-vm": "0.2.0-prerelease.20190918022946", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 4971aa2f0c9b2993179041f711513a936d23294c Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 23 Sep 2019 12:38:24 -0400 Subject: [PATCH 1204/1971] Merge pull request #5203 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190923154429 Greenkeeper/scratch paint 0.2.0 prerelease.20190923154429 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 54671c9f68..5d07a3a710 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-l10n": "3.5.20190917223712", "scratch-blocks": "0.1.0-prerelease.1568071968", - "scratch-paint": "0.2.0-prerelease.20190822205009", + "scratch-paint": "0.2.0-prerelease.20190923154429", "scratch-render": "0.1.0-prerelease.20190912174749", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", From eeb2ea1cd553c37911936bdede12b7b3b0aa5514 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 25 Sep 2019 15:06:51 +0100 Subject: [PATCH 1205/1971] Merge pull request #5209 from LLK/greenkeeper/scratch-l10n-3.5.20190924223720 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5d07a3a710..54fa85ecc4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", - "scratch-l10n": "3.5.20190917223712", + "scratch-l10n": "3.5.20190924223720", "scratch-blocks": "0.1.0-prerelease.1568071968", "scratch-paint": "0.2.0-prerelease.20190923154429", "scratch-render": "0.1.0-prerelease.20190912174749", From 15ad0894a24b67bae1f6f3371ee0339d10228b4e Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 25 Sep 2019 15:54:21 +0100 Subject: [PATCH 1206/1971] Merge pull request #5210 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1569422127 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 54fa85ecc4..82bf998473 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190114210212", "scratch-l10n": "3.5.20190924223720", - "scratch-blocks": "0.1.0-prerelease.1568071968", + "scratch-blocks": "0.1.0-prerelease.1569422127", "scratch-paint": "0.2.0-prerelease.20190923154429", "scratch-render": "0.1.0-prerelease.20190912174749", "scratch-storage": "1.3.2", From 46a325dc0a35f979c767e470e414d04e03b430ba Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 25 Sep 2019 15:49:01 -0400 Subject: [PATCH 1207/1971] Merge pull request #5211 from LLK/greenkeeper/scratch-audio-0.1.0-prerelease.20190925183642 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-audio to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 82bf998473..f8694762fe 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -106,7 +106,7 @@ "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "0.1.0-prerelease.20190114210212", + "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.5.20190924223720", "scratch-blocks": "0.1.0-prerelease.1569422127", "scratch-paint": "0.2.0-prerelease.20190923154429", From 6451ca9a83e9875360914bcbab1057ded1eedbf8 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 25 Sep 2019 13:59:22 -0700 Subject: [PATCH 1208/1971] Merge pull request #5212 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190925202615 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f8694762fe..9241d26657 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-l10n": "3.5.20190924223720", "scratch-blocks": "0.1.0-prerelease.1569422127", "scratch-paint": "0.2.0-prerelease.20190923154429", - "scratch-render": "0.1.0-prerelease.20190912174749", + "scratch-render": "0.1.0-prerelease.20190925202615", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", "scratch-vm": "0.2.0-prerelease.20190918022946", From 0ad6a54e8ce3505fddc9e903ecf96622744854bb Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 26 Sep 2019 10:05:56 -0400 Subject: [PATCH 1209/1971] Merge pull request #5216 from LLK/revert-5212-greenkeeper/scratch-render-0.1.0-prerelease.20190925202615 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "Update scratch-render to the latest version 🚀" --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9241d26657..f8694762fe 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-l10n": "3.5.20190924223720", "scratch-blocks": "0.1.0-prerelease.1569422127", "scratch-paint": "0.2.0-prerelease.20190923154429", - "scratch-render": "0.1.0-prerelease.20190925202615", + "scratch-render": "0.1.0-prerelease.20190912174749", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", "scratch-vm": "0.2.0-prerelease.20190918022946", From 95cfbc5e1d0dabf39f4a00de55c8ea00a9c004e3 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 30 Sep 2019 15:04:18 -0400 Subject: [PATCH 1210/1971] Merge pull request #5220 from rschamp/ci/circle Add config for CircleCI --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f8694762fe..2fa5ec1564 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -67,6 +67,7 @@ "immutable": "3.8.2", "intl": "1.2.5", "jest": "^21.0.0", + "jest-junit": "^7.0.0", "js-base64": "2.4.9", "keymirror": "0.1.1", "lodash.bindall": "4.4.0", From 9aa95e3743c89c72d349c4ec6a54f658dff19562 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Tue, 1 Oct 2019 10:54:32 -0500 Subject: [PATCH 1211/1971] Merge pull request #5222 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20191001145300 Greenkeeper/scratch vm 0.2.0 prerelease.20191001145300 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2fa5ec1564..5c6f569dca 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20190912174749", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", - "scratch-vm": "0.2.0-prerelease.20190918022946", + "scratch-vm": "0.2.0-prerelease.20191001145300", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 51e598cd254e5f46bd23f5bc165f76781ea058a3 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 9 Oct 2019 08:17:43 -0400 Subject: [PATCH 1212/1971] Merge pull request #5231 from LLK/greenkeeper/scratch-l10n-3.6.20191008224547 Greenkeeper/scratch l10n 3.6.20191008224547 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5c6f569dca..1ba7aeb864 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-l10n": "3.5.20190924223720", + "scratch-l10n": "3.6.20191008224547", "scratch-blocks": "0.1.0-prerelease.1569422127", "scratch-paint": "0.2.0-prerelease.20190923154429", "scratch-render": "0.1.0-prerelease.20190912174749", From c4e665bf3cd52f9946e3ac93e2d66f6c42548d15 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 9 Oct 2019 08:36:46 -0400 Subject: [PATCH 1213/1971] Merge pull request #5232 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1570622534 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1ba7aeb864..8cdd8bd996 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,8 +108,8 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-l10n": "3.6.20191008224547", - "scratch-blocks": "0.1.0-prerelease.1569422127", + "scratch-l10n": "3.5.20190924223720", + "scratch-blocks": "0.1.0-prerelease.1570622534", "scratch-paint": "0.2.0-prerelease.20190923154429", "scratch-render": "0.1.0-prerelease.20190912174749", "scratch-storage": "1.3.2", From 8f3986f90413ff12e66f4a609bfd53cab2b82637 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 9 Oct 2019 10:05:19 -0400 Subject: [PATCH 1214/1971] Merge pull request #5233 from chrisgarrity/add-swahili change l10n version to get swahili --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8cdd8bd996..dfbca81f77 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-l10n": "3.5.20190924223720", + "scratch-l10n": "3.6.20191008224547", "scratch-blocks": "0.1.0-prerelease.1570622534", "scratch-paint": "0.2.0-prerelease.20190923154429", "scratch-render": "0.1.0-prerelease.20190912174749", From 4bc1f8a65acfc57ae8db8adde5d8dcad03c42144 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 9 Oct 2019 15:32:57 -0400 Subject: [PATCH 1215/1971] Merge pull request #5234 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20191001200908 Greenkeeper/scratch render 0.1.0 prerelease.20191001200908 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index dfbca81f77..a354e2d54e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-l10n": "3.6.20191008224547", "scratch-blocks": "0.1.0-prerelease.1570622534", "scratch-paint": "0.2.0-prerelease.20190923154429", - "scratch-render": "0.1.0-prerelease.20190912174749", + "scratch-render": "0.1.0-prerelease.20191001200908", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", "scratch-vm": "0.2.0-prerelease.20191001145300", From 622ca944cd4804d13fa2ed528bfd46909c30650f Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 10 Oct 2019 12:58:46 -0400 Subject: [PATCH 1216/1971] Merge pull request #5237 from fsih/updatePaint Update paint to add swahili to overflow languages --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a354e2d54e..ffed1f9929 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.6.20191008224547", "scratch-blocks": "0.1.0-prerelease.1570622534", - "scratch-paint": "0.2.0-prerelease.20190923154429", + "scratch-paint": "0.2.0-prerelease.20191010161144", "scratch-render": "0.1.0-prerelease.20191001200908", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", From 4686c82e40f263ca186f87cc613eeb21c1319a72 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 10 Oct 2019 14:48:35 -0400 Subject: [PATCH 1217/1971] Merge pull request #5239 from chrisgarrity/update-blocks Update blocks for swahili --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ffed1f9929..3292787041 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.6.20191008224547", - "scratch-blocks": "0.1.0-prerelease.1570622534", + "scratch-blocks": "0.1.0-prerelease.1570730963", "scratch-paint": "0.2.0-prerelease.20191010161144", "scratch-render": "0.1.0-prerelease.20191001200908", "scratch-storage": "1.3.2", From 0ef91593d5161cbdea05207fbcb5827cfd40a7bb Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 14 Oct 2019 22:13:23 -0400 Subject: [PATCH 1218/1971] Merge pull request #5248 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20191015002244 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3292787041..49114b3d3a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20191001200908", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", - "scratch-vm": "0.2.0-prerelease.20191001145300", + "scratch-vm": "0.2.0-prerelease.20191015002244", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 2e44b0bf2247997e03d670f7612914af6e91dec7 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 17 Oct 2019 14:24:55 -0400 Subject: [PATCH 1219/1971] Merge pull request #5253 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20191017151616 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 49114b3d3a..4cbf63f990 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20191001200908", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", - "scratch-vm": "0.2.0-prerelease.20191015002244", + "scratch-vm": "0.2.0-prerelease.20191017151616", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From f7d59146567befe2d6acf1a1e5db651c6329050b Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Mon, 21 Oct 2019 10:52:02 -0400 Subject: [PATCH 1220/1971] Merge pull request #5168 from benjiwheeler/clear-title-on-default-project Refactor title handling --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 1fa7a01cc1..98bc128a92 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -395,7 +395,6 @@ class MenuBar extends React.Component { {(className, renderFileInput, handleLoadProject) => (
@@ -746,7 +744,6 @@ MenuBar.propTypes = { onSeeCommunity: PropTypes.func, onShare: PropTypes.func, onToggleLoginOpen: PropTypes.func, - onUpdateProjectTitle: PropTypes.func, projectTitle: PropTypes.string, renderLogin: PropTypes.func, sessionExists: PropTypes.bool, From 474ed02d8c3274edcdf5abf065f092754772ae03 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 21 Oct 2019 16:28:09 -0400 Subject: [PATCH 1221/1971] Merge pull request #5259 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20191021180653 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4cbf63f990..aa899e507c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-l10n": "3.6.20191008224547", "scratch-blocks": "0.1.0-prerelease.1570730963", "scratch-paint": "0.2.0-prerelease.20191010161144", - "scratch-render": "0.1.0-prerelease.20191001200908", + "scratch-render": "0.1.0-prerelease.20191021180653", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", "scratch-vm": "0.2.0-prerelease.20191017151616", From d3752697c8956745620501e06bab1842212e0b45 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Tue, 29 Oct 2019 17:59:36 -0400 Subject: [PATCH 1222/1971] Merge pull request #5270 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1572384380 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀[Translation update] --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index aa899e507c..77cc0ed6ba 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.6.20191008224547", - "scratch-blocks": "0.1.0-prerelease.1570730963", + "scratch-blocks": "0.1.0-prerelease.1572384380", "scratch-paint": "0.2.0-prerelease.20191010161144", "scratch-render": "0.1.0-prerelease.20191021180653", "scratch-storage": "1.3.2", From 0c210fbe6c03f08c684c787843d4662c2e12911a Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 30 Oct 2019 16:41:06 -0400 Subject: [PATCH 1223/1971] Merge pull request #5269 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20191029200037 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 77cc0ed6ba..ec4cdd0ca1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-l10n": "3.6.20191008224547", "scratch-blocks": "0.1.0-prerelease.1572384380", "scratch-paint": "0.2.0-prerelease.20191010161144", - "scratch-render": "0.1.0-prerelease.20191021180653", + "scratch-render": "0.1.0-prerelease.20191029200037", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", "scratch-vm": "0.2.0-prerelease.20191017151616", From 661dc5ba19e24218ec473aeefc5c8d1578e306b1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 1 Nov 2019 11:00:19 -0400 Subject: [PATCH 1224/1971] Merge pull request #5275 from LLK/greenkeeper/chromedriver-78.0.1 Greenkeeper/chromedriver 78.0.1 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ec4cdd0ca1..d68990fa9b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -44,7 +44,7 @@ "babel-loader": "^8.0.4", "base64-loader": "1.0.0", "bowser": "1.9.4", - "chromedriver": "77.0.0", + "chromedriver": "78.0.1", "classnames": "2.2.6", "computed-style-to-inline-style": "3.0.0", "copy-webpack-plugin": "^4.5.1", From 59ce8dc3953ee1ae46fc62ad3291d7a0dc5c0d4e Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 1 Nov 2019 15:27:27 -0400 Subject: [PATCH 1225/1971] Merge pull request #5273 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20191031221353 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 brings in https://github.com/LLK/scratch-svg-renderer/pull/84 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d68990fa9b..296470b5ca 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -113,7 +113,7 @@ "scratch-paint": "0.2.0-prerelease.20191010161144", "scratch-render": "0.1.0-prerelease.20191029200037", "scratch-storage": "1.3.2", - "scratch-svg-renderer": "0.2.0-prerelease.20190822202608", + "scratch-svg-renderer": "0.2.0-prerelease.20191031221353", "scratch-vm": "0.2.0-prerelease.20191017151616", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 64b6cd4238dfc137a9e484957e992856c6caf272 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 1 Nov 2019 20:26:57 -0400 Subject: [PATCH 1226/1971] Merge pull request #5280 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20191101193358 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 brings in LLK/scratch-svg-renderer#84 via paint --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 296470b5ca..d6a40d8b18 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.6.20191008224547", "scratch-blocks": "0.1.0-prerelease.1572384380", - "scratch-paint": "0.2.0-prerelease.20191010161144", + "scratch-paint": "0.2.0-prerelease.20191101193358", "scratch-render": "0.1.0-prerelease.20191029200037", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20191031221353", From 711056fdb6ec5d3feea474d34fa3303873493bfa Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 1 Nov 2019 20:32:13 -0400 Subject: [PATCH 1227/1971] Merge pull request #5279 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20191101193213 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 brings in LLK/scratch-svg-renderer#84 via render --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d6a40d8b18..821e0852b2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-l10n": "3.6.20191008224547", "scratch-blocks": "0.1.0-prerelease.1572384380", "scratch-paint": "0.2.0-prerelease.20191101193358", - "scratch-render": "0.1.0-prerelease.20191029200037", + "scratch-render": "0.1.0-prerelease.20191101193213", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20191031221353", "scratch-vm": "0.2.0-prerelease.20191017151616", From c9a85aa3421d586e2d845a7f7de45c9417e054c0 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Mon, 4 Nov 2019 07:50:25 -0500 Subject: [PATCH 1228/1971] Merge pull request #5274 from LLK/greenkeeper/scratch-l10n-3.6.20191029224007 Greenkeeper/scratch l10n 3.6.20191029224007 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 821e0852b2..09ee8a6162 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-l10n": "3.6.20191008224547", + "scratch-l10n": "3.6.20191029224007", "scratch-blocks": "0.1.0-prerelease.1572384380", "scratch-paint": "0.2.0-prerelease.20191101193358", "scratch-render": "0.1.0-prerelease.20191101193213", From 81c22a36897cd942e7b41ab79d8f8a90778d5f35 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 4 Nov 2019 16:35:41 -0500 Subject: [PATCH 1229/1971] Merge pull request #5281 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20191104164753 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 brings in https://github.com/LLK/scratch-svg-renderer/pull/99 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 09ee8a6162..751daf979c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -113,7 +113,7 @@ "scratch-paint": "0.2.0-prerelease.20191101193358", "scratch-render": "0.1.0-prerelease.20191101193213", "scratch-storage": "1.3.2", - "scratch-svg-renderer": "0.2.0-prerelease.20191031221353", + "scratch-svg-renderer": "0.2.0-prerelease.20191104164753", "scratch-vm": "0.2.0-prerelease.20191017151616", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From d356d4d6c9a57379771aa416eb8bf33ba1beffc7 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 4 Nov 2019 20:05:58 -0500 Subject: [PATCH 1230/1971] Merge pull request #5283 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20191104214909 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 brings in LLK/scratch-svg-renderer#99 via paint --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 751daf979c..12c0439515 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.6.20191029224007", "scratch-blocks": "0.1.0-prerelease.1572384380", - "scratch-paint": "0.2.0-prerelease.20191101193358", + "scratch-paint": "0.2.0-prerelease.20191104214909", "scratch-render": "0.1.0-prerelease.20191101193213", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20191104164753", From ec30dbb5e032524e0a9f35d60995aff4b6618c15 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 4 Nov 2019 20:07:58 -0500 Subject: [PATCH 1231/1971] Merge pull request #5282 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20191104214419 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 brings in LLK/scratch-svg-renderer#99 via render --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 12c0439515..28b9612473 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-l10n": "3.6.20191029224007", "scratch-blocks": "0.1.0-prerelease.1572384380", "scratch-paint": "0.2.0-prerelease.20191104214909", - "scratch-render": "0.1.0-prerelease.20191101193213", + "scratch-render": "0.1.0-prerelease.20191104214419", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20191104164753", "scratch-vm": "0.2.0-prerelease.20191017151616", From af4c67a87d50dd4b9333b13c4c33c4e20032363e Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Tue, 12 Nov 2019 17:51:56 -0500 Subject: [PATCH 1232/1971] Merge pull request #5258 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20191018142546 Greenkeeper/scratch vm 0.2.0 prerelease.20191018142546 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 28b9612473..121d3432ad 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20191104214419", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20191104164753", - "scratch-vm": "0.2.0-prerelease.20191017151616", + "scratch-vm": "0.2.0-prerelease.20191018142546", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 186aa485cdb91e39e8600ba9810d14a270d7861e Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 14 Nov 2019 11:02:14 -0500 Subject: [PATCH 1233/1971] Merge pull request #5295 from fsih/updateRender MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backing out the render change, because we want the recent render chan… --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 121d3432ad..59713f3f88 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-l10n": "3.6.20191029224007", "scratch-blocks": "0.1.0-prerelease.1572384380", "scratch-paint": "0.2.0-prerelease.20191104214909", - "scratch-render": "0.1.0-prerelease.20191104214419", + "scratch-render": "0.1.0-prerelease.20191021180653", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20191104164753", "scratch-vm": "0.2.0-prerelease.20191018142546", From 0da50296d80888d594234388da4fc20fa1f4ef6f Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 14 Nov 2019 15:44:29 -0500 Subject: [PATCH 1234/1971] Merge pull request #5297 from fsih/updateRender Update render --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 59713f3f88..710949338c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-l10n": "3.6.20191029224007", "scratch-blocks": "0.1.0-prerelease.1572384380", "scratch-paint": "0.2.0-prerelease.20191104214909", - "scratch-render": "0.1.0-prerelease.20191021180653", + "scratch-render": "0.1.0-prerelease.20191106204814", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20191104164753", "scratch-vm": "0.2.0-prerelease.20191018142546", From 4ae76a324da84553e16ab232b9a1f83f0f90fcb8 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 14 Nov 2019 16:09:46 -0500 Subject: [PATCH 1235/1971] Merge pull request #5298 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20191114200704 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 710949338c..b341419aca 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.6.20191029224007", "scratch-blocks": "0.1.0-prerelease.1572384380", - "scratch-paint": "0.2.0-prerelease.20191104214909", + "scratch-paint": "0.2.0-prerelease.20191114200704", "scratch-render": "0.1.0-prerelease.20191106204814", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20191104164753", From dcda8beb94ee359afd60112f63c76432c545a988 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Mon, 18 Nov 2019 08:20:59 -0500 Subject: [PATCH 1236/1971] Merge pull request #5296 from LLK/greenkeeper/scratch-l10n-3.6.20191112223853 Greenkeeper/scratch l10n 3.6.20191112223853 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b341419aca..e6e78474db 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-l10n": "3.6.20191029224007", + "scratch-l10n": "3.6.20191112223853", "scratch-blocks": "0.1.0-prerelease.1572384380", "scratch-paint": "0.2.0-prerelease.20191114200704", "scratch-render": "0.1.0-prerelease.20191106204814", From c4b7fd617eb443b002c9ed0553380d9e1846890c Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 19 Nov 2019 09:56:31 -0500 Subject: [PATCH 1237/1971] Merge pull request #5306 from ericrosenbaum/feature/update-vm update vm --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e6e78474db..821ed1134a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20191106204814", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20191104164753", - "scratch-vm": "0.2.0-prerelease.20191018142546", + "scratch-vm": "0.2.0-prerelease.20191118212927", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From ee270713e21672b18e6931893d67ee7bcb4bdfe7 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Tue, 19 Nov 2019 17:18:32 -0500 Subject: [PATCH 1238/1971] Merge pull request #5310 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1574180945 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Translations: Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 821ed1134a..8f2e717e20 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.6.20191112223853", - "scratch-blocks": "0.1.0-prerelease.1572384380", + "scratch-blocks": "0.1.0-prerelease.1574180945", "scratch-paint": "0.2.0-prerelease.20191114200704", "scratch-render": "0.1.0-prerelease.20191106204814", "scratch-storage": "1.3.2", From c047a2577b3b4dc235e43915eb300392136bd244 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 20 Nov 2019 11:51:59 -0500 Subject: [PATCH 1239/1971] Merge pull request #5311 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20191119203901 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8f2e717e20..124d79ec7c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20191106204814", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20191104164753", - "scratch-vm": "0.2.0-prerelease.20191118212927", + "scratch-vm": "0.2.0-prerelease.20191119203901", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 03a18b11a518b7d7e2f66717549fcdd5a139528a Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 20 Nov 2019 12:50:08 -0500 Subject: [PATCH 1240/1971] Merge pull request #5315 from LLK/greenkeeper/scratch-l10n-3.6.20191120161253 Greenkeeper/scratch l10n 3.6.20191120161253 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 124d79ec7c..9144803a07 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-l10n": "3.6.20191112223853", + "scratch-l10n": "3.6.20191120161253", "scratch-blocks": "0.1.0-prerelease.1574180945", "scratch-paint": "0.2.0-prerelease.20191114200704", "scratch-render": "0.1.0-prerelease.20191106204814", From 05cc78f0efcf0901f06114ad29747848ed52f351 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 4 Dec 2019 09:33:20 -0500 Subject: [PATCH 1241/1971] Merge pull request #5319 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20191126210304 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9144803a07..e1fe84dac3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-l10n": "3.6.20191120161253", "scratch-blocks": "0.1.0-prerelease.1574180945", "scratch-paint": "0.2.0-prerelease.20191114200704", - "scratch-render": "0.1.0-prerelease.20191106204814", + "scratch-render": "0.1.0-prerelease.20191126210304", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20191104164753", "scratch-vm": "0.2.0-prerelease.20191119203901", From 0b85ea1ce81cb30a98e01c4c8cdb6b5cba6d2846 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 4 Dec 2019 09:36:58 -0500 Subject: [PATCH 1242/1971] Merge pull request #5325 from LLK/greenkeeper/scratch-l10n-3.6.20191204142956 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e1fe84dac3..b12034f2fc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-l10n": "3.6.20191120161253", + "scratch-l10n": "3.6.20191204142956", "scratch-blocks": "0.1.0-prerelease.1574180945", "scratch-paint": "0.2.0-prerelease.20191114200704", "scratch-render": "0.1.0-prerelease.20191126210304", From 6bab895c032e620d5d7311205a50a9c8680c9eb3 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 5 Dec 2019 09:33:36 -0500 Subject: [PATCH 1243/1971] Merge pull request #5328 from LLK/revert-5319-greenkeeper/scratch-render-0.1.0-prerelease.20191126210304 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "Update scratch-render to the latest version 🚀" --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b12034f2fc..82f11f04ba 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-l10n": "3.6.20191204142956", "scratch-blocks": "0.1.0-prerelease.1574180945", "scratch-paint": "0.2.0-prerelease.20191114200704", - "scratch-render": "0.1.0-prerelease.20191126210304", + "scratch-render": "0.1.0-prerelease.20191106204814", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20191104164753", "scratch-vm": "0.2.0-prerelease.20191119203901", From e1f90b1f3191587f57e0ba8d927dbb0dd35a6f8b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 5 Dec 2019 10:29:15 -0500 Subject: [PATCH 1244/1971] Merge pull request #5329 from LLK/revert-5328-revert-5319-greenkeeper/scratch-render-0.1.0-prerelease.20191126210304 Bring back in renderer update with mipmap change --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 82f11f04ba..b12034f2fc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-l10n": "3.6.20191204142956", "scratch-blocks": "0.1.0-prerelease.1574180945", "scratch-paint": "0.2.0-prerelease.20191114200704", - "scratch-render": "0.1.0-prerelease.20191106204814", + "scratch-render": "0.1.0-prerelease.20191126210304", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20191104164753", "scratch-vm": "0.2.0-prerelease.20191119203901", From 32a33a17c02e09bcd62cdd32134080ddc2e0a268 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 10 Dec 2019 05:40:44 -0500 Subject: [PATCH 1245/1971] Merge pull request #5331 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20191209210523 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b12034f2fc..a36fecddf5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.6.20191204142956", "scratch-blocks": "0.1.0-prerelease.1574180945", - "scratch-paint": "0.2.0-prerelease.20191114200704", + "scratch-paint": "0.2.0-prerelease.20191209210523", "scratch-render": "0.1.0-prerelease.20191126210304", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20191104164753", From b53868898bf9a52bf7cec79b5a8472d553c7f147 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 10 Dec 2019 10:36:05 -0500 Subject: [PATCH 1246/1971] Merge pull request #5332 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20191210104711 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a36fecddf5..be8e389366 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.6.20191204142956", "scratch-blocks": "0.1.0-prerelease.1574180945", - "scratch-paint": "0.2.0-prerelease.20191209210523", + "scratch-paint": "0.2.0-prerelease.20191210104711", "scratch-render": "0.1.0-prerelease.20191126210304", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20191104164753", From 1c0c86c365d48d4929ee7193b66955d5f087426b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 11 Dec 2019 11:17:10 -0500 Subject: [PATCH 1247/1971] Merge pull request #5336 from LLK/greenkeeper/scratch-l10n-3.6.20191210224141 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index be8e389366..3848a7b6a3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-l10n": "3.6.20191204142956", + "scratch-l10n": "3.6.20191210224141", "scratch-blocks": "0.1.0-prerelease.1574180945", "scratch-paint": "0.2.0-prerelease.20191210104711", "scratch-render": "0.1.0-prerelease.20191126210304", From c56971cada1273cedc6d7dc70e6cbb0d40fd44ee Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 11 Dec 2019 15:59:48 -0500 Subject: [PATCH 1248/1971] Merge pull request #5337 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20191211203739 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3848a7b6a3..6872a95f63 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-l10n": "3.6.20191210224141", "scratch-blocks": "0.1.0-prerelease.1574180945", "scratch-paint": "0.2.0-prerelease.20191210104711", - "scratch-render": "0.1.0-prerelease.20191126210304", + "scratch-render": "0.1.0-prerelease.20191211203739", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20191104164753", "scratch-vm": "0.2.0-prerelease.20191119203901", From 488cfb9d9d8f90133088e2f4852d1082921d6267 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Tue, 17 Dec 2019 16:19:48 -0500 Subject: [PATCH 1249/1971] Merge pull request #5344 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20191217211338 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6872a95f63..384e7d7ca5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -113,7 +113,7 @@ "scratch-paint": "0.2.0-prerelease.20191210104711", "scratch-render": "0.1.0-prerelease.20191211203739", "scratch-storage": "1.3.2", - "scratch-svg-renderer": "0.2.0-prerelease.20191104164753", + "scratch-svg-renderer": "0.2.0-prerelease.20191217211338", "scratch-vm": "0.2.0-prerelease.20191119203901", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From fdc8d2ff46cd901077c7a7552239f276e8bdcdb8 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Tue, 17 Dec 2019 16:29:30 -0500 Subject: [PATCH 1250/1971] Merge pull request #5345 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20191217212645 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 384e7d7ca5..60ac4778e9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-l10n": "3.6.20191210224141", "scratch-blocks": "0.1.0-prerelease.1574180945", "scratch-paint": "0.2.0-prerelease.20191210104711", - "scratch-render": "0.1.0-prerelease.20191211203739", + "scratch-render": "0.1.0-prerelease.20191217212645", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20191217211338", "scratch-vm": "0.2.0-prerelease.20191119203901", From 1e7f1cfee2e11941ca450bd8d1671a5270f351ff Mon Sep 17 00:00:00 2001 From: picklesrus Date: Tue, 17 Dec 2019 17:05:33 -0500 Subject: [PATCH 1251/1971] Merge pull request #5346 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20191217213717 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 60ac4778e9..a184943ae1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.6.20191210224141", "scratch-blocks": "0.1.0-prerelease.1574180945", - "scratch-paint": "0.2.0-prerelease.20191210104711", + "scratch-paint": "0.2.0-prerelease.20191217213717", "scratch-render": "0.1.0-prerelease.20191217212645", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20191217211338", From 6524e67ed2245dd2bf908b87d4a15df68e74a10b Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Fri, 20 Dec 2019 08:06:10 -0500 Subject: [PATCH 1252/1971] Merge pull request #5349 from LLK/greenkeeper/scratch-l10n-3.7.20191219145348 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a184943ae1..249fb7be55 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-l10n": "3.6.20191210224141", + "scratch-l10n": "3.7.20191219145348", "scratch-blocks": "0.1.0-prerelease.1574180945", "scratch-paint": "0.2.0-prerelease.20191217213717", "scratch-render": "0.1.0-prerelease.20191217212645", From f914a7d2d47389f218d477a284055db68c9eb132 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Fri, 20 Dec 2019 11:28:06 -0500 Subject: [PATCH 1253/1971] Merge pull request #5352 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1576850350 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 249fb7be55..9c7c75ec8e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.7.20191219145348", - "scratch-blocks": "0.1.0-prerelease.1574180945", + "scratch-blocks": "0.1.0-prerelease.1576850350", "scratch-paint": "0.2.0-prerelease.20191217213717", "scratch-render": "0.1.0-prerelease.20191217212645", "scratch-storage": "1.3.2", From 84daf0f356a657dfd9d278f86ad21fd978070384 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 27 Dec 2019 14:34:16 -0500 Subject: [PATCH 1254/1971] Merge pull request #5355 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20191227164934 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9c7c75ec8e..16fbbc1b78 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20191217212645", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20191217211338", - "scratch-vm": "0.2.0-prerelease.20191119203901", + "scratch-vm": "0.2.0-prerelease.20191227164934", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 0a867be4ad3b3d4d62867f5547406c518169320b Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Mon, 30 Dec 2019 23:18:50 -0500 Subject: [PATCH 1255/1971] Merge pull request #5357 from LLK/greenkeeper/papaparse-5.1.1 Greenkeeper/papaparse 5.1.1 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 16fbbc1b78..fa6da61e52 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -80,7 +80,7 @@ "minilog": "3.1.0", "mkdirp": "^0.5.1", "omggif": "1.0.9", - "papaparse": "4.6.2", + "papaparse": "5.1.1", "postcss-import": "^12.0.0", "postcss-loader": "^3.0.0", "postcss-simple-vars": "^5.0.1", From ac9afd6d46104eea92ba8b9955fa159b1c8de8eb Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 7 Jan 2020 11:48:59 -0500 Subject: [PATCH 1256/1971] Merge pull request #5358 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20200103191258 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index fa6da61e52..85fb7dc697 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -113,7 +113,7 @@ "scratch-paint": "0.2.0-prerelease.20191217213717", "scratch-render": "0.1.0-prerelease.20191217212645", "scratch-storage": "1.3.2", - "scratch-svg-renderer": "0.2.0-prerelease.20191217211338", + "scratch-svg-renderer": "0.2.0-prerelease.20200103191258", "scratch-vm": "0.2.0-prerelease.20191227164934", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From f77cf9824fef797c48be4bd20812a2c7c757e7a5 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 7 Jan 2020 14:11:58 -0500 Subject: [PATCH 1257/1971] Merge pull request #5366 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20200107165342 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 85fb7dc697..75472e51ae 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-l10n": "3.7.20191219145348", "scratch-blocks": "0.1.0-prerelease.1576850350", "scratch-paint": "0.2.0-prerelease.20191217213717", - "scratch-render": "0.1.0-prerelease.20191217212645", + "scratch-render": "0.1.0-prerelease.20200107165342", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200103191258", "scratch-vm": "0.2.0-prerelease.20191227164934", From 0601ea58807f4e1e67f9cbbe8413251b5801602a Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 7 Jan 2020 14:13:09 -0500 Subject: [PATCH 1258/1971] Merge pull request #5367 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20200107165446 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 75472e51ae..bc9cd59664 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.7.20191219145348", "scratch-blocks": "0.1.0-prerelease.1576850350", - "scratch-paint": "0.2.0-prerelease.20191217213717", + "scratch-paint": "0.2.0-prerelease.20200107165446", "scratch-render": "0.1.0-prerelease.20200107165342", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200103191258", From 294c49b555db042d7425db3ccf94ef6cb1659ea0 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 7 Jan 2020 16:22:38 -0500 Subject: [PATCH 1259/1971] Merge pull request #5368 from fsih/updateSvgRend Update SVG Renderer --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index bc9cd59664..0b7215c1e1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -113,7 +113,7 @@ "scratch-paint": "0.2.0-prerelease.20200107165446", "scratch-render": "0.1.0-prerelease.20200107165342", "scratch-storage": "1.3.2", - "scratch-svg-renderer": "0.2.0-prerelease.20200103191258", + "scratch-svg-renderer": "0.2.0-prerelease.20200103211543", "scratch-vm": "0.2.0-prerelease.20191227164934", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 395224b4a6be8d399262bd8fb18a48ebc0c0c16e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 8 Jan 2020 10:03:50 -0500 Subject: [PATCH 1260/1971] Merge pull request #5369 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20200107211248 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0b7215c1e1..c660f61749 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.7.20191219145348", "scratch-blocks": "0.1.0-prerelease.1576850350", - "scratch-paint": "0.2.0-prerelease.20200107165446", + "scratch-paint": "0.2.0-prerelease.20200107211248", "scratch-render": "0.1.0-prerelease.20200107165342", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200103211543", From 5f0500c162510524218f8f580bb7f75afb2975d2 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Wed, 8 Jan 2020 21:53:40 -0500 Subject: [PATCH 1261/1971] Merge pull request #5360 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1578322100 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c660f61749..14e40b8f2c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.7.20191219145348", - "scratch-blocks": "0.1.0-prerelease.1576850350", + "scratch-blocks": "0.1.0-prerelease.1578322100", "scratch-paint": "0.2.0-prerelease.20200107211248", "scratch-render": "0.1.0-prerelease.20200107165342", "scratch-storage": "1.3.2", From 2508ca4b4723ee07b41a25f22f83857322345047 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Wed, 8 Jan 2020 21:59:19 -0500 Subject: [PATCH 1262/1971] Merge pull request #5370 from LLK/greenkeeper/scratch-l10n-3.7.20200108141814 Greenkeeper/scratch l10n 3.7.20200108141814 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 14e40b8f2c..a4e6773626 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-l10n": "3.7.20191219145348", + "scratch-l10n": "3.7.20200108141814", "scratch-blocks": "0.1.0-prerelease.1578322100", "scratch-paint": "0.2.0-prerelease.20200107211248", "scratch-render": "0.1.0-prerelease.20200107165342", From fe5e3832c1cb67386715749dfe4ddd58a059bcf5 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 9 Jan 2020 02:52:28 -0500 Subject: [PATCH 1263/1971] Merge pull request #5374 from fsih/updateEmptyPaintBug Fix empty paint bug --- packages/scratch-gui/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a4e6773626..baa7a0b7a5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,10 +110,10 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.7.20200108141814", "scratch-blocks": "0.1.0-prerelease.1578322100", - "scratch-paint": "0.2.0-prerelease.20200107211248", - "scratch-render": "0.1.0-prerelease.20200107165342", + "scratch-paint": "0.2.0-prerelease.20200109073728", + "scratch-render": "0.1.0-prerelease.20200109074051", "scratch-storage": "1.3.2", - "scratch-svg-renderer": "0.2.0-prerelease.20200103211543", + "scratch-svg-renderer": "0.2.0-prerelease.20200109070519", "scratch-vm": "0.2.0-prerelease.20191227164934", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 459d28e78d9807f2bfa522d3c1297f318a5d6227 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 13 Jan 2020 15:37:55 -0500 Subject: [PATCH 1264/1971] Merge pull request #5384 from fsih/updateRender Update with render hotfix --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index baa7a0b7a5..5797ac508d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-l10n": "3.7.20200108141814", "scratch-blocks": "0.1.0-prerelease.1578322100", "scratch-paint": "0.2.0-prerelease.20200109073728", - "scratch-render": "0.1.0-prerelease.20200109074051", + "scratch-render": "0.1.0-prerelease.20200113193920", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200109070519", "scratch-vm": "0.2.0-prerelease.20191227164934", From bee3dceaf9b5a102433b0da2b07e9d99147d492b Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 22 Jan 2020 16:51:33 -0500 Subject: [PATCH 1265/1971] Merge pull request #5388 from LLK/greenkeeper/scratch-l10n-3.7.20200114224412 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5797ac508d..f8d734f4c7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-l10n": "3.7.20200108141814", + "scratch-l10n": "3.7.20200114224412", "scratch-blocks": "0.1.0-prerelease.1578322100", "scratch-paint": "0.2.0-prerelease.20200109073728", "scratch-render": "0.1.0-prerelease.20200113193920", From cf34a8d4dbb9a669edde773d9b74c7869415d889 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 29 Jan 2020 06:02:37 -0500 Subject: [PATCH 1266/1971] Merge pull request #5398 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20200128205403 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f8d734f4c7..c564b46090 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-l10n": "3.7.20200114224412", "scratch-blocks": "0.1.0-prerelease.1578322100", "scratch-paint": "0.2.0-prerelease.20200109073728", - "scratch-render": "0.1.0-prerelease.20200113193920", + "scratch-render": "0.1.0-prerelease.20200128205403", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200109070519", "scratch-vm": "0.2.0-prerelease.20191227164934", From 3601167976a36245367884f2805f79c438bd6887 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 29 Jan 2020 08:46:15 -0500 Subject: [PATCH 1267/1971] Merge pull request #5399 from LLK/greenkeeper/scratch-l10n-3.7.20200128224436 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c564b46090..b3a0269a0c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-l10n": "3.7.20200114224412", + "scratch-l10n": "3.7.20200128224436", "scratch-blocks": "0.1.0-prerelease.1578322100", "scratch-paint": "0.2.0-prerelease.20200109073728", "scratch-render": "0.1.0-prerelease.20200128205403", From 122bfea5c49196d4000d3f40f1b2ebb2cb05496d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 29 Jan 2020 13:00:34 -0500 Subject: [PATCH 1268/1971] Merge pull request #5400 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1580309823 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b3a0269a0c..d1fdba1661 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.7.20200128224436", - "scratch-blocks": "0.1.0-prerelease.1578322100", + "scratch-blocks": "0.1.0-prerelease.1580309823", "scratch-paint": "0.2.0-prerelease.20200109073728", "scratch-render": "0.1.0-prerelease.20200128205403", "scratch-storage": "1.3.2", From a7d566278088499f260d9ff601df39dd77890539 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 29 Jan 2020 13:43:31 -0500 Subject: [PATCH 1269/1971] Merge pull request #5402 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20200129174741 Greenkeeper/scratch paint 0.2.0 prerelease.20200129174741 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d1fdba1661..95075099e9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.7.20200128224436", "scratch-blocks": "0.1.0-prerelease.1580309823", - "scratch-paint": "0.2.0-prerelease.20200109073728", + "scratch-paint": "0.2.0-prerelease.20200129174741", "scratch-render": "0.1.0-prerelease.20200128205403", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200109070519", From c7277ed7bf7765e5e7ca5a315df1e809142dd31c Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 4 Feb 2020 16:41:51 -0500 Subject: [PATCH 1270/1971] Merge pull request #5417 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20200204155336 Greenkeeper/scratch paint 0.2.0 prerelease.20200204155336 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 95075099e9..632c55c987 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.7.20200128224436", "scratch-blocks": "0.1.0-prerelease.1580309823", - "scratch-paint": "0.2.0-prerelease.20200129174741", + "scratch-paint": "0.2.0-prerelease.20200204155336", "scratch-render": "0.1.0-prerelease.20200128205403", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200109070519", From bd4aa42f2719a915087160522ea4a2cd456a92f4 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Tue, 4 Feb 2020 17:32:04 -0500 Subject: [PATCH 1271/1971] Merge pull request #4809 from benjiwheeler/refactor-file-uploader refactor SB* file uploader into top-level HOC --- .../src/components/menu-bar/menu-bar.jsx | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 98bc128a92..05a5294bc3 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -17,7 +17,6 @@ import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx'; import Divider from '../divider/divider.jsx'; import LanguageSelector from '../../containers/language-selector.jsx'; import SaveStatus from './save-status.jsx'; -import SBFileUploader from '../../containers/sb-file-uploader.jsx'; import ProjectWatcher from '../../containers/project-watcher.jsx'; import MenuBarMenu from './menu-bar-menu.jsx'; import {MenuItem, MenuSection} from '../menu/menu.jsx'; @@ -392,22 +391,11 @@ class MenuBar extends React.Component { )} - - {(className, renderFileInput, handleLoadProject) => ( - - {/* eslint-disable max-len */} - {this.props.intl.formatMessage(sharedMessages.loadFromComputerTitle)} - {/* eslint-enable max-len */} - {renderFileInput()} - - )} - + {this.props.intl.formatMessage(sharedMessages.loadFromComputerTitle)} + {(className, downloadProjectCallback) => ( Date: Tue, 4 Feb 2020 21:38:46 -0500 Subject: [PATCH 1272/1971] Merge pull request #5424 from fsih/updateDeps Update dependencies --- packages/scratch-gui/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 632c55c987..accc4bdec6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,11 +109,11 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.7.20200128224436", - "scratch-blocks": "0.1.0-prerelease.1580309823", - "scratch-paint": "0.2.0-prerelease.20200204155336", - "scratch-render": "0.1.0-prerelease.20200128205403", + "scratch-blocks": "0.1.0-prerelease.1580827419", + "scratch-paint": "0.2.0-prerelease.20200205010220", + "scratch-render": "0.1.0-prerelease.20200205011809", "scratch-storage": "1.3.2", - "scratch-svg-renderer": "0.2.0-prerelease.20200109070519", + "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", "scratch-vm": "0.2.0-prerelease.20191227164934", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From a7e4d1fef62ae5199bafdf307c98a1aab3d6bd9f Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 5 Feb 2020 11:02:41 -0500 Subject: [PATCH 1273/1971] Merge pull request #5426 from LLK/greenkeeper/chromedriver-80.0.0 Greenkeeper/chromedriver 80.0.0 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index accc4bdec6..66519ef4cd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -44,7 +44,7 @@ "babel-loader": "^8.0.4", "base64-loader": "1.0.0", "bowser": "1.9.4", - "chromedriver": "78.0.1", + "chromedriver": "80.0.0", "classnames": "2.2.6", "computed-style-to-inline-style": "3.0.0", "copy-webpack-plugin": "^4.5.1", From 3dca048c1d84f646698a84a696af0e9c9d681fd8 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 5 Feb 2020 13:03:56 -0500 Subject: [PATCH 1274/1971] Merge pull request #5427 from LLK/greenkeeper/mkdirp-1.0.3 Greenkeeper/mkdirp 1.0.3 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 66519ef4cd..d9e210504b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -78,7 +78,7 @@ "lodash.pick": "4.4.0", "lodash.throttle": "4.0.1", "minilog": "3.1.0", - "mkdirp": "^0.5.1", + "mkdirp": "^1.0.3", "omggif": "1.0.9", "papaparse": "5.1.1", "postcss-import": "^12.0.0", From 2527b78bee5693be427e1a087307ffd40874f0cb Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 5 Feb 2020 15:32:30 -0500 Subject: [PATCH 1275/1971] Merge pull request #5428 from fsih/unUpdateDeps Revert "Update dependencies" --- packages/scratch-gui/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d9e210504b..c262fa4fcf 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,11 +109,11 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.7.20200128224436", - "scratch-blocks": "0.1.0-prerelease.1580827419", - "scratch-paint": "0.2.0-prerelease.20200205010220", - "scratch-render": "0.1.0-prerelease.20200205011809", + "scratch-blocks": "0.1.0-prerelease.1580309823", + "scratch-paint": "0.2.0-prerelease.20200204155336", + "scratch-render": "0.1.0-prerelease.20200128205403", "scratch-storage": "1.3.2", - "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", + "scratch-svg-renderer": "0.2.0-prerelease.20200109070519", "scratch-vm": "0.2.0-prerelease.20191227164934", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 6579beee0abaaf441ffad9d82c67f2a4736141c1 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 5 Feb 2020 16:41:42 -0500 Subject: [PATCH 1276/1971] Merge pull request #5432 from fsih/updateDeps Update blocks and l10n --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c262fa4fcf..8258d2ad5f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,8 +108,8 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-l10n": "3.7.20200128224436", - "scratch-blocks": "0.1.0-prerelease.1580309823", + "scratch-l10n": "3.7.20200205213636", + "scratch-blocks": "0.1.0-prerelease.1580911107", "scratch-paint": "0.2.0-prerelease.20200204155336", "scratch-render": "0.1.0-prerelease.20200128205403", "scratch-storage": "1.3.2", From 0215902b95b7f571bcda5f9422abf96d05e5cd05 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 5 Feb 2020 17:42:54 -0500 Subject: [PATCH 1277/1971] Merge pull request #5433 from fsih/updatePaint update paint' --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8258d2ad5f..9735dbff7d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.7.20200205213636", "scratch-blocks": "0.1.0-prerelease.1580911107", - "scratch-paint": "0.2.0-prerelease.20200204155336", + "scratch-paint": "0.2.0-prerelease.20200204235639", "scratch-render": "0.1.0-prerelease.20200128205403", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200109070519", From 89040de0184e8ad54e832ec467a5f8771a4f4160 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Thu, 6 Feb 2020 11:38:58 -0500 Subject: [PATCH 1278/1971] Merge pull request #5437 from LLK/revert-4809-refactor-file-uploader Revert "refactor SB* file uploader into top-level HOC" --- .../src/components/menu-bar/menu-bar.jsx | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 05a5294bc3..98bc128a92 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -17,6 +17,7 @@ import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx'; import Divider from '../divider/divider.jsx'; import LanguageSelector from '../../containers/language-selector.jsx'; import SaveStatus from './save-status.jsx'; +import SBFileUploader from '../../containers/sb-file-uploader.jsx'; import ProjectWatcher from '../../containers/project-watcher.jsx'; import MenuBarMenu from './menu-bar-menu.jsx'; import {MenuItem, MenuSection} from '../menu/menu.jsx'; @@ -391,11 +392,22 @@ class MenuBar extends React.Component { )} - - {this.props.intl.formatMessage(sharedMessages.loadFromComputerTitle)} - + {(className, renderFileInput, handleLoadProject) => ( + + {/* eslint-disable max-len */} + {this.props.intl.formatMessage(sharedMessages.loadFromComputerTitle)} + {/* eslint-enable max-len */} + {renderFileInput()} + + )} + {(className, downloadProjectCallback) => ( Date: Thu, 6 Feb 2020 12:57:20 -0500 Subject: [PATCH 1279/1971] Merge pull request #5439 from fsih/updatePaint Update paint to roll back centering tool --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9735dbff7d..e7b78f3cd5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.7.20200205213636", "scratch-blocks": "0.1.0-prerelease.1580911107", - "scratch-paint": "0.2.0-prerelease.20200204235639", + "scratch-paint": "0.2.0-prerelease.20200206172557", "scratch-render": "0.1.0-prerelease.20200128205403", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200109070519", From d8848727dbd28da1ced6bdb4ba68809d3f59d785 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 7 Feb 2020 13:56:01 -0500 Subject: [PATCH 1280/1971] Merge pull request #5441 from fsih/updateSvgRend Add back rounded gradient strokes for 2.0 projects --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e7b78f3cd5..b943268d7a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,9 +111,9 @@ "scratch-l10n": "3.7.20200205213636", "scratch-blocks": "0.1.0-prerelease.1580911107", "scratch-paint": "0.2.0-prerelease.20200206172557", - "scratch-render": "0.1.0-prerelease.20200128205403", + "scratch-render": "0.1.0-prerelease.20200205011809", "scratch-storage": "1.3.2", - "scratch-svg-renderer": "0.2.0-prerelease.20200109070519", + "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", "scratch-vm": "0.2.0-prerelease.20191227164934", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 51bd8b75cbd16eb82a4c0e26275f2abf32d129ce Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 10 Feb 2020 13:21:31 -0500 Subject: [PATCH 1281/1971] Merge pull request #5447 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20200210174730 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b943268d7a..aeebe4920e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.7.20200205213636", "scratch-blocks": "0.1.0-prerelease.1580911107", - "scratch-paint": "0.2.0-prerelease.20200206172557", + "scratch-paint": "0.2.0-prerelease.20200210174730", "scratch-render": "0.1.0-prerelease.20200205011809", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", From 2e5f7402266908ead797fce17c003d27d962fcf5 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 12 Feb 2020 13:41:06 -0500 Subject: [PATCH 1282/1971] Merge pull request #5455 from fsih/updatePaint Update paint --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index aeebe4920e..175d8f6475 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.7.20200205213636", "scratch-blocks": "0.1.0-prerelease.1580911107", - "scratch-paint": "0.2.0-prerelease.20200210174730", + "scratch-paint": "0.2.0-prerelease.20200212183139", "scratch-render": "0.1.0-prerelease.20200205011809", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", From e7894c563e2efd1fff75bbcb17477a7acdfa6d6d Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 12 Feb 2020 17:55:33 -0500 Subject: [PATCH 1283/1971] Merge pull request #5453 from LLK/greenkeeper/scratch-l10n-3.7.20200212141356 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 175d8f6475..d4689f3a11 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-l10n": "3.7.20200205213636", + "scratch-l10n": "3.7.20200212141356", "scratch-blocks": "0.1.0-prerelease.1580911107", "scratch-paint": "0.2.0-prerelease.20200212183139", "scratch-render": "0.1.0-prerelease.20200205011809", From e978d27a796a564c3ee6d0cb075025cdb383dc88 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 12 Feb 2020 18:01:33 -0500 Subject: [PATCH 1284/1971] Merge pull request #5449 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1581428931 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d4689f3a11..68822c9a75 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.7.20200212141356", - "scratch-blocks": "0.1.0-prerelease.1580911107", + "scratch-blocks": "0.1.0-prerelease.1581428931", "scratch-paint": "0.2.0-prerelease.20200212183139", "scratch-render": "0.1.0-prerelease.20200205011809", "scratch-storage": "1.3.2", From 84e469af4b768d670a7c8f5e5e07a283a2671cfc Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 13 Feb 2020 12:55:41 -0500 Subject: [PATCH 1285/1971] Merge pull request #5457 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20200213174123 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 68822c9a75..f5f9b78312 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.7.20200212141356", "scratch-blocks": "0.1.0-prerelease.1581428931", - "scratch-paint": "0.2.0-prerelease.20200212183139", + "scratch-paint": "0.2.0-prerelease.20200213174123", "scratch-render": "0.1.0-prerelease.20200205011809", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", From af6c1e8eaebfc50a959350f4865986f51a391983 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 13 Feb 2020 15:33:52 -0500 Subject: [PATCH 1286/1971] Merge pull request #5459 from LLK/ci-skip-gh-pages Ignore commits on the gh-pages branch for CI --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f5f9b78312..3a2d6067fd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -6,7 +6,7 @@ "scripts": { "build": "npm run clean && webpack --progress --colors --bail", "clean": "rimraf ./build && mkdirp build && rimraf ./dist && mkdirp dist", - "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"", + "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1) [skip ci]\"", "prune": "./prune-gh-pages.sh", "i18n:push": "tx-push-src scratch-editor interface translations/en.json", "i18n:src": "rimraf ./translations/messages/src && babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/src ./translations/ && npm run i18n:push", From 9589e47700600156ed97f529e2185ac0e4362134 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 19 Feb 2020 16:10:09 -0500 Subject: [PATCH 1287/1971] Merge pull request #5465 from LLK/greenkeeper/scratch-l10n-3.7.20200218213016 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3a2d6067fd..32623295d0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-l10n": "3.7.20200212141356", + "scratch-l10n": "3.7.20200218213016", "scratch-blocks": "0.1.0-prerelease.1581428931", "scratch-paint": "0.2.0-prerelease.20200213174123", "scratch-render": "0.1.0-prerelease.20200205011809", From f9a6f9eb354218f02a75088b6a78fd74f3974964 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 19 Feb 2020 16:39:27 -0500 Subject: [PATCH 1288/1971] Merge pull request #5464 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1582033791 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 32623295d0..366513b1ef 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-l10n": "3.7.20200218213016", - "scratch-blocks": "0.1.0-prerelease.1581428931", + "scratch-blocks": "0.1.0-prerelease.1582033791", "scratch-paint": "0.2.0-prerelease.20200213174123", "scratch-render": "0.1.0-prerelease.20200205011809", "scratch-storage": "1.3.2", From e91664a26204d1714ece741d7f13c870363e714e Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 26 Feb 2020 16:06:43 -0500 Subject: [PATCH 1289/1971] Merge pull request #5476 from chrisgarrity/l10n-update-02-25 Update to latest l10n for translations --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 366513b1ef..e248ab52ab 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-l10n": "3.7.20200218213016", + "scratch-l10n": "3.7.20200225213156", "scratch-blocks": "0.1.0-prerelease.1582033791", "scratch-paint": "0.2.0-prerelease.20200213174123", "scratch-render": "0.1.0-prerelease.20200205011809", From fdc538d5a3ed52e0b6b25223ef45a8d476e3a572 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 27 Feb 2020 16:06:06 -0500 Subject: [PATCH 1290/1971] Merge pull request #5478 from fsih/updateRender Update render --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e248ab52ab..eb1e2cdd0c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-l10n": "3.7.20200225213156", "scratch-blocks": "0.1.0-prerelease.1582033791", "scratch-paint": "0.2.0-prerelease.20200213174123", - "scratch-render": "0.1.0-prerelease.20200205011809", + "scratch-render": "0.1.0-prerelease.20200227210101", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", "scratch-vm": "0.2.0-prerelease.20191227164934", From 62ab73f3b9d86a02a24bf4a5602a5a3b0c991312 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 27 Feb 2020 16:58:35 -0500 Subject: [PATCH 1291/1971] Merge pull request #5479 from LLK/updateVm022720 Update vm --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index eb1e2cdd0c..d342020a1b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20200227210101", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", - "scratch-vm": "0.2.0-prerelease.20191227164934", + "scratch-vm": "0.2.0-prerelease.20200227204654", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 3b9931b3ba8491b042f585d541cc336658d821a4 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 28 Feb 2020 13:01:11 -0500 Subject: [PATCH 1292/1971] Merge pull request #5484 from fsih/updateRender Update render to roll back pen line drawing change --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d342020a1b..f131e130da 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-l10n": "3.7.20200225213156", "scratch-blocks": "0.1.0-prerelease.1582033791", "scratch-paint": "0.2.0-prerelease.20200213174123", - "scratch-render": "0.1.0-prerelease.20200227210101", + "scratch-render": "0.1.0-prerelease.20200228152431", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", "scratch-vm": "0.2.0-prerelease.20200227204654", From b6657b7f5f5254acea2734d55adf2010da292d75 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 4 Mar 2020 16:54:56 -0500 Subject: [PATCH 1293/1971] Merge pull request #5492 from rschamp/bump-dependencies Bump scratch-blocks and scratch-l10n versions for release --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f131e130da..495f037e33 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,8 +108,8 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-l10n": "3.7.20200225213156", - "scratch-blocks": "0.1.0-prerelease.1582033791", + "scratch-blocks": "0.1.0-prerelease.1583243431", + "scratch-l10n": "3.7.20200303213115", "scratch-paint": "0.2.0-prerelease.20200213174123", "scratch-render": "0.1.0-prerelease.20200228152431", "scratch-storage": "1.3.2", From 5e4a95fdaa06d18688228de7b49afaac1db11dd2 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 10 Mar 2020 16:59:00 -0400 Subject: [PATCH 1294/1971] Merge pull request #5502 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20200310202129 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 495f037e33..043969bfde 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20200228152431", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", - "scratch-vm": "0.2.0-prerelease.20200227204654", + "scratch-vm": "0.2.0-prerelease.20200310202129", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From ea697a2595c531ea6518d53d5a5ad21999b41138 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Wed, 11 Mar 2020 15:31:09 -0400 Subject: [PATCH 1295/1971] Merge pull request #5503 from LLK/greenkeeper/scratch-l10n-3.7.20200310213125 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 043969bfde..aaef1ef105 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.1583243431", - "scratch-l10n": "3.7.20200303213115", + "scratch-l10n": "3.7.20200310213125", "scratch-paint": "0.2.0-prerelease.20200213174123", "scratch-render": "0.1.0-prerelease.20200228152431", "scratch-storage": "1.3.2", From 14eb2c228bc4a356d476d5ab0b6770579e64e1d1 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Wed, 11 Mar 2020 15:50:55 -0400 Subject: [PATCH 1296/1971] Merge pull request #5504 from picklesrus/update-scratch-blocks Updates scratch-blocks manually --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index aaef1ef105..6bf6022738 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-blocks": "0.1.0-prerelease.1583243431", + "scratch-blocks": "0.1.0-prerelease.1583868812", "scratch-l10n": "3.7.20200310213125", "scratch-paint": "0.2.0-prerelease.20200213174123", "scratch-render": "0.1.0-prerelease.20200228152431", From 6951e1760fc86758ecc2ef3984a88342abe6b270 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 17 Mar 2020 16:08:27 -0400 Subject: [PATCH 1297/1971] Merge pull request #5519 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20200317155559 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6bf6022738..b2bf99faca 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20200228152431", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", - "scratch-vm": "0.2.0-prerelease.20200310202129", + "scratch-vm": "0.2.0-prerelease.20200317155559", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 4638e745b56b12a894d8a6228cdd5f27385e6de8 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 18 Mar 2020 13:02:31 -0400 Subject: [PATCH 1298/1971] Merge pull request #5521 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20200318165714 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-vm to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b2bf99faca..1847be333b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20200228152431", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", - "scratch-vm": "0.2.0-prerelease.20200317155559", + "scratch-vm": "0.2.0-prerelease.20200318165714", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 570058291a160118dfa9cfb6af85e467b8e0f84d Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 18 Mar 2020 13:04:16 -0400 Subject: [PATCH 1299/1971] Merge pull request #5518 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1584453125 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1847be333b..58d5a8ff5b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-blocks": "0.1.0-prerelease.1583868812", + "scratch-blocks": "0.1.0-prerelease.1584453125", "scratch-l10n": "3.7.20200310213125", "scratch-paint": "0.2.0-prerelease.20200213174123", "scratch-render": "0.1.0-prerelease.20200228152431", From 8f10e89a074d55119d00c8aaefe295adedc4e1a7 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 18 Mar 2020 13:05:24 -0400 Subject: [PATCH 1300/1971] Merge pull request #5520 from LLK/greenkeeper/scratch-l10n-3.7.20200317213230 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 58d5a8ff5b..a4ae7ce778 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.1584453125", - "scratch-l10n": "3.7.20200310213125", + "scratch-l10n": "3.7.20200317213230", "scratch-paint": "0.2.0-prerelease.20200213174123", "scratch-render": "0.1.0-prerelease.20200228152431", "scratch-storage": "1.3.2", From bea41a6ce438447e7555637fe14a3042c446a04a Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 19 Mar 2020 14:19:18 -0400 Subject: [PATCH 1301/1971] Merge pull request #5526 from fsih/updateVm Update vm --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a4ae7ce778..d857f6612e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20200228152431", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", - "scratch-vm": "0.2.0-prerelease.20200318165714", + "scratch-vm": "0.2.0-prerelease.20200319175700", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From c5c23d0119a4921315f18eb2744f57021d363a41 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 25 Mar 2020 12:51:38 -0400 Subject: [PATCH 1302/1971] Merge pull request #5540 from LLK/greenkeeper/scratch-l10n-3.8.20200325112845 Greenkeeper/scratch l10n 3.8.20200325112845 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d857f6612e..8235b46e27 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.1584453125", - "scratch-l10n": "3.7.20200317213230", + "scratch-l10n": "3.8.20200325112845", "scratch-paint": "0.2.0-prerelease.20200213174123", "scratch-render": "0.1.0-prerelease.20200228152431", "scratch-storage": "1.3.2", From 91e6c6635c40adfe8bddc43fca694bc1ab0852d4 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 25 Mar 2020 14:48:41 -0400 Subject: [PATCH 1303/1971] Merge pull request #5541 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1585138708 Greenkeeper/scratch blocks 0.1.0 prerelease.1585138708 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8235b46e27..b0e2181b37 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-blocks": "0.1.0-prerelease.1584453125", + "scratch-blocks": "0.1.0-prerelease.1585138708", "scratch-l10n": "3.8.20200325112845", "scratch-paint": "0.2.0-prerelease.20200213174123", "scratch-render": "0.1.0-prerelease.20200228152431", From d1b780cbc65b2f14713d1f76b4ce9b949cdec7e5 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 2 Apr 2020 20:02:38 -0400 Subject: [PATCH 1304/1971] Merge pull request #5560 from fsih/updateStuff Update render and vm with contributor prs --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b0e2181b37..8dc1fdd284 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,10 +111,10 @@ "scratch-blocks": "0.1.0-prerelease.1585138708", "scratch-l10n": "3.8.20200325112845", "scratch-paint": "0.2.0-prerelease.20200213174123", - "scratch-render": "0.1.0-prerelease.20200228152431", + "scratch-render": "0.1.0-prerelease.20200402191852", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", - "scratch-vm": "0.2.0-prerelease.20200319175700", + "scratch-vm": "0.2.0-prerelease.20200402182733", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 52a7f27283d20992e6436365c3b3ec6be3e400e2 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 8 Apr 2020 07:36:11 -0400 Subject: [PATCH 1305/1971] Merge pull request #5569 from LLK/greenkeeper/scratch-l10n-3.8.20200407213553 Greenkeeper/scratch l10n 3.8.20200407213553 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8dc1fdd284..b42b28c4d9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.1585138708", - "scratch-l10n": "3.8.20200325112845", + "scratch-l10n": "3.8.20200407213553", "scratch-paint": "0.2.0-prerelease.20200213174123", "scratch-render": "0.1.0-prerelease.20200402191852", "scratch-storage": "1.3.2", From 55b836f76fcb67ca36ff4bb8aa6e07ec17585683 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 8 Apr 2020 15:32:49 -0400 Subject: [PATCH 1306/1971] Merge pull request #5570 from chrisgarrity/update-blocks-0408 Update scratch-blocks to the latest version --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b42b28c4d9..f2f0641ab8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-blocks": "0.1.0-prerelease.1585138708", + "scratch-blocks": "0.1.0-prerelease.1586372076", "scratch-l10n": "3.8.20200407213553", "scratch-paint": "0.2.0-prerelease.20200213174123", "scratch-render": "0.1.0-prerelease.20200402191852", From 2f044c48230fabb138dc91071dfa9003dc4f7c8b Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 9 Apr 2020 13:49:58 -0400 Subject: [PATCH 1307/1971] Merge pull request #5571 from fsih/updateRender update render to revert pen change --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f2f0641ab8..584319df0d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.1586372076", "scratch-l10n": "3.8.20200407213553", "scratch-paint": "0.2.0-prerelease.20200213174123", - "scratch-render": "0.1.0-prerelease.20200402191852", + "scratch-render": "0.1.0-prerelease.20200409163640", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", "scratch-vm": "0.2.0-prerelease.20200402182733", From 81ece6b8d3b91b97b7ee276515f3f579ee800ca5 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Wed, 15 Apr 2020 15:54:08 -0400 Subject: [PATCH 1308/1971] Merge pull request #5645 from LLK/greenkeeper/scratch-l10n-3.8.20200414213430 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 584319df0d..2872aba5ba 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.1586372076", - "scratch-l10n": "3.8.20200407213553", + "scratch-l10n": "3.8.20200414213430", "scratch-paint": "0.2.0-prerelease.20200213174123", "scratch-render": "0.1.0-prerelease.20200409163640", "scratch-storage": "1.3.2", From 79ec87d00323b7cdfb2065fbfde7755ec83e5b9f Mon Sep 17 00:00:00 2001 From: picklesrus Date: Thu, 16 Apr 2020 13:16:04 -0400 Subject: [PATCH 1309/1971] Merge pull request #5655 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.20200416142646 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-blocks to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2872aba5ba..64f8b848a8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-blocks": "0.1.0-prerelease.1586372076", + "scratch-blocks": "0.1.0-prerelease.20200416142646", "scratch-l10n": "3.8.20200414213430", "scratch-paint": "0.2.0-prerelease.20200213174123", "scratch-render": "0.1.0-prerelease.20200409163640", From c319ca0f3d2d9633ed39096a1334c91fabb8573a Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Wed, 22 Apr 2020 18:42:02 -0400 Subject: [PATCH 1310/1971] Merge pull request #5668 from LLK/greenkeeper/scratch-l10n-3.8.20200421213407 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 64f8b848a8..f0db723714 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.20200416142646", - "scratch-l10n": "3.8.20200414213430", + "scratch-l10n": "3.8.20200421213407", "scratch-paint": "0.2.0-prerelease.20200213174123", "scratch-render": "0.1.0-prerelease.20200409163640", "scratch-storage": "1.3.2", From 6fef1ddbdb3c9d0a1d851ae97f99f9817cc40328 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 22 Apr 2020 19:55:35 -0400 Subject: [PATCH 1311/1971] Merge pull request #5671 from fsih/updateRender Update render --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f0db723714..0812cfa4a2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200416142646", "scratch-l10n": "3.8.20200421213407", "scratch-paint": "0.2.0-prerelease.20200213174123", - "scratch-render": "0.1.0-prerelease.20200409163640", + "scratch-render": "0.1.0-prerelease.20200422235043", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", "scratch-vm": "0.2.0-prerelease.20200402182733", From 5aab2ae3617b96ae4a346c4af04f65b6d23d00ae Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Wed, 22 Apr 2020 20:16:09 -0400 Subject: [PATCH 1312/1971] Merge pull request #5669 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200421135214 Bump scratch-blocks from 0.1.0-prerelease.20200416142646 to 0.1.0-prerelease.20200421135214 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0812cfa4a2..60a66cb73e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-blocks": "0.1.0-prerelease.20200416142646", + "scratch-blocks": "0.1.0-prerelease.20200421135214", "scratch-l10n": "3.8.20200421213407", "scratch-paint": "0.2.0-prerelease.20200213174123", "scratch-render": "0.1.0-prerelease.20200422235043", From 4c32d4013cff1c00ca046690cef146c04c51d523 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 24 Apr 2020 22:20:02 -0400 Subject: [PATCH 1313/1971] Merge pull request #5677 from LLK/dependabot/npm_and_yarn/scratch-render-0.1.0-prerelease.20200424190354 Bump scratch-render from 0.1.0-prerelease.20200422235043 to 0.1.0-prerelease.20200424190354 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 60a66cb73e..8fb8966d30 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200421135214", "scratch-l10n": "3.8.20200421213407", "scratch-paint": "0.2.0-prerelease.20200213174123", - "scratch-render": "0.1.0-prerelease.20200422235043", + "scratch-render": "0.1.0-prerelease.20200424190354", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", "scratch-vm": "0.2.0-prerelease.20200402182733", From 45e9e974cd2738f1bf298b5c45d0af829ad16796 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 29 Apr 2020 18:33:44 -0400 Subject: [PATCH 1314/1971] Merge pull request #5689 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.8.20200428213506 Bump scratch-l10n from 3.8.20200421213407 to 3.8.20200428213506 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8fb8966d30..7a5bf3139c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.20200421135214", - "scratch-l10n": "3.8.20200421213407", + "scratch-l10n": "3.8.20200428213506", "scratch-paint": "0.2.0-prerelease.20200213174123", "scratch-render": "0.1.0-prerelease.20200424190354", "scratch-storage": "1.3.2", From 207b71d8529fdfda4dd1dcc602dce77e3e8f8b3b Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 29 Apr 2020 18:34:22 -0400 Subject: [PATCH 1315/1971] Merge pull request #5684 from LLK/dependabot/npm_and_yarn/scratch-render-0.1.0-prerelease.20200427175234 Bump scratch-render from 0.1.0-prerelease.20200424190354 to 0.1.0-prerelease.20200427175234 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7a5bf3139c..cd76c5cdc5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200421135214", "scratch-l10n": "3.8.20200428213506", "scratch-paint": "0.2.0-prerelease.20200213174123", - "scratch-render": "0.1.0-prerelease.20200424190354", + "scratch-render": "0.1.0-prerelease.20200427175234", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", "scratch-vm": "0.2.0-prerelease.20200402182733", From e304d650ef644a93a755df2aa0f9ace4e76b3401 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 29 Apr 2020 18:52:56 -0400 Subject: [PATCH 1316/1971] Merge pull request #5695 from fsih/updatePaint Update paint --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cd76c5cdc5..53fd9fece3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.20200421135214", "scratch-l10n": "3.8.20200428213506", - "scratch-paint": "0.2.0-prerelease.20200213174123", + "scratch-paint": "0.2.0-prerelease.20200429220710", "scratch-render": "0.1.0-prerelease.20200427175234", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", From 3ef03ef82e7309e2a30f68b439a64bf551d1fc9c Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 30 Apr 2020 12:00:51 -0400 Subject: [PATCH 1317/1971] Merge pull request #5696 from fsih/updatePaint Update paint --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 53fd9fece3..ec03d1f9e6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.20200421135214", "scratch-l10n": "3.8.20200428213506", - "scratch-paint": "0.2.0-prerelease.20200429220710", + "scratch-paint": "0.2.0-prerelease.20200430155806", "scratch-render": "0.1.0-prerelease.20200427175234", "scratch-storage": "1.3.2", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", From 106c6ed2ea70ce58758041367b29ca42493dc085 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford <7019101+cwillisf@users.noreply.github.com> Date: Wed, 6 May 2020 12:11:07 -0700 Subject: [PATCH 1318/1971] Merge pull request #5709 from LLK/dependabot/npm_and_yarn/scratch-storage-1.3.3 Bump scratch-storage from 1.3.2 to 1.3.3 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ec03d1f9e6..bbea30918f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -112,7 +112,7 @@ "scratch-l10n": "3.8.20200428213506", "scratch-paint": "0.2.0-prerelease.20200430155806", "scratch-render": "0.1.0-prerelease.20200427175234", - "scratch-storage": "1.3.2", + "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", "scratch-vm": "0.2.0-prerelease.20200402182733", "selenium-webdriver": "3.6.0", From 3fa6a480589482c618ece0f1233cb9ef79f01c78 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 6 May 2020 17:01:10 -0400 Subject: [PATCH 1319/1971] Merge pull request #5708 from LLK/greenkeeper/scratch-l10n-3.8.20200505213509 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-l10n to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index bbea30918f..e6c1a28936 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.20200421135214", - "scratch-l10n": "3.8.20200428213506", + "scratch-l10n": "3.8.20200505213509", "scratch-paint": "0.2.0-prerelease.20200430155806", "scratch-render": "0.1.0-prerelease.20200427175234", "scratch-storage": "1.3.3", From 4cbe3ccb7f9ef3de58554cbf4d1dc0703e1718b6 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 6 May 2020 17:11:24 -0400 Subject: [PATCH 1320/1971] Merge pull request #5712 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20200506204308 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e6c1a28936..0b6e80109e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200421135214", "scratch-l10n": "3.8.20200505213509", "scratch-paint": "0.2.0-prerelease.20200430155806", - "scratch-render": "0.1.0-prerelease.20200427175234", + "scratch-render": "0.1.0-prerelease.20200506204308", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", "scratch-vm": "0.2.0-prerelease.20200402182733", From ce631b28a14041a89019b24aa44450bbe9f41654 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 6 May 2020 18:55:51 -0400 Subject: [PATCH 1321/1971] Merge pull request #5713 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20200506211808 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0b6e80109e..c31da87893 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.20200421135214", "scratch-l10n": "3.8.20200505213509", - "scratch-paint": "0.2.0-prerelease.20200430155806", + "scratch-paint": "0.2.0-prerelease.20200506211808", "scratch-render": "0.1.0-prerelease.20200506204308", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", From f7c71dc535f1c948fc1f8003e86d9d35d627d9c1 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 7 May 2020 15:46:05 -0400 Subject: [PATCH 1322/1971] Merge pull request #5718 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20200507183648 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-svg-renderer to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c31da87893..dd0178f07f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -113,7 +113,7 @@ "scratch-paint": "0.2.0-prerelease.20200506211808", "scratch-render": "0.1.0-prerelease.20200506204308", "scratch-storage": "1.3.3", - "scratch-svg-renderer": "0.2.0-prerelease.20200205003400", + "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", "scratch-vm": "0.2.0-prerelease.20200402182733", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 6ae4acb6878f0120da523a754b4019dfe17eca00 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 7 May 2020 17:29:11 -0400 Subject: [PATCH 1323/1971] Merge pull request #5723 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20200507195338 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index dd0178f07f..404bb0b9c3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.20200421135214", "scratch-l10n": "3.8.20200505213509", - "scratch-paint": "0.2.0-prerelease.20200506211808", + "scratch-paint": "0.2.0-prerelease.20200507195338", "scratch-render": "0.1.0-prerelease.20200506204308", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", From ea81f2c8a94ab65457fe5664a2f84f75c53e26c6 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 7 May 2020 17:30:43 -0400 Subject: [PATCH 1324/1971] Merge pull request #5722 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20200507194753 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-render to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 404bb0b9c3..77033e4e74 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200421135214", "scratch-l10n": "3.8.20200505213509", "scratch-paint": "0.2.0-prerelease.20200507195338", - "scratch-render": "0.1.0-prerelease.20200506204308", + "scratch-render": "0.1.0-prerelease.20200507194753", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", "scratch-vm": "0.2.0-prerelease.20200402182733", From 9006f7d66e08ef5b40dbc949efc8784ee8f6b8a8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 11 May 2020 21:07:00 +0000 Subject: [PATCH 1325/1971] Merge pull request #5733 from LLK/dependabot/npm_and_yarn/scratch-render-0.1.0-prerelease.20200508204538 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 77033e4e74..2a29c31f2b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200421135214", "scratch-l10n": "3.8.20200505213509", "scratch-paint": "0.2.0-prerelease.20200507195338", - "scratch-render": "0.1.0-prerelease.20200507194753", + "scratch-render": "0.1.0-prerelease.20200508204538", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", "scratch-vm": "0.2.0-prerelease.20200402182733", From 2b4bcaccafeffa34dc14ee48472c3520cb58ba62 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 11 May 2020 21:39:52 +0000 Subject: [PATCH 1326/1971] Merge pull request #5737 from LLK/dependabot/npm_and_yarn/scratch-render-0.1.0-prerelease.20200511203322 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2a29c31f2b..7643a9df89 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200421135214", "scratch-l10n": "3.8.20200505213509", "scratch-paint": "0.2.0-prerelease.20200507195338", - "scratch-render": "0.1.0-prerelease.20200508204538", + "scratch-render": "0.1.0-prerelease.20200511203322", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", "scratch-vm": "0.2.0-prerelease.20200402182733", From bfd11fa5aa9d3d589b89ba10f597eb76f9bafc55 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 12 May 2020 00:17:55 +0000 Subject: [PATCH 1327/1971] Merge pull request #5719 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200507190920 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7643a9df89..4795f5fb33 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-blocks": "0.1.0-prerelease.20200421135214", + "scratch-blocks": "0.1.0-prerelease.20200507190920", "scratch-l10n": "3.8.20200505213509", "scratch-paint": "0.2.0-prerelease.20200507195338", "scratch-render": "0.1.0-prerelease.20200511203322", From e24ac5392ca88c50c01d4fd5ea414b84077d25d4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 12 May 2020 13:40:11 +0000 Subject: [PATCH 1328/1971] Merge pull request #5739 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.9.20200512112117 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4795f5fb33..a5446de8ca 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.20200507190920", - "scratch-l10n": "3.8.20200505213509", + "scratch-l10n": "3.9.20200512112117", "scratch-paint": "0.2.0-prerelease.20200507195338", "scratch-render": "0.1.0-prerelease.20200511203322", "scratch-storage": "1.3.3", From 12abdf300f1cd8e8294b9bb03482e8ad14a6c40f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 12 May 2020 20:39:19 +0000 Subject: [PATCH 1329/1971] Merge pull request #5743 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200512201140 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a5446de8ca..1c3fd155ca 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-blocks": "0.1.0-prerelease.20200507190920", + "scratch-blocks": "0.1.0-prerelease.20200512201140", "scratch-l10n": "3.9.20200512112117", "scratch-paint": "0.2.0-prerelease.20200507195338", "scratch-render": "0.1.0-prerelease.20200511203322", From 201621dae7bb8e22472208436e7a35d33f97248b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 12 May 2020 21:07:32 +0000 Subject: [PATCH 1330/1971] Merge pull request #5744 from LLK/dependabot/npm_and_yarn/scratch-paint-0.2.0-prerelease.20200512201624 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1c3fd155ca..c451877f8c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.20200512201140", "scratch-l10n": "3.9.20200512112117", - "scratch-paint": "0.2.0-prerelease.20200507195338", + "scratch-paint": "0.2.0-prerelease.20200512201624", "scratch-render": "0.1.0-prerelease.20200511203322", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", From 184b5d616a822fa59c98620fe4a50ed9a37881f8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 12 May 2020 21:19:26 +0000 Subject: [PATCH 1331/1971] Merge pull request #5749 from LLK/dependabot/npm_and_yarn/scratch-vm-0.2.0-prerelease.20200512204241 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c451877f8c..7429c1261f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20200511203322", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", - "scratch-vm": "0.2.0-prerelease.20200402182733", + "scratch-vm": "0.2.0-prerelease.20200512204241", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From f26c9b48c0dc252bc7716bebb744b908834046f7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 12 May 2020 21:52:01 +0000 Subject: [PATCH 1332/1971] Merge pull request #5747 from LLK/dependabot/npm_and_yarn/scratch-render-0.1.0-prerelease.20200512203149 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7429c1261f..12ff2603f1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200512201140", "scratch-l10n": "3.9.20200512112117", "scratch-paint": "0.2.0-prerelease.20200512201624", - "scratch-render": "0.1.0-prerelease.20200511203322", + "scratch-render": "0.1.0-prerelease.20200512203149", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", "scratch-vm": "0.2.0-prerelease.20200512204241", From d6b87888e89dbafcd8d0c2944d2a2e957664d373 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 13 May 2020 00:47:32 +0000 Subject: [PATCH 1333/1971] Merge pull request #5753 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.9.20200512220101 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 12ff2603f1..920bba5dc3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.20200512201140", - "scratch-l10n": "3.9.20200512112117", + "scratch-l10n": "3.9.20200512220101", "scratch-paint": "0.2.0-prerelease.20200512201624", "scratch-render": "0.1.0-prerelease.20200512203149", "scratch-storage": "1.3.3", From e4a1db172d73a8cb0b33d73f7918e7cfbfc5afd7 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 14 May 2020 14:44:33 -0400 Subject: [PATCH 1334/1971] Merge pull request #5759 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20200514182959 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update scratch-paint to the latest version 🚀 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 920bba5dc3..2c8814e1c0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.20200512201140", "scratch-l10n": "3.9.20200512220101", - "scratch-paint": "0.2.0-prerelease.20200512201624", + "scratch-paint": "0.2.0-prerelease.20200514182959", "scratch-render": "0.1.0-prerelease.20200512203149", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", From 7715f26b294d2fa732c7c98cff927dab425d882b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 15 May 2020 19:34:25 +0000 Subject: [PATCH 1335/1971] Merge pull request #5763 from LLK/dependabot/npm_and_yarn/scratch-paint-0.2.0-prerelease.20200515190220 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2c8814e1c0..6f67fd61c4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.20200512201140", "scratch-l10n": "3.9.20200512220101", - "scratch-paint": "0.2.0-prerelease.20200514182959", + "scratch-paint": "0.2.0-prerelease.20200515190220", "scratch-render": "0.1.0-prerelease.20200512203149", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", From 5c939d4a17b52ba623e270c7768fc35c62e03d76 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 16 May 2020 00:50:06 +0000 Subject: [PATCH 1336/1971] Merge pull request #5764 from LLK/dependabot/npm_and_yarn/scratch-paint-0.2.0-prerelease.20200516002830 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6f67fd61c4..a9239b906d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.20200512201140", "scratch-l10n": "3.9.20200512220101", - "scratch-paint": "0.2.0-prerelease.20200515190220", + "scratch-paint": "0.2.0-prerelease.20200516002830", "scratch-render": "0.1.0-prerelease.20200512203149", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", From b55a28c4c717bd461893cc263a12bb31381f5361 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 19 May 2020 02:46:16 +0000 Subject: [PATCH 1337/1971] Merge pull request #5779 from LLK/dependabot/npm_and_yarn/scratch-paint-0.2.0-prerelease.20200519001920 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a9239b906d..3e2c4ef145 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.20200512201140", "scratch-l10n": "3.9.20200512220101", - "scratch-paint": "0.2.0-prerelease.20200516002830", + "scratch-paint": "0.2.0-prerelease.20200519001920", "scratch-render": "0.1.0-prerelease.20200512203149", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", From e0ee7959e71a518d77db0feecb995de8ba4cc31e Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford <7019101+cwillisf@users.noreply.github.com> Date: Wed, 20 May 2020 11:15:51 -0700 Subject: [PATCH 1338/1971] Merge pull request #5434 from cwillisf/add-about-menu-button Add about menu button --- packages/scratch-gui/package.json | 3 ++- .../src/components/menu-bar/menu-bar.jsx | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3e2c4ef145..43970865e1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -139,7 +139,8 @@ ], "moduleNameMapper": { "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/test/__mocks__/fileMock.js", - "\\.(css|less)$": "/test/__mocks__/styleMock.js" + "\\.(css|less)$": "/test/__mocks__/styleMock.js", + "editor-msgs(\\.js)?$": "/test/__mocks__/editor-msgs-mock.js" } } } diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 98bc128a92..fec3d576e1 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -69,6 +69,7 @@ import profileIcon from './icon--profile.png'; import remixIcon from './icon--remix.svg'; import dropdownCaret from './dropdown-caret.svg'; import languageIcon from '../language-selector/language-icon.svg'; +import aboutIcon from './icon--about.svg'; import scratchLogo from './scratch-logo.svg'; @@ -141,6 +142,19 @@ MenuItemTooltip.propTypes = { isRtl: PropTypes.bool }; +const AboutButton = props => ( + ); + // Show the About button only if we have a handler for it (like in the desktop app) + const aboutButton = this.props.onClickAbout ? : null; return ( )}
+ + {aboutButton} ); } @@ -722,6 +740,7 @@ MenuBar.propTypes = { locale: PropTypes.string.isRequired, loginMenuOpen: PropTypes.bool, logo: PropTypes.string, + onClickAbout: PropTypes.func, onClickAccount: PropTypes.func, onClickEdit: PropTypes.func, onClickFile: PropTypes.func, From f8d8a66c924741ef4db152fad028d52cfabccc4d Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 20 May 2020 15:21:11 -0400 Subject: [PATCH 1339/1971] Merge pull request #5794 from chrisgarrity/update-scratch-dependencies Pull the latest scratch-* dependencies --- packages/scratch-gui/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 43970865e1..c476d9a477 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,13 +108,13 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-blocks": "0.1.0-prerelease.20200512201140", - "scratch-l10n": "3.9.20200512220101", - "scratch-paint": "0.2.0-prerelease.20200519001920", + "scratch-blocks": "0.1.0-prerelease.20200520151625", + "scratch-l10n": "3.9.20200519213537", + "scratch-paint": "0.2.0-prerelease.20200519194740", "scratch-render": "0.1.0-prerelease.20200512203149", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", - "scratch-vm": "0.2.0-prerelease.20200512204241", + "scratch-vm": "0.2.0-prerelease.20200519202709", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From c7e5892f660ff65ca1f24dea5e1cfb580c9c66f8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 22 May 2020 20:54:29 +0000 Subject: [PATCH 1340/1971] Merge pull request #5804 from LLK/dependabot/npm_and_yarn/scratch-vm-0.2.0-prerelease.20200522203113 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c476d9a477..fad581ae13 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20200512203149", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", - "scratch-vm": "0.2.0-prerelease.20200519202709", + "scratch-vm": "0.2.0-prerelease.20200522203113", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From b985d45de1011bbbf09b2bab59370c22a2aafbc2 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 26 May 2020 15:02:08 +0000 Subject: [PATCH 1341/1971] Merge pull request #5807 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200526135421 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index fad581ae13..d4123a534d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", - "scratch-blocks": "0.1.0-prerelease.20200520151625", + "scratch-blocks": "0.1.0-prerelease.20200526135421", "scratch-l10n": "3.9.20200519213537", "scratch-paint": "0.2.0-prerelease.20200519194740", "scratch-render": "0.1.0-prerelease.20200512203149", From 6b406192590a537a7505c9c6de2951ce61f8e8ae Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 26 May 2020 21:58:03 +0000 Subject: [PATCH 1342/1971] Merge pull request #5809 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.9.20200526213608 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d4123a534d..8a1e804a84 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.20200526135421", - "scratch-l10n": "3.9.20200519213537", + "scratch-l10n": "3.9.20200526213608", "scratch-paint": "0.2.0-prerelease.20200519194740", "scratch-render": "0.1.0-prerelease.20200512203149", "scratch-storage": "1.3.3", From 780ac644aaa306306891e10b598558cc7edab667 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 27 May 2020 20:08:40 +0000 Subject: [PATCH 1343/1971] Merge pull request #5815 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.9.20200527185330 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8a1e804a84..1386f6d961 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.20200526135421", - "scratch-l10n": "3.9.20200526213608", + "scratch-l10n": "3.9.20200527185330", "scratch-paint": "0.2.0-prerelease.20200519194740", "scratch-render": "0.1.0-prerelease.20200512203149", "scratch-storage": "1.3.3", From b605e7f523d9fe88ce52bcfc806ca2de25d98750 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 27 May 2020 20:39:28 +0000 Subject: [PATCH 1344/1971] Merge pull request #5816 from LLK/dependabot/npm_and_yarn/scratch-vm-0.2.0-prerelease.20200527195112 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1386f6d961..6a6b7ca3ac 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20200512203149", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", - "scratch-vm": "0.2.0-prerelease.20200522203113", + "scratch-vm": "0.2.0-prerelease.20200527195112", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 4eced8fa9421d8375f844ffd6cabc308828e1bed Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 27 May 2020 20:59:17 +0000 Subject: [PATCH 1345/1971] Merge pull request #5817 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.9.20200527202656 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 6a6b7ca3ac..e1dcc48635 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.20200526135421", - "scratch-l10n": "3.9.20200527185330", + "scratch-l10n": "3.9.20200527202656", "scratch-paint": "0.2.0-prerelease.20200519194740", "scratch-render": "0.1.0-prerelease.20200512203149", "scratch-storage": "1.3.3", From 0403a7926e719686188c7d79a1db3ea680a606f4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 28 May 2020 14:56:37 +0000 Subject: [PATCH 1346/1971] Merge pull request #5825 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.9.20200528120215 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e1dcc48635..4026c64f3e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.20200526135421", - "scratch-l10n": "3.9.20200527202656", + "scratch-l10n": "3.9.20200528120215", "scratch-paint": "0.2.0-prerelease.20200519194740", "scratch-render": "0.1.0-prerelease.20200512203149", "scratch-storage": "1.3.3", From 9b46ae74e2a4f937d9425cb79c90eb8a736f7d93 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 28 May 2020 19:04:36 +0000 Subject: [PATCH 1347/1971] Merge pull request #5827 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.9.20200528181319 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4026c64f3e..43ca7e5c5d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20190925183642", "scratch-blocks": "0.1.0-prerelease.20200526135421", - "scratch-l10n": "3.9.20200528120215", + "scratch-l10n": "3.9.20200528181319", "scratch-paint": "0.2.0-prerelease.20200519194740", "scratch-render": "0.1.0-prerelease.20200512203149", "scratch-storage": "1.3.3", From 784679d60bba5b41f4f9d3f41c5c7df5acb8c03a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 28 May 2020 20:20:26 +0000 Subject: [PATCH 1348/1971] Merge pull request #5830 from LLK/dependabot/npm_and_yarn/scratch-audio-0.1.0-prerelease.20200528195344 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 43ca7e5c5d..8fc1b5d3d4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -107,7 +107,7 @@ "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", "rimraf": "^2.6.1", - "scratch-audio": "0.1.0-prerelease.20190925183642", + "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200526135421", "scratch-l10n": "3.9.20200528181319", "scratch-paint": "0.2.0-prerelease.20200519194740", From cc13c4ccfb41af749e0dbaa7d7b0f56a3a98fca1 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 28 May 2020 20:39:22 +0000 Subject: [PATCH 1349/1971] Merge pull request #5829 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200528193139 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8fc1b5d3d4..e298223788 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200526135421", + "scratch-blocks": "0.1.0-prerelease.20200528193139", "scratch-l10n": "3.9.20200528181319", "scratch-paint": "0.2.0-prerelease.20200519194740", "scratch-render": "0.1.0-prerelease.20200512203149", From a917aa7d707e2e1c70b2ab6fce47183473d7cc59 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 29 May 2020 19:07:15 +0000 Subject: [PATCH 1350/1971] Merge pull request #5833 from LLK/dependabot/npm_and_yarn/scratch-paint-0.2.0-prerelease.20200529184518 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e298223788..b9378a093e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200528193139", "scratch-l10n": "3.9.20200528181319", - "scratch-paint": "0.2.0-prerelease.20200519194740", + "scratch-paint": "0.2.0-prerelease.20200529184518", "scratch-render": "0.1.0-prerelease.20200512203149", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", From fa291dd4e91badf53e37e3797b8e437ccbf08db5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 2 Jun 2020 04:19:36 +0000 Subject: [PATCH 1351/1971] Merge pull request #5841 from LLK/dependabot/npm_and_yarn/scratch-paint-0.2.0-prerelease.20200602002610 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b9378a093e..d4fffb08c9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200528193139", "scratch-l10n": "3.9.20200528181319", - "scratch-paint": "0.2.0-prerelease.20200529184518", + "scratch-paint": "0.2.0-prerelease.20200602002610", "scratch-render": "0.1.0-prerelease.20200512203149", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", From 6c778b0fa21330440b481df2c45a3028194b9737 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 2 Jun 2020 16:09:36 +0000 Subject: [PATCH 1352/1971] Merge pull request #5842 from LLK/dependabot/npm_and_yarn/scratch-vm-0.2.0-prerelease.20200602151852 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d4fffb08c9..31bf60a1c9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20200512203149", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", - "scratch-vm": "0.2.0-prerelease.20200527195112", + "scratch-vm": "0.2.0-prerelease.20200602151852", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 85fcc9f4b177799f6ba889aeabfc6026888e3b3c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 2 Jun 2020 20:48:11 +0000 Subject: [PATCH 1353/1971] Merge pull request #5845 from LLK/dependabot/npm_and_yarn/scratch-paint-0.2.0-prerelease.20200602154954 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 31bf60a1c9..440e91a142 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200528193139", "scratch-l10n": "3.9.20200528181319", - "scratch-paint": "0.2.0-prerelease.20200602002610", + "scratch-paint": "0.2.0-prerelease.20200602154954", "scratch-render": "0.1.0-prerelease.20200512203149", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", From 3160599c704c0d7f5f62974b4d62be50888e1781 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 2 Jun 2020 22:08:28 +0000 Subject: [PATCH 1354/1971] Merge pull request #5846 from LLK/dependabot/npm_and_yarn/scratch-render-0.1.0-prerelease.20200602200702 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 440e91a142..fcffac09a2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200528193139", "scratch-l10n": "3.9.20200528181319", "scratch-paint": "0.2.0-prerelease.20200602154954", - "scratch-render": "0.1.0-prerelease.20200512203149", + "scratch-render": "0.1.0-prerelease.20200602200702", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", "scratch-vm": "0.2.0-prerelease.20200602151852", From a1fa27e48df36f504b35fdbb4d2cea4e25574de6 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2020 18:35:42 +0000 Subject: [PATCH 1355/1971] Merge pull request #5849 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.9.20200603151351 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index fcffac09a2..a448ca8799 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200528193139", - "scratch-l10n": "3.9.20200528181319", + "scratch-l10n": "3.9.20200603151351", "scratch-paint": "0.2.0-prerelease.20200602154954", "scratch-render": "0.1.0-prerelease.20200602200702", "scratch-storage": "1.3.3", From 749bdecee3f596774963400892c7f4a4f31be9b8 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 3 Jun 2020 16:36:56 -0400 Subject: [PATCH 1356/1971] Merge pull request #5851 from LLK/dependabot/npm_and_yarn/scratch-paint-0.2.0-prerelease.20200603154236 Bump scratch-paint from 0.2.0-prerelease.20200602154954 to 0.2.0-prerelease.20200603154236 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a448ca8799..f6e1ae461b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200528193139", "scratch-l10n": "3.9.20200603151351", - "scratch-paint": "0.2.0-prerelease.20200602154954", + "scratch-paint": "0.2.0-prerelease.20200603154236", "scratch-render": "0.1.0-prerelease.20200602200702", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", From f742cfabdf09010d4a83c9ad0c48bf2fa157265d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2020 02:05:26 +0000 Subject: [PATCH 1357/1971] Merge pull request #5854 from LLK/dependabot/npm_and_yarn/scratch-paint-0.2.0-prerelease.20200604013720 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f6e1ae461b..3f817459ef 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200528193139", "scratch-l10n": "3.9.20200603151351", - "scratch-paint": "0.2.0-prerelease.20200603154236", + "scratch-paint": "0.2.0-prerelease.20200604013720", "scratch-render": "0.1.0-prerelease.20200602200702", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", From 97d033bf7a37e8468ce04b19508a3768fe79ff47 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2020 21:45:34 +0000 Subject: [PATCH 1358/1971] Merge pull request #5857 from LLK/dependabot/npm_and_yarn/scratch-paint-0.2.0-prerelease.20200604192759 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3f817459ef..fc7375f685 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200528193139", "scratch-l10n": "3.9.20200603151351", - "scratch-paint": "0.2.0-prerelease.20200604013720", + "scratch-paint": "0.2.0-prerelease.20200604192759", "scratch-render": "0.1.0-prerelease.20200602200702", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", From e0dfe6203b7875de72fc96c3205ac102ab4fb16a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2020 12:58:23 +0000 Subject: [PATCH 1359/1971] Merge pull request #5858 from LLK/dependabot/npm_and_yarn/scratch-svg-renderer-0.2.0-prerelease.20200604203226 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index fc7375f685..5317a865ed 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -113,7 +113,7 @@ "scratch-paint": "0.2.0-prerelease.20200604192759", "scratch-render": "0.1.0-prerelease.20200602200702", "scratch-storage": "1.3.3", - "scratch-svg-renderer": "0.2.0-prerelease.20200507183648", + "scratch-svg-renderer": "0.2.0-prerelease.20200604203226", "scratch-vm": "0.2.0-prerelease.20200602151852", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 13c62cf31ac3ce5b238c8b9954095dedcabe74f9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 9 Jun 2020 15:16:23 +0000 Subject: [PATCH 1360/1971] Merge pull request #5868 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200609135544 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5317a865ed..a8ecbaec42 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200528193139", + "scratch-blocks": "0.1.0-prerelease.20200609135544", "scratch-l10n": "3.9.20200603151351", "scratch-paint": "0.2.0-prerelease.20200604192759", "scratch-render": "0.1.0-prerelease.20200602200702", From 74c7c50db09d0e745f2c7e94cf9857e5afabb4dd Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 9 Jun 2020 15:31:16 +0000 Subject: [PATCH 1361/1971] Merge pull request #5859 from LLK/dependabot/npm_and_yarn/scratch-render-0.1.0-prerelease.20200604210444 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a8ecbaec42..7171c1858e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200609135544", "scratch-l10n": "3.9.20200603151351", "scratch-paint": "0.2.0-prerelease.20200604192759", - "scratch-render": "0.1.0-prerelease.20200602200702", + "scratch-render": "0.1.0-prerelease.20200604210444", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200604203226", "scratch-vm": "0.2.0-prerelease.20200602151852", From 8522f2bc4be94da203c29cd4a0661927b73e9988 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 9 Jun 2020 16:41:24 -0400 Subject: [PATCH 1362/1971] Merge pull request #5869 from fsih/updateDeps Update render and svg-renderer with changes that Dependabot missed --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7171c1858e..8de93410b5 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,8 +110,8 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200609135544", "scratch-l10n": "3.9.20200603151351", - "scratch-paint": "0.2.0-prerelease.20200604192759", - "scratch-render": "0.1.0-prerelease.20200604210444", + "scratch-paint": "0.2.0-prerelease.20200609172344", + "scratch-render": "0.1.0-prerelease.20200609200715", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200604203226", "scratch-vm": "0.2.0-prerelease.20200602151852", From fc814d3bd5fca5fa409262304bb33c813d32b7ec Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 10 Jun 2020 13:54:39 +0000 Subject: [PATCH 1363/1971] Merge pull request #5870 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.9.20200609200912 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8de93410b5..0d7adcdc00 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200609135544", - "scratch-l10n": "3.9.20200603151351", + "scratch-l10n": "3.9.20200609200912", "scratch-paint": "0.2.0-prerelease.20200609172344", "scratch-render": "0.1.0-prerelease.20200609200715", "scratch-storage": "1.3.3", From 847cc5f8321111319fddb7e20fd8e3e84ec28e6e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 10 Jun 2020 14:05:01 +0000 Subject: [PATCH 1364/1971] Merge pull request #5874 from LLK/dependabot/npm_and_yarn/scratch-svg-renderer-0.2.0-prerelease.20200609210443 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0d7adcdc00..4123097ebf 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -113,7 +113,7 @@ "scratch-paint": "0.2.0-prerelease.20200609172344", "scratch-render": "0.1.0-prerelease.20200609200715", "scratch-storage": "1.3.3", - "scratch-svg-renderer": "0.2.0-prerelease.20200604203226", + "scratch-svg-renderer": "0.2.0-prerelease.20200609210443", "scratch-vm": "0.2.0-prerelease.20200602151852", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From a84dc3d80fa63aa0f648e6a52e954ba98d9699bf Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 10 Jun 2020 18:51:18 -0400 Subject: [PATCH 1365/1971] Merge pull request #5879 from fsih/updateDeps Update to revert svg-render change --- packages/scratch-gui/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 4123097ebf..a50aa55936 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,10 +110,10 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200609135544", "scratch-l10n": "3.9.20200609200912", - "scratch-paint": "0.2.0-prerelease.20200609172344", - "scratch-render": "0.1.0-prerelease.20200609200715", + "scratch-paint": "0.2.0-prerelease.20200610221942", + "scratch-render": "0.1.0-prerelease.20200610224308", "scratch-storage": "1.3.3", - "scratch-svg-renderer": "0.2.0-prerelease.20200609210443", + "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", "scratch-vm": "0.2.0-prerelease.20200602151852", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", From 1e7f4cbe624599fa3e90f2800570b7c769fb91e7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2020 16:01:16 +0000 Subject: [PATCH 1366/1971] Merge pull request #5889 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.9.20200615152937 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a50aa55936..3e7a3e4901 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200609135544", - "scratch-l10n": "3.9.20200609200912", + "scratch-l10n": "3.9.20200615152937", "scratch-paint": "0.2.0-prerelease.20200610221942", "scratch-render": "0.1.0-prerelease.20200610224308", "scratch-storage": "1.3.3", From 228729c74eb1e374a39c7c6acd767d69b1a27663 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2020 16:21:01 +0000 Subject: [PATCH 1367/1971] Merge pull request #5892 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200615150854 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3e7a3e4901..a79d48f249 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200609135544", + "scratch-blocks": "0.1.0-prerelease.20200615150854", "scratch-l10n": "3.9.20200615152937", "scratch-paint": "0.2.0-prerelease.20200610221942", "scratch-render": "0.1.0-prerelease.20200610224308", From 17091a755e97e8886ef17500a4de31d832b3b02d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2020 16:46:57 +0000 Subject: [PATCH 1368/1971] Merge pull request #5890 from LLK/dependabot/npm_and_yarn/scratch-paint-0.2.0-prerelease.20200615150428 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a79d48f249..d6e845d4c8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200615150854", "scratch-l10n": "3.9.20200615152937", - "scratch-paint": "0.2.0-prerelease.20200610221942", + "scratch-paint": "0.2.0-prerelease.20200615150428", "scratch-render": "0.1.0-prerelease.20200610224308", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", From 7b5d07a4e48ed7915b427eba0ddb8fdfb763e027 Mon Sep 17 00:00:00 2001 From: Bryce Taylor Date: Tue, 16 Jun 2020 09:52:25 -0400 Subject: [PATCH 1369/1971] Merge pull request #5885 from BryceLTaylor/update-localization-test Update localization test --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d6e845d4c8..90cacf4dd7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -44,7 +44,7 @@ "babel-loader": "^8.0.4", "base64-loader": "1.0.0", "bowser": "1.9.4", - "chromedriver": "80.0.0", + "chromedriver": "83.0.0", "classnames": "2.2.6", "computed-style-to-inline-style": "3.0.0", "copy-webpack-plugin": "^4.5.1", From 464a144f65f7a4fca8fc1a65c8f370933f1718f9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2020 16:28:21 +0000 Subject: [PATCH 1370/1971] Merge pull request #5893 from LLK/dependabot/npm_and_yarn/scratch-render-0.1.0-prerelease.20200615201428 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 90cacf4dd7..dd05d8e2db 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200615150854", "scratch-l10n": "3.9.20200615152937", "scratch-paint": "0.2.0-prerelease.20200615150428", - "scratch-render": "0.1.0-prerelease.20200610224308", + "scratch-render": "0.1.0-prerelease.20200615201428", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", "scratch-vm": "0.2.0-prerelease.20200602151852", From 65e9ae5171716d64dc33ba769bfa92be484396ed Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2020 16:41:35 +0000 Subject: [PATCH 1371/1971] Merge pull request #5884 from LLK/dependabot/npm_and_yarn/scratch-vm-0.2.0-prerelease.20200611202828 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index dd05d8e2db..ac86c4c4e0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20200615201428", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", - "scratch-vm": "0.2.0-prerelease.20200602151852", + "scratch-vm": "0.2.0-prerelease.20200611202828", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From a3b96bfd673a795536bac4587212281dbc8a37e3 Mon Sep 17 00:00:00 2001 From: adroitwhiz Date: Tue, 16 Jun 2020 16:02:29 -0400 Subject: [PATCH 1372/1971] Merge pull request #5826 from adroitwhiz/filter-extension-blocks Pass current editing target to getBlocksXML --- packages/scratch-gui/src/containers/blocks.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index b659cc7145..b2648e7b78 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -338,7 +338,7 @@ class Blocks extends React.Component { const stageCostumes = stage.getCostumes(); const targetCostumes = target.getCostumes(); const targetSounds = target.getSounds(); - const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(); + const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(target); return makeToolboxXML(target.isStage, target.id, dynamicBlocksXML, targetCostumes[targetCostumes.length - 1].name, stageCostumes[stageCostumes.length - 1].name, From f55901a8c380f6aaa21afe71acf25af848ed17d6 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2020 04:01:20 +0000 Subject: [PATCH 1373/1971] Merge pull request #5896 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.9.20200616200843 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index ac86c4c4e0..bd94f6d7ef 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200615150854", - "scratch-l10n": "3.9.20200615152937", + "scratch-l10n": "3.9.20200616200843", "scratch-paint": "0.2.0-prerelease.20200615150428", "scratch-render": "0.1.0-prerelease.20200615201428", "scratch-storage": "1.3.3", From 090a230c955cdc61eec7111b5c0431de52ea965a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2020 04:02:46 +0000 Subject: [PATCH 1374/1971] Merge pull request #5897 from LLK/dependabot/npm_and_yarn/scratch-vm-0.2.0-prerelease.20200616200729 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index bd94f6d7ef..cc95a14347 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20200615201428", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", - "scratch-vm": "0.2.0-prerelease.20200611202828", + "scratch-vm": "0.2.0-prerelease.20200616200729", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From ef84ebad3967bd8f053789797f46d59aaf9e4035 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2020 04:15:32 +0000 Subject: [PATCH 1375/1971] Merge pull request #5895 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200616140139 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cc95a14347..44f2439dfb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200615150854", + "scratch-blocks": "0.1.0-prerelease.20200616140139", "scratch-l10n": "3.9.20200616200843", "scratch-paint": "0.2.0-prerelease.20200615150428", "scratch-render": "0.1.0-prerelease.20200615201428", From eeb698db10282692e706199ad2e99ad1f36787ba Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 17 Jun 2020 12:17:00 -0400 Subject: [PATCH 1376/1971] Merge pull request #5899 from chrisgarrity/update-dependencies-mongolian Update dependencies to add Mongolian --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 44f2439dfb..62a980018b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,8 +108,8 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200616140139", - "scratch-l10n": "3.9.20200616200843", + "scratch-blocks": "0.1.0-prerelease.20200617153418", + "scratch-l10n": "3.10.20200617135753", "scratch-paint": "0.2.0-prerelease.20200615150428", "scratch-render": "0.1.0-prerelease.20200615201428", "scratch-storage": "1.3.3", From 2481216f20ef3c79d8fb2f9074c8fc1f3cfed2d2 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2020 22:02:18 +0000 Subject: [PATCH 1377/1971] Merge pull request #5902 from LLK/dependabot/npm_and_yarn/scratch-render-0.1.0-prerelease.20200617185826 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 62a980018b..c3cd9e5474 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200617153418", "scratch-l10n": "3.10.20200617135753", "scratch-paint": "0.2.0-prerelease.20200615150428", - "scratch-render": "0.1.0-prerelease.20200615201428", + "scratch-render": "0.1.0-prerelease.20200617185826", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", "scratch-vm": "0.2.0-prerelease.20200616200729", From 24d86cb1c43cd8e6ccf8d3bcef8f26d5625baddb Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2020 22:13:53 +0000 Subject: [PATCH 1378/1971] Merge pull request #5903 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.10.20200617210158 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c3cd9e5474..09ab05f2a2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200617153418", - "scratch-l10n": "3.10.20200617135753", + "scratch-l10n": "3.10.20200617210158", "scratch-paint": "0.2.0-prerelease.20200615150428", "scratch-render": "0.1.0-prerelease.20200617185826", "scratch-storage": "1.3.3", From c523ca136d54c9744ec5c647cd0f188a55b21ff7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2020 22:34:03 +0000 Subject: [PATCH 1379/1971] Merge pull request #5901 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200617163252 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 09ab05f2a2..067b9bb4a0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200617153418", + "scratch-blocks": "0.1.0-prerelease.20200617163252", "scratch-l10n": "3.10.20200617210158", "scratch-paint": "0.2.0-prerelease.20200615150428", "scratch-render": "0.1.0-prerelease.20200617185826", From 9e18f1567656305207bab35aae6b03da06fd6269 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 18:58:42 +0000 Subject: [PATCH 1380/1971] Merge pull request #5906 from LLK/dependabot/npm_and_yarn/scratch-vm-0.2.0-prerelease.20200618162938 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 067b9bb4a0..7a574a5e39 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20200617185826", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", - "scratch-vm": "0.2.0-prerelease.20200616200729", + "scratch-vm": "0.2.0-prerelease.20200618162938", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 1d44d9bdb9f35f8cc66b9f6209ad055cc3434cbe Mon Sep 17 00:00:00 2001 From: adroitwhiz Date: Fri, 19 Jun 2020 12:37:58 -0400 Subject: [PATCH 1381/1971] Merge pull request #5510 from adroitwhiz/drag-refactor Call renderer.draw immediately on drag start/stop --- packages/scratch-gui/src/containers/stage.jsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 9ddd0d9079..3cc913a2e4 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -362,6 +362,7 @@ class Stage extends React.Component { this.drawDragCanvas(drawableData); this.positionDragCanvas(x, y); this.props.vm.postSpriteInfo({visible: false}); + this.props.vm.renderer.draw(); } } onStopDrag (mouseX, mouseY) { @@ -387,12 +388,9 @@ class Stage extends React.Component { } this.props.vm.postSpriteInfo(spriteInfo); // Then clear the dragging canvas and stop drag (potentially slow if selecting sprite) - setTimeout(() => { - this.clearDragCanvas(); - setTimeout(() => { - commonStopDragActions(); - }, 30); - }, 30); + this.clearDragCanvas(); + commonStopDragActions(); + this.props.vm.renderer.draw(); } else { commonStopDragActions(); } From a7dfd592c21b240c9c7ad4b6ca64c86d563d9a40 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2020 16:51:04 +0000 Subject: [PATCH 1382/1971] Merge pull request #5912 from LLK/dependabot/npm_and_yarn/scratch-paint-0.2.0-prerelease.20200619164738 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 7a574a5e39..e7f82589f4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200617163252", "scratch-l10n": "3.10.20200617210158", - "scratch-paint": "0.2.0-prerelease.20200615150428", + "scratch-paint": "0.2.0-prerelease.20200619164738", "scratch-render": "0.1.0-prerelease.20200617185826", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", From f9cdce186f5201a0d67992c1881d928261b9637a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2020 16:51:34 +0000 Subject: [PATCH 1383/1971] Merge pull request #5911 from LLK/dependabot/npm_and_yarn/scratch-vm-0.2.0-prerelease.20200622143012 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e7f82589f4..3b9f9b4d26 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20200617185826", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", - "scratch-vm": "0.2.0-prerelease.20200618162938", + "scratch-vm": "0.2.0-prerelease.20200622143012", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From fa10b19453ddb665422c55f59720b3d398e8d060 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2020 20:04:53 +0000 Subject: [PATCH 1384/1971] Merge pull request #5921 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.10.20200622132359 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3b9f9b4d26..b240f38afa 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200617163252", - "scratch-l10n": "3.10.20200617210158", + "scratch-l10n": "3.10.20200622132359", "scratch-paint": "0.2.0-prerelease.20200619164738", "scratch-render": "0.1.0-prerelease.20200617185826", "scratch-storage": "1.3.3", From 4782493e52ac4ec9639a8e610598102f02a7d904 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2020 20:29:04 +0000 Subject: [PATCH 1385/1971] Merge pull request #5923 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200622190622 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b240f38afa..b02b5fa06d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200617163252", + "scratch-blocks": "0.1.0-prerelease.20200622190622", "scratch-l10n": "3.10.20200622132359", "scratch-paint": "0.2.0-prerelease.20200619164738", "scratch-render": "0.1.0-prerelease.20200617185826", From 5149f2c7717cce36c870d74b687178913d871b18 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Tue, 23 Jun 2020 16:23:53 -0400 Subject: [PATCH 1386/1971] Merge pull request #5463 from apple502j/apple-banana Fix default parameter text loading in English --- packages/scratch-gui/src/containers/blocks.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index b2648e7b78..a83e8c35c9 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -339,7 +339,7 @@ class Blocks extends React.Component { const targetCostumes = target.getCostumes(); const targetSounds = target.getSounds(); const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(target); - return makeToolboxXML(target.isStage, target.id, dynamicBlocksXML, + return makeToolboxXML(false, target.isStage, target.id, dynamicBlocksXML, targetCostumes[targetCostumes.length - 1].name, stageCostumes[stageCostumes.length - 1].name, targetSounds.length > 0 ? targetSounds[targetSounds.length - 1].name : '' From 2037cb784792b3b1a76fbbabce237cadc71486e4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 25 Jun 2020 21:29:47 +0000 Subject: [PATCH 1387/1971] Merge pull request #5939 from LLK/dependabot/npm_and_yarn/scratch-vm-0.2.0-prerelease.20200625173937 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b02b5fa06d..dd33b1f2bc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20200617185826", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", - "scratch-vm": "0.2.0-prerelease.20200622143012", + "scratch-vm": "0.2.0-prerelease.20200625173937", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 31bcbe1ef1394de14dac791457e710d78b9148c9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 27 Jun 2020 02:22:25 +0000 Subject: [PATCH 1388/1971] Merge pull request #5944 from LLK/dependabot/npm_and_yarn/scratch-paint-0.2.0-prerelease.20200627020736 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index dd33b1f2bc..0ccc61cf04 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200622190622", "scratch-l10n": "3.10.20200622132359", - "scratch-paint": "0.2.0-prerelease.20200619164738", + "scratch-paint": "0.2.0-prerelease.20200627020736", "scratch-render": "0.1.0-prerelease.20200617185826", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", From 3a4355a403600af4124b2f856b1227827777e9ad Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 27 Jun 2020 02:35:48 +0000 Subject: [PATCH 1389/1971] Merge pull request #5931 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.10.20200624030914 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0ccc61cf04..23fbb88b72 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200622190622", - "scratch-l10n": "3.10.20200622132359", + "scratch-l10n": "3.10.20200624030914", "scratch-paint": "0.2.0-prerelease.20200627020736", "scratch-render": "0.1.0-prerelease.20200617185826", "scratch-storage": "1.3.3", From 9550961cc159338ed45db5ef2c3abc23d11bc1ef Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 27 Jun 2020 02:53:58 +0000 Subject: [PATCH 1390/1971] Merge pull request #5924 from LLK/dependabot/npm_and_yarn/scratch-render-0.1.0-prerelease.20200622210757 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 23fbb88b72..c858908e17 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200622190622", "scratch-l10n": "3.10.20200624030914", "scratch-paint": "0.2.0-prerelease.20200627020736", - "scratch-render": "0.1.0-prerelease.20200617185826", + "scratch-render": "0.1.0-prerelease.20200622210757", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", "scratch-vm": "0.2.0-prerelease.20200625173937", From 9a532d79772c74b82f93235859160214cccae749 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 27 Jun 2020 02:54:14 +0000 Subject: [PATCH 1391/1971] Merge pull request #5929 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200623135713 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c858908e17..32854362f7 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200622190622", + "scratch-blocks": "0.1.0-prerelease.20200623135713", "scratch-l10n": "3.10.20200624030914", "scratch-paint": "0.2.0-prerelease.20200627020736", "scratch-render": "0.1.0-prerelease.20200622210757", From c108573769d3f6e7815c9d67280e0a7fc92a5a7d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 8 Jul 2020 03:23:39 +0000 Subject: [PATCH 1392/1971] Merge pull request #5971 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.10.20200708030815 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 32854362f7..9f05d0794b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200623135713", - "scratch-l10n": "3.10.20200624030914", + "scratch-l10n": "3.10.20200708030815", "scratch-paint": "0.2.0-prerelease.20200627020736", "scratch-render": "0.1.0-prerelease.20200622210757", "scratch-storage": "1.3.3", From 3efc68c78e047d4f9205575d550799fa1fb48fa0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 8 Jul 2020 03:35:58 +0000 Subject: [PATCH 1393/1971] Merge pull request #5957 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200630140237 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9f05d0794b..df70acebab 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200623135713", + "scratch-blocks": "0.1.0-prerelease.20200630140237", "scratch-l10n": "3.10.20200708030815", "scratch-paint": "0.2.0-prerelease.20200627020736", "scratch-render": "0.1.0-prerelease.20200622210757", From bdee16594a616a2b83fc2e6910f11db996e67676 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford <7019101+cwillisf@users.noreply.github.com> Date: Thu, 9 Jul 2020 08:55:50 -0700 Subject: [PATCH 1394/1971] Merge pull request #5962 from cwillisf/ci-no-progress disable webpack progress plugin on CI --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index df70acebab..e035452a35 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -4,7 +4,7 @@ "description": "GraphicaL User Interface for creating and running Scratch 3.0 projects", "main": "./dist/scratch-gui.js", "scripts": { - "build": "npm run clean && webpack --progress --colors --bail", + "build": "npm run clean && webpack --colors --bail", "clean": "rimraf ./build && mkdirp build && rimraf ./dist && mkdirp dist", "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1) [skip ci]\"", "prune": "./prune-gh-pages.sh", @@ -16,7 +16,7 @@ "test:lint": "eslint . --ext .js,.jsx", "test:unit": "jest test[\\\\/]unit", "test:smoke": "jest --runInBand test[\\\\/]smoke", - "watch": "webpack --progress --colors --watch" + "watch": "webpack --colors --watch" }, "author": "Massachusetts Institute of Technology", "license": "BSD-3-Clause", From cbd1640b3d5a510c157edf85e7b5d13c661400c6 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2020 20:30:01 +0000 Subject: [PATCH 1395/1971] Merge pull request #5975 from LLK/dependabot/npm_and_yarn/scratch-vm-0.2.0-prerelease.20200709173451 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e035452a35..58193fa737 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20200622210757", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", - "scratch-vm": "0.2.0-prerelease.20200625173937", + "scratch-vm": "0.2.0-prerelease.20200709173451", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 7a6ece795e5be14981afb48f287bd2ed708afd48 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2020 20:50:47 +0000 Subject: [PATCH 1396/1971] Merge pull request #5976 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200709183415 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 58193fa737..e2ba91aa2c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200630140237", + "scratch-blocks": "0.1.0-prerelease.20200709183415", "scratch-l10n": "3.10.20200708030815", "scratch-paint": "0.2.0-prerelease.20200627020736", "scratch-render": "0.1.0-prerelease.20200622210757", From f43111d28179530e1e61a9dddd3caef02eab6f4e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2020 16:08:53 +0000 Subject: [PATCH 1397/1971] Merge pull request #5982 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200714135908 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e2ba91aa2c..001728adbd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200709183415", + "scratch-blocks": "0.1.0-prerelease.20200714135908", "scratch-l10n": "3.10.20200708030815", "scratch-paint": "0.2.0-prerelease.20200627020736", "scratch-render": "0.1.0-prerelease.20200622210757", From 82ccf87d50dc66cbed69f8340285e19434c80d89 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2020 16:21:12 +0000 Subject: [PATCH 1398/1971] Merge pull request #5978 from LLK/dependabot/npm_and_yarn/scratch-render-0.1.0-prerelease.20200709194750 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 001728adbd..cd2d85e175 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200714135908", "scratch-l10n": "3.10.20200708030815", "scratch-paint": "0.2.0-prerelease.20200627020736", - "scratch-render": "0.1.0-prerelease.20200622210757", + "scratch-render": "0.1.0-prerelease.20200709194750", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", "scratch-vm": "0.2.0-prerelease.20200709173451", From 932e60b01bc047c278b5c09ba97dedb710837c03 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 14 Jul 2020 17:01:43 -0400 Subject: [PATCH 1399/1971] Merge pull request #5986 from fsih/updatePaintAndRender Update paint and render --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cd2d85e175..aeb355b135 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,8 +110,8 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200714135908", "scratch-l10n": "3.10.20200708030815", - "scratch-paint": "0.2.0-prerelease.20200627020736", - "scratch-render": "0.1.0-prerelease.20200709194750", + "scratch-paint": "0.2.0-prerelease.20200714203549", + "scratch-render": "0.1.0-prerelease.20200714205428", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", "scratch-vm": "0.2.0-prerelease.20200709173451", From 289fb728118bc7acab7450e833af88de7fed8d23 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2020 21:18:16 +0000 Subject: [PATCH 1400/1971] Merge pull request #5985 from LLK/dependabot/npm_and_yarn/scratch-vm-0.2.0-prerelease.20200714185213 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index aeb355b135..5f45ee1eef 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20200714205428", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", - "scratch-vm": "0.2.0-prerelease.20200709173451", + "scratch-vm": "0.2.0-prerelease.20200714185213", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 5c32e0201ebccdf52a0085f59c3e53a74245ea42 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2020 03:24:36 +0000 Subject: [PATCH 1401/1971] Merge pull request #5987 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.10.20200715030910 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5f45ee1eef..52739757bc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200714135908", - "scratch-l10n": "3.10.20200708030815", + "scratch-l10n": "3.10.20200715030910", "scratch-paint": "0.2.0-prerelease.20200714203549", "scratch-render": "0.1.0-prerelease.20200714205428", "scratch-storage": "1.3.3", From b46aa97fb98d58af61140f9f270f56ebfa1e8b9a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2020 19:51:06 +0000 Subject: [PATCH 1402/1971] Merge pull request #6001 from LLK/dependabot/npm_and_yarn/scratch-render-0.1.0-prerelease.20200716193608 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 52739757bc..00f5344eda 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200714135908", "scratch-l10n": "3.10.20200715030910", "scratch-paint": "0.2.0-prerelease.20200714203549", - "scratch-render": "0.1.0-prerelease.20200714205428", + "scratch-render": "0.1.0-prerelease.20200716193608", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", "scratch-vm": "0.2.0-prerelease.20200714185213", From 42a5d24e2671d0e616490ac73c712eda846139a5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2020 03:00:45 +0000 Subject: [PATCH 1403/1971] Merge pull request #6005 from LLK/dependabot/npm_and_yarn/scratch-paint-0.2.0-prerelease.20200717024459 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 00f5344eda..656d8bf9d2 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200714135908", "scratch-l10n": "3.10.20200715030910", - "scratch-paint": "0.2.0-prerelease.20200714203549", + "scratch-paint": "0.2.0-prerelease.20200717024459", "scratch-render": "0.1.0-prerelease.20200716193608", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", From 216cd6c65e503422541aff5c94194cdeec58f73e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2020 19:26:30 +0000 Subject: [PATCH 1404/1971] Merge pull request #6013 from LLK/dependabot/npm_and_yarn/scratch-vm-0.2.0-prerelease.20200720182258 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 656d8bf9d2..820147fcc0 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20200716193608", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", - "scratch-vm": "0.2.0-prerelease.20200714185213", + "scratch-vm": "0.2.0-prerelease.20200720182258", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From 0756fd3a40bd1efa4ef8ca0e6b3c3625d6ececcc Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2020 19:28:17 +0000 Subject: [PATCH 1405/1971] Merge pull request #6014 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.10.20200720182215 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 820147fcc0..91317129ac 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200714135908", - "scratch-l10n": "3.10.20200715030910", + "scratch-l10n": "3.10.20200720182215", "scratch-paint": "0.2.0-prerelease.20200717024459", "scratch-render": "0.1.0-prerelease.20200716193608", "scratch-storage": "1.3.3", From 69f9be2f09e270dcca0c6383e3189075123edf19 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2020 16:32:15 +0000 Subject: [PATCH 1406/1971] Merge pull request #6016 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200721135911 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 91317129ac..64f5272a6c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200714135908", + "scratch-blocks": "0.1.0-prerelease.20200721135911", "scratch-l10n": "3.10.20200720182215", "scratch-paint": "0.2.0-prerelease.20200717024459", "scratch-render": "0.1.0-prerelease.20200716193608", From cab3891fd62742102886eba740a6788ca41ba823 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 21 Jul 2020 16:07:02 -0400 Subject: [PATCH 1407/1971] Merge pull request #6017 from adroitwhiz/bump-render-1 Bump scratch-render --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 64f5272a6c..b460c2d939 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200721135911", "scratch-l10n": "3.10.20200720182215", "scratch-paint": "0.2.0-prerelease.20200717024459", - "scratch-render": "0.1.0-prerelease.20200716193608", + "scratch-render": "0.1.0-prerelease.20200721190141", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", "scratch-vm": "0.2.0-prerelease.20200720182258", From 0d7d4f5373e9bdfbdbe8c4b8419947fa9da23412 Mon Sep 17 00:00:00 2001 From: adroitwhiz Date: Tue, 21 Jul 2020 16:08:11 -0400 Subject: [PATCH 1408/1971] Merge pull request #5513 from adroitwhiz/drag-new-method Extract dragged sprites' drawables in screen space --- packages/scratch-gui/src/containers/stage.jsx | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 3cc913a2e4..8e1ac92483 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -304,25 +304,31 @@ class Stage extends React.Component { } this.setState({mouseDownTimeoutId: null}); } - drawDragCanvas (drawableData) { + /** + * Initialize the position of the "dragged sprite" canvas + * @param {DrawableExtraction} drawableData The data returned from renderer.extractDrawableScreenSpace + * @param {number} x The x position of the initial drag event + * @param {number} y The y position of the initial drag event + */ + drawDragCanvas (drawableData, x, y) { const { - data, - width, - height, - x, - y + imageData, + x: boundsX, + y: boundsY, + width: boundsWidth, + height: boundsHeight } = drawableData; - this.dragCanvas.width = width; - this.dragCanvas.height = height; - // Need to convert uint8array from WebGL readPixels into Uint8ClampedArray - // for ImageData constructor. Shares underlying buffer, so it is fast. - const imageData = new ImageData( - new Uint8ClampedArray(data.buffer), width, height); + this.dragCanvas.width = imageData.width; + this.dragCanvas.height = imageData.height; + // On high-DPI devices, the canvas size in layout-pixels is not equal to the size of the extracted data. + this.dragCanvas.style.width = `${boundsWidth}px`; + this.dragCanvas.style.height = `${boundsHeight}px`; + this.dragCanvas.getContext('2d').putImageData(imageData, 0, 0); // Position so that pick location is at (0, 0) so that positionDragCanvas() // can use translation to move to mouse position smoothly. - this.dragCanvas.style.left = `${-x}px`; - this.dragCanvas.style.top = `${-y}px`; + this.dragCanvas.style.left = `${boundsX - x}px`; + this.dragCanvas.style.top = `${boundsY - y}px`; this.dragCanvas.style.display = 'block'; } clearDragCanvas () { @@ -349,17 +355,20 @@ class Stage extends React.Component { // Dragging always brings the target to the front target.goToFront(); - // Extract the drawable art - const drawableData = this.renderer.extractDrawable(drawableId, x, y); + const [scratchMouseX, scratchMouseY] = this.getScratchCoords(x, y); + const offsetX = target.x - scratchMouseX; + const offsetY = -(target.y + scratchMouseY); this.props.vm.startDrag(targetId); this.setState({ isDragging: true, dragId: targetId, - dragOffset: drawableData.scratchOffset + dragOffset: [offsetX, offsetY] }); if (this.props.useEditorDragStyle) { - this.drawDragCanvas(drawableData); + // Extract the drawable art + const drawableData = this.renderer.extractDrawableScreenSpace(drawableId); + this.drawDragCanvas(drawableData, x, y); this.positionDragCanvas(x, y); this.props.vm.postSpriteInfo({visible: false}); this.props.vm.renderer.draw(); From efbf6b9fb4480f1cb27b7d3aa6467bea945fda01 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 21 Jul 2020 16:26:27 -0400 Subject: [PATCH 1409/1971] Merge pull request #6018 from fsih/updatePaint Update paint --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b460c2d939..0fa4f5ccbb 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200721135911", "scratch-l10n": "3.10.20200720182215", - "scratch-paint": "0.2.0-prerelease.20200717024459", + "scratch-paint": "0.2.0-prerelease.20200721201211", "scratch-render": "0.1.0-prerelease.20200721190141", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", From b8d04d7f1f763503b62e20dcfa9ed2c1bbb7dd88 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 22 Jul 2020 03:21:47 +0000 Subject: [PATCH 1410/1971] Merge pull request #6019 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.10.20200722030735 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0fa4f5ccbb..158d71e018 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200721135911", - "scratch-l10n": "3.10.20200720182215", + "scratch-l10n": "3.10.20200722030735", "scratch-paint": "0.2.0-prerelease.20200721201211", "scratch-render": "0.1.0-prerelease.20200721190141", "scratch-storage": "1.3.3", From 8909bd9a8d84b62aaddf9ed2d1ec7edf83414a58 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2020 21:15:36 +0000 Subject: [PATCH 1411/1971] Merge pull request #6040 from LLK/dependabot/npm_and_yarn/scratch-paint-0.2.0-prerelease.20200728205208 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 158d71e018..8c668b7bcd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200721135911", "scratch-l10n": "3.10.20200722030735", - "scratch-paint": "0.2.0-prerelease.20200721201211", + "scratch-paint": "0.2.0-prerelease.20200728205208", "scratch-render": "0.1.0-prerelease.20200721190141", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", From 9b219c40b002a7790910dd42c01c4da6f853f17e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2020 21:27:58 +0000 Subject: [PATCH 1412/1971] Merge pull request #6024 from LLK/dependabot/npm_and_yarn/scratch-render-0.1.0-prerelease.20200723210832 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8c668b7bcd..0c0afe3062 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200721135911", "scratch-l10n": "3.10.20200722030735", "scratch-paint": "0.2.0-prerelease.20200728205208", - "scratch-render": "0.1.0-prerelease.20200721190141", + "scratch-render": "0.1.0-prerelease.20200723210832", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", "scratch-vm": "0.2.0-prerelease.20200720182258", From d6cd55e773706dd3a5e4fc3281e9fa37849455f7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2020 03:21:32 +0000 Subject: [PATCH 1413/1971] Merge pull request #6042 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.10.20200729030742 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0c0afe3062..87366acbbd 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200721135911", - "scratch-l10n": "3.10.20200722030735", + "scratch-l10n": "3.10.20200729030742", "scratch-paint": "0.2.0-prerelease.20200728205208", "scratch-render": "0.1.0-prerelease.20200723210832", "scratch-storage": "1.3.3", From a876223e8a8012e09becc8b9fbb840b1397e5820 Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Wed, 29 Jul 2020 16:56:00 -0400 Subject: [PATCH 1414/1971] Merge pull request #6045 from benjiwheeler/revert-paint revert version of scratch-paint to remove outline gradient --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 87366acbbd..d47e93cfa6 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200721135911", "scratch-l10n": "3.10.20200729030742", - "scratch-paint": "0.2.0-prerelease.20200728205208", + "scratch-paint": "0.2.0-prerelease.20200728190940", "scratch-render": "0.1.0-prerelease.20200723210832", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", From ede973fc1ae152a597b79ddc5d41c50f02bb4283 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2020 23:10:50 +0000 Subject: [PATCH 1415/1971] Merge pull request #6047 from LLK/dependabot/npm_and_yarn/scratch-paint-0.2.0-prerelease.20200729212812 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d47e93cfa6..73cb32a3fc 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200721135911", "scratch-l10n": "3.10.20200729030742", - "scratch-paint": "0.2.0-prerelease.20200728190940", + "scratch-paint": "0.2.0-prerelease.20200729212812", "scratch-render": "0.1.0-prerelease.20200723210832", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", From d11c1cd8e8b5ca2c3b7821467fd04d471c2a7a9f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2020 16:19:27 +0000 Subject: [PATCH 1416/1971] Merge pull request #6065 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200804140007 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 73cb32a3fc..adbfa597e9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200721135911", + "scratch-blocks": "0.1.0-prerelease.20200804140007", "scratch-l10n": "3.10.20200729030742", "scratch-paint": "0.2.0-prerelease.20200729212812", "scratch-render": "0.1.0-prerelease.20200723210832", From 92bf1005857eade9ab33dd27aa8d70965c8db264 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 5 Aug 2020 02:37:44 +0000 Subject: [PATCH 1417/1971] Merge pull request #6066 from LLK/dependabot/npm_and_yarn/scratch-render-0.1.0-prerelease.20200804212124 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index adbfa597e9..8d08de3eff 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200804140007", "scratch-l10n": "3.10.20200729030742", "scratch-paint": "0.2.0-prerelease.20200729212812", - "scratch-render": "0.1.0-prerelease.20200723210832", + "scratch-render": "0.1.0-prerelease.20200804212124", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", "scratch-vm": "0.2.0-prerelease.20200720182258", From bce9e70a1aaefa9615088e6a9490b79ab834257b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 5 Aug 2020 05:47:11 +0000 Subject: [PATCH 1418/1971] Merge pull request #6068 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.10.20200805030706 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 8d08de3eff..328e5e9f74 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200804140007", - "scratch-l10n": "3.10.20200729030742", + "scratch-l10n": "3.10.20200805030706", "scratch-paint": "0.2.0-prerelease.20200729212812", "scratch-render": "0.1.0-prerelease.20200804212124", "scratch-storage": "1.3.3", From ed479f857926345f1e2681e776d51013c77809aa Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2020 21:24:47 +0000 Subject: [PATCH 1419/1971] Merge pull request #6088 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200811140116 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 328e5e9f74..62f3406b46 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200804140007", + "scratch-blocks": "0.1.0-prerelease.20200811140116", "scratch-l10n": "3.10.20200805030706", "scratch-paint": "0.2.0-prerelease.20200729212812", "scratch-render": "0.1.0-prerelease.20200804212124", From e6e453493cf0f8da9a51a68878348110290aebca Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2020 21:39:06 +0000 Subject: [PATCH 1420/1971] Merge pull request #6070 from LLK/dependabot/npm_and_yarn/scratch-render-0.1.0-prerelease.20200806013734 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 62f3406b46..94e2daa9a9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200811140116", "scratch-l10n": "3.10.20200805030706", "scratch-paint": "0.2.0-prerelease.20200729212812", - "scratch-render": "0.1.0-prerelease.20200804212124", + "scratch-render": "0.1.0-prerelease.20200806013734", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", "scratch-vm": "0.2.0-prerelease.20200720182258", From 2396714d31066c113c1f034c3bc7d87be6d805e3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2020 07:17:41 +0000 Subject: [PATCH 1421/1971] Merge pull request #6089 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.10.20200812030801 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 94e2daa9a9..f0fdbac62f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200811140116", - "scratch-l10n": "3.10.20200805030706", + "scratch-l10n": "3.10.20200812030801", "scratch-paint": "0.2.0-prerelease.20200729212812", "scratch-render": "0.1.0-prerelease.20200806013734", "scratch-storage": "1.3.3", From 780ed079df7753c5901f2c6a18c68dad7533297e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2020 02:42:22 +0000 Subject: [PATCH 1422/1971] Merge pull request #6096 from LLK/dependabot/npm_and_yarn/scratch-paint-0.2.0-prerelease.20200813210710 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index f0fdbac62f..1aeed5a829 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200811140116", "scratch-l10n": "3.10.20200812030801", - "scratch-paint": "0.2.0-prerelease.20200729212812", + "scratch-paint": "0.2.0-prerelease.20200813210710", "scratch-render": "0.1.0-prerelease.20200806013734", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", From a47be1e6381934d352ac10824e23b7ad80b6caaa Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 17 Aug 2020 21:09:19 +0000 Subject: [PATCH 1423/1971] Merge pull request #6099 from LLK/dependabot/npm_and_yarn/scratch-render-0.1.0-prerelease.20200817194622 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 1aeed5a829..5d94af121b 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200811140116", "scratch-l10n": "3.10.20200812030801", "scratch-paint": "0.2.0-prerelease.20200813210710", - "scratch-render": "0.1.0-prerelease.20200806013734", + "scratch-render": "0.1.0-prerelease.20200817194622", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", "scratch-vm": "0.2.0-prerelease.20200720182258", From 169f2ddd3f573ab611721240f0af357de1f1723b Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Tue, 18 Aug 2020 16:11:07 -0400 Subject: [PATCH 1424/1971] Merge pull request #4542 from towerofnix/better-shareBlocksToTarget Position blocks added from "share the love" within the viewport --- .../scratch-gui/src/containers/blocks.jsx | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index a83e8c35c9..f5d52388ff 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -14,7 +14,7 @@ import ExtensionLibrary from './extension-library.jsx'; import extensionData from '../lib/libraries/extensions/index.jsx'; import CustomProcedures from './custom-procedures.jsx'; import errorBoundaryHOC from '../lib/error-boundary-hoc.jsx'; -import {STAGE_DISPLAY_SIZES} from '../lib/layout-constants'; +import {BLOCKS_DEFAULT_SCALE, STAGE_DISPLAY_SIZES} from '../lib/layout-constants'; import DropAreaHOC from '../lib/drop-area-hoc.jsx'; import DragConstants from '../lib/drag-constants'; import defineDynamicBlock from '../lib/define-dynamic-block'; @@ -25,6 +25,7 @@ import {activateColorPicker} from '../reducers/color-picker'; import {closeExtensionLibrary, openSoundRecorder, openConnectionModal} from '../reducers/modals'; import {activateCustomProcedures, deactivateCustomProcedures} from '../reducers/custom-procedures'; import {setConnectionModalExtensionId} from '../reducers/connection-modal'; +import {updateMetrics} from '../reducers/workspace-metrics'; import { activateTab, @@ -79,7 +80,6 @@ class Blocks extends React.Component { this.ScratchBlocks.recordSoundCallback = this.handleOpenSoundRecorder; this.state = { - workspaceMetrics: {}, prompt: null }; this.onTargetsUpdate = debounce(this.onTargetsUpdate, 100); @@ -301,14 +301,17 @@ class Blocks extends React.Component { onWorkspaceMetricsChange () { const target = this.props.vm.editingTarget; if (target && target.id) { - const workspaceMetrics = Object.assign({}, this.state.workspaceMetrics, { - [target.id]: { + // Dispatch updateMetrics later, since onWorkspaceMetricsChange may be (very indirectly) + // called from a reducer, i.e. when you create a custom procedure. + // TODO: Is this a vehement hack? + setTimeout(() => { + this.props.updateMetrics({ + targetID: target.id, scrollX: this.workspace.scrollX, scrollY: this.workspace.scrollY, scale: this.workspace.scale - } - }); - this.setState({workspaceMetrics}); + }); + }, 0); } } onScriptGlowOn (data) { @@ -355,7 +358,7 @@ class Blocks extends React.Component { this.props.updateToolboxState(toolboxXML); } - if (this.props.vm.editingTarget && !this.state.workspaceMetrics[this.props.vm.editingTarget.id]) { + if (this.props.vm.editingTarget && !this.props.workspaceMetrics.targets[this.props.vm.editingTarget.id]) { this.onWorkspaceMetricsChange(); } @@ -381,8 +384,8 @@ class Blocks extends React.Component { } this.workspace.addChangeListener(this.props.vm.blockListener); - if (this.props.vm.editingTarget && this.state.workspaceMetrics[this.props.vm.editingTarget.id]) { - const {scrollX, scrollY, scale} = this.state.workspaceMetrics[this.props.vm.editingTarget.id]; + if (this.props.vm.editingTarget && this.props.workspaceMetrics.targets[this.props.vm.editingTarget.id]) { + const {scrollX, scrollY, scale} = this.props.workspaceMetrics.targets[this.props.vm.editingTarget.id]; this.workspace.scrollX = scrollX; this.workspace.scrollY = scrollY; this.workspace.scale = scale; @@ -525,6 +528,8 @@ class Blocks extends React.Component { onRequestCloseExtensionLibrary, onRequestCloseCustomProcedures, toolboxXML, + updateMetrics: updateMetricsProp, + workspaceMetrics, ...props } = this.props; /* eslint-enable no-unused-vars */ @@ -607,15 +612,19 @@ Blocks.propTypes = { }), stageSize: PropTypes.oneOf(Object.keys(STAGE_DISPLAY_SIZES)).isRequired, toolboxXML: PropTypes.string, + updateMetrics: PropTypes.func, updateToolboxState: PropTypes.func, - vm: PropTypes.instanceOf(VM).isRequired + vm: PropTypes.instanceOf(VM).isRequired, + workspaceMetrics: PropTypes.shape({ + targets: PropTypes.objectOf(PropTypes.object) + }) }; Blocks.defaultOptions = { zoom: { controls: true, wheel: true, - startScale: 0.675 + startScale: BLOCKS_DEFAULT_SCALE }, grid: { spacing: 40, @@ -654,7 +663,8 @@ const mapStateToProps = state => ({ locale: state.locales.locale, messages: state.locales.messages, toolboxXML: state.scratchGui.toolbox.toolboxXML, - customProceduresVisible: state.scratchGui.customProcedures.active + customProceduresVisible: state.scratchGui.customProcedures.active, + workspaceMetrics: state.scratchGui.workspaceMetrics }); const mapDispatchToProps = dispatch => ({ @@ -676,6 +686,9 @@ const mapDispatchToProps = dispatch => ({ }, updateToolboxState: toolboxXML => { dispatch(updateToolbox(toolboxXML)); + }, + updateMetrics: metrics => { + dispatch(updateMetrics(metrics)); } }); From 8b85aeeaae1933b20338d5ee6861abf12952748f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 19 Aug 2020 12:36:18 +0000 Subject: [PATCH 1425/1971] Merge pull request #6102 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200818140148 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5d94af121b..78bd893368 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200811140116", + "scratch-blocks": "0.1.0-prerelease.20200818140148", "scratch-l10n": "3.10.20200812030801", "scratch-paint": "0.2.0-prerelease.20200813210710", "scratch-render": "0.1.0-prerelease.20200817194622", From 10f475f0fdef414d55494661e4b352abf5688e71 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 21:58:15 +0000 Subject: [PATCH 1426/1971] Merge pull request #6110 from LLK/dependabot/npm_and_yarn/scratch-vm-0.2.0-prerelease.20200820211625 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 78bd893368..523d03fcb9 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -114,7 +114,7 @@ "scratch-render": "0.1.0-prerelease.20200817194622", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", - "scratch-vm": "0.2.0-prerelease.20200720182258", + "scratch-vm": "0.2.0-prerelease.20200820211625", "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", From f19cb4561db4acd3be00e28d9364da16be2e642a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 22:10:48 +0000 Subject: [PATCH 1427/1971] Merge pull request #6109 from LLK/dependabot/npm_and_yarn/scratch-render-0.1.0-prerelease.20200820192709 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 523d03fcb9..fe900d27f3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200818140148", "scratch-l10n": "3.10.20200812030801", "scratch-paint": "0.2.0-prerelease.20200813210710", - "scratch-render": "0.1.0-prerelease.20200817194622", + "scratch-render": "0.1.0-prerelease.20200820192709", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", "scratch-vm": "0.2.0-prerelease.20200820211625", From c670c87b2dd204abe50829de23ce529ced6462d3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 21 Aug 2020 13:38:34 +0000 Subject: [PATCH 1428/1971] Merge pull request #6112 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.10.20200819030716 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index fe900d27f3..d9e6d2a686 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200818140148", - "scratch-l10n": "3.10.20200812030801", + "scratch-l10n": "3.10.20200819030716", "scratch-paint": "0.2.0-prerelease.20200813210710", "scratch-render": "0.1.0-prerelease.20200820192709", "scratch-storage": "1.3.3", From d7b46454cba74b1d31f2db62efaf8afa0b560f44 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 25 Aug 2020 23:02:19 -0400 Subject: [PATCH 1429/1971] Merge pull request #6126 from fsih/travisTest Update chromedriver --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d9e6d2a686..3249efd799 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -44,7 +44,7 @@ "babel-loader": "^8.0.4", "base64-loader": "1.0.0", "bowser": "1.9.4", - "chromedriver": "83.0.0", + "chromedriver": "84.0.1", "classnames": "2.2.6", "computed-style-to-inline-style": "3.0.0", "copy-webpack-plugin": "^4.5.1", From 5470b92b52d82950fa0cd5e5cc10c1ece3d968ab Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 25 Aug 2020 23:17:54 -0400 Subject: [PATCH 1430/1971] Merge pull request #6124 from fsih/updateRender Update render --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 3249efd799..886495327d 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200818140148", "scratch-l10n": "3.10.20200819030716", "scratch-paint": "0.2.0-prerelease.20200813210710", - "scratch-render": "0.1.0-prerelease.20200820192709", + "scratch-render": "0.1.0-prerelease.20200826001335", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", "scratch-vm": "0.2.0-prerelease.20200820211625", From 6f41bf1ae1a7f2b07983b3767391009bb2b45369 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 26 Aug 2020 10:31:06 +0000 Subject: [PATCH 1431/1971] Merge pull request #6129 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.10.20200826030836 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 886495327d..d0e38d6d6e 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200818140148", - "scratch-l10n": "3.10.20200819030716", + "scratch-l10n": "3.10.20200826030836", "scratch-paint": "0.2.0-prerelease.20200813210710", "scratch-render": "0.1.0-prerelease.20200826001335", "scratch-storage": "1.3.3", From 52bf93fee8c8fd4c6b4f75656a1d482ac1edfba9 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 27 Aug 2020 17:51:45 -0400 Subject: [PATCH 1432/1971] Merge pull request #6132 from fsih/updateRender Update to render with touching white fixes --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d0e38d6d6e..db117b8029 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -111,7 +111,7 @@ "scratch-blocks": "0.1.0-prerelease.20200818140148", "scratch-l10n": "3.10.20200826030836", "scratch-paint": "0.2.0-prerelease.20200813210710", - "scratch-render": "0.1.0-prerelease.20200826001335", + "scratch-render": "0.1.0-prerelease.20200827214414", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", "scratch-vm": "0.2.0-prerelease.20200820211625", From 78bd5ee1ba4429e796e45da281516e2f37c48041 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2020 03:29:54 +0000 Subject: [PATCH 1433/1971] Merge pull request #6142 from LLK/dependabot/npm_and_yarn/scratch-paint-0.2.0-prerelease.20200831213104 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index db117b8029..5a7a8c833f 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -110,7 +110,7 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200818140148", "scratch-l10n": "3.10.20200826030836", - "scratch-paint": "0.2.0-prerelease.20200813210710", + "scratch-paint": "0.2.0-prerelease.20200831213104", "scratch-render": "0.1.0-prerelease.20200827214414", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", From 81a3713dcde0544765ec6e3255a599b1a346171e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2020 13:11:17 +0000 Subject: [PATCH 1434/1971] Merge pull request #6150 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200901140240 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 5a7a8c833f..a047c14de1 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -108,7 +108,7 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200818140148", + "scratch-blocks": "0.1.0-prerelease.20200901140240", "scratch-l10n": "3.10.20200826030836", "scratch-paint": "0.2.0-prerelease.20200831213104", "scratch-render": "0.1.0-prerelease.20200827214414", From 6b94491d3d117446ec82ee74125c67aa6942b719 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2020 13:28:46 +0000 Subject: [PATCH 1435/1971] Merge pull request #6151 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.10.20200902030719 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index a047c14de1..94b6ad2f32 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -109,7 +109,7 @@ "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200901140240", - "scratch-l10n": "3.10.20200826030836", + "scratch-l10n": "3.10.20200902030719", "scratch-paint": "0.2.0-prerelease.20200831213104", "scratch-render": "0.1.0-prerelease.20200827214414", "scratch-storage": "1.3.3", From f380e486a1d553ba69c0b4dd43eac06d4879d241 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford <7019101+cwillisf@users.noreply.github.com> Date: Thu, 3 Sep 2020 12:39:16 -0700 Subject: [PATCH 1436/1971] Merge pull request #6130 from cwillisf/dep-dev-dep Move src/* dependencies into `dependencies` --- packages/scratch-gui/package.json | 79 +++++++++++++++---------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 94b6ad2f32..0eadf3c52c 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -25,60 +25,30 @@ "type": "git", "url": "git+ssh://git@github.com/LLK/scratch-gui.git" }, - "peerDependencies": { - "react": "^16.0.0", - "react-dom": "^16.0.0" - }, - "devDependencies": { - "@babel/cli": "^7.1.2", - "@babel/core": "^7.1.2", - "@babel/plugin-proposal-object-rest-spread": "^7.0.0", - "@babel/plugin-syntax-dynamic-import": "^7.0.0", - "@babel/plugin-transform-async-to-generator": "^7.1.0", - "@babel/preset-env": "^7.1.0", - "@babel/preset-react": "^7.0.0", + "dependencies": { "arraybuffer-loader": "^1.0.6", "autoprefixer": "^9.0.1", - "babel-core": "7.0.0-bridge.0", - "babel-eslint": "^10.0.1", - "babel-loader": "^8.0.4", "base64-loader": "1.0.0", "bowser": "1.9.4", - "chromedriver": "84.0.1", "classnames": "2.2.6", "computed-style-to-inline-style": "3.0.0", "copy-webpack-plugin": "^4.5.1", "core-js": "2.5.7", "css-loader": "^1.0.0", - "enzyme": "^3.5.0", - "enzyme-adapter-react-16": "1.3.0", "es6-object-assign": "1.1.0", - "eslint": "^5.0.1", - "eslint-config-scratch": "^5.0.0", - "eslint-import-resolver-webpack": "^0.11.1", - "eslint-plugin-import": "^2.18.2", - "eslint-plugin-jest": "^22.14.1", - "eslint-plugin-react": "^7.12.4", "file-loader": "2.0.0", "get-float-time-domain-data": "0.1.0", "get-user-media-promise": "1.1.4", - "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", - "html-webpack-plugin": "^3.2.0", "immutable": "3.8.2", "intl": "1.2.5", - "jest": "^21.0.0", - "jest-junit": "^7.0.0", "js-base64": "2.4.9", "keymirror": "0.1.1", "lodash.bindall": "4.4.0", "lodash.debounce": "4.0.8", "lodash.defaultsdeep": "4.6.0", - "lodash.isequal": "4.5.0", "lodash.omit": "4.5.0", - "lodash.pick": "4.4.0", "lodash.throttle": "4.0.1", "minilog": "3.1.0", - "mkdirp": "^1.0.3", "omggif": "1.0.9", "papaparse": "5.1.1", "postcss-import": "^12.0.0", @@ -86,7 +56,6 @@ "postcss-simple-vars": "^5.0.1", "prop-types": "^15.5.10", "query-string": "^5.1.1", - "raf": "^3.4.0", "raw-loader": "^0.5.1", "react": "16.2.0", "react-contextmenu": "2.9.4", @@ -100,13 +69,10 @@ "react-responsive": "5.0.0", "react-style-proptype": "3.2.2", "react-tabs": "2.3.0", - "react-test-renderer": "16.2.0", "react-tooltip": "3.8.0", "react-virtualized": "9.20.1", "redux": "3.7.2", - "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", - "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200901140240", "scratch-l10n": "3.10.20200902030719", @@ -115,19 +81,52 @@ "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", "scratch-vm": "0.2.0-prerelease.20200820211625", - "selenium-webdriver": "3.6.0", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", - "svg-to-image": "1.1.3", "text-encoding": "0.7.0", "to-style": "1.3.3", - "uglifyjs-webpack-plugin": "^1.2.5", "wav-encoder": "1.3.0", + "xhr": "2.5.0" + }, + "peerDependencies": { + "react": "^16.0.0", + "react-dom": "^16.0.0" + }, + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/plugin-proposal-object-rest-spread": "^7.0.0", + "@babel/plugin-syntax-dynamic-import": "^7.0.0", + "@babel/plugin-transform-async-to-generator": "^7.1.0", + "@babel/preset-env": "^7.1.0", + "@babel/preset-react": "^7.0.0", + "babel-core": "7.0.0-bridge.0", + "babel-eslint": "^10.0.1", + "babel-loader": "^8.0.4", + "chromedriver": "84.0.1", + "enzyme": "^3.5.0", + "enzyme-adapter-react-16": "1.3.0", + "eslint": "^5.0.1", + "eslint-config-scratch": "^5.0.0", + "eslint-import-resolver-webpack": "^0.11.1", + "eslint-plugin-import": "^2.18.2", + "eslint-plugin-jest": "^22.14.1", + "eslint-plugin-react": "^7.12.4", + "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", + "html-webpack-plugin": "^3.2.0", + "jest": "^21.0.0", + "jest-junit": "^7.0.0", + "mkdirp": "^1.0.3", + "raf": "^3.4.0", + "react-test-renderer": "16.2.0", + "redux-mock-store": "^1.2.3", + "rimraf": "^2.6.1", + "selenium-webdriver": "3.6.0", + "uglifyjs-webpack-plugin": "^1.2.5", "web-audio-test-api": "^0.5.2", "webpack": "^4.6.0", "webpack-cli": "^3.1.0", - "webpack-dev-server": "^3.1.3", - "xhr": "2.5.0" + "webpack-dev-server": "^3.1.3" }, "jest": { "setupFiles": [ From 60c0d5d9b3f30b5b58003af2e4c11af264247cb9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 4 Sep 2020 10:36:41 +0000 Subject: [PATCH 1437/1971] Merge pull request #6161 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200903205312 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 0eadf3c52c..cadee19ffe 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -74,7 +74,7 @@ "redux": "3.7.2", "redux-throttle": "0.1.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200901140240", + "scratch-blocks": "0.1.0-prerelease.20200903205312", "scratch-l10n": "3.10.20200902030719", "scratch-paint": "0.2.0-prerelease.20200831213104", "scratch-render": "0.1.0-prerelease.20200827214414", From 07f43eb845ffc1cba19b90df15f9727966be0b20 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 4 Sep 2020 10:37:07 +0000 Subject: [PATCH 1438/1971] Merge pull request #6160 from LLK/dependabot/npm_and_yarn/scratch-vm-0.2.0-prerelease.20200903205543 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index cadee19ffe..eccc93c2c8 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -80,7 +80,7 @@ "scratch-render": "0.1.0-prerelease.20200827214414", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", - "scratch-vm": "0.2.0-prerelease.20200820211625", + "scratch-vm": "0.2.0-prerelease.20200903205543", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", "text-encoding": "0.7.0", From 45d1bd9e5c32559d13fa6d5a457790482df58b9c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 9 Sep 2020 13:55:26 +0000 Subject: [PATCH 1439/1971] Merge pull request #6171 from LLK/dependabot/npm_and_yarn/scratch-blocks-0.1.0-prerelease.20200908141031 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index eccc93c2c8..e812aeef85 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -74,7 +74,7 @@ "redux": "3.7.2", "redux-throttle": "0.1.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200903205312", + "scratch-blocks": "0.1.0-prerelease.20200908141031", "scratch-l10n": "3.10.20200902030719", "scratch-paint": "0.2.0-prerelease.20200831213104", "scratch-render": "0.1.0-prerelease.20200827214414", From a33596e854d33b81d80de624a8450fdf3bf45290 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 9 Sep 2020 21:19:43 +0000 Subject: [PATCH 1440/1971] Merge pull request #6174 from LLK/dependabot/npm_and_yarn/scratch-l10n-3.10.20200909030847 --- packages/scratch-gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index e812aeef85..804d8b6964 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -75,7 +75,7 @@ "redux-throttle": "0.1.1", "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200908141031", - "scratch-l10n": "3.10.20200902030719", + "scratch-l10n": "3.10.20200909030847", "scratch-paint": "0.2.0-prerelease.20200831213104", "scratch-render": "0.1.0-prerelease.20200827214414", "scratch-storage": "1.3.3", From 7e52e25b04ef6087fb519f89ea0e60539810f99f Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 18 Apr 2016 17:20:30 -0400 Subject: [PATCH 1441/1971] WIP --- packages/scratch-vm/src/engine/runtime.js | 119 ++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 packages/scratch-vm/src/engine/runtime.js diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js new file mode 100644 index 0000000000..e4f94f8f3e --- /dev/null +++ b/packages/scratch-vm/src/engine/runtime.js @@ -0,0 +1,119 @@ +var Primitives = require('./primatives'); +var Sequencer = require('./sequencer'); +var Thread = require('./thread'); + +var STEP_THREADS_INTERVAL = 1000 / 30; + +/** + * A simple runtime for blocks. + */ +function Runtime () { + this.sequencer = new Sequencer(this); + this.primitives = new Primitives(); + + // State + this.blocks = {}; + this.stacks = []; +} + +Runtime.prototype.createBlock = function (e) { + // Create new block + this.blocks[e.id] = { + id: e.id, + opcode: e.opcode, + next: null, + inputs: {} + }; + + // Push block id to stacks array. New blocks are always a stack even if only + // momentary. If the new block is added to an existing stack this stack will + // be removed by the `moveBlock` method below. + this.stacks.push(e.id); +}; + +Runtime.prototype.moveBlock = function (e) { + var _this = this; + + // Block has a new parent + if (e.oldParent === undefined && e.newParent !== undefined) { + // Remove stack + _this._deleteStack(e.id); + + // Update new parent + if (e.newInput === undefined) { + _this.blocks[e.newParent].next = e.id; + } else { + _this.blocks[e.newParent].inputs[e.newInput] = e.id; + } + } + + // Block was removed from parent + if (e.newParentId === undefined && e.oldParent !== undefined) { + // Add stack + _this.stacks.push(e.id); + + // Update old parent + if (e.oldInput === undefined) { + _this.blocks[e.oldParent].next = null; + } else { + delete _this.blocks[e.oldParent].inputs[e.oldInput]; + } + } +}; + +Runtime.prototype.changeBlock = function (e) { + // @todo +}; + +Runtime.prototype.deleteBlock = function (e) { + // @todo Stop threads running on this stack + + // Delete children + var block = this.blocks[e.id]; + if (block.next !== null) { + this.deleteBlock({id: block.next}); + } + + // Delete inputs + for (var i in block.inputs) { + this.deleteBlock({id: block.inputs[i]}); + } + + // Delete stack + this._deleteStack(e.id); + + // Delete block + delete this.blocks[e.id]; +}; + +Runtime.prototype.runAllStacks = function () { + // @todo +}; + +Runtime.prototype.runStack = function () { + // @todo +}; + +Runtime.prototype.stopAllStacks = function () { + // @todo +}; + +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- + +Runtime.prototype._deleteStack = function (id) { + var i = this.stacks.indexOf(id); + if (i > -1) this.stacks.splice(i, 1); +}; + +Runtime.prototype._getNextBlock = function (id) { + if (typeof this.blocks[id] === 'undefined') return null; + return this.blocks[id].next; +}; + +Runtime.prototype._getSubstack = function (id) { + if (typeof this.blocks[id] === 'undefined') return null; + return this.blocks[id].inputs['SUBSTACK']; +}; + +module.exports = Runtime; From 19927aea67c4da84c51463112809cf8de669f891 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 18 Apr 2016 18:03:18 -0400 Subject: [PATCH 1442/1971] WIP --- packages/scratch-vm/src/engine/runtime.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index e4f94f8f3e..6625fa4650 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1,3 +1,6 @@ +var EventEmitter = require('events'); +var util = require('util'); + var Primitives = require('./primatives'); var Sequencer = require('./sequencer'); var Thread = require('./thread'); @@ -8,6 +11,10 @@ var STEP_THREADS_INTERVAL = 1000 / 30; * A simple runtime for blocks. */ function Runtime () { + // Bind event emitter + EventEmitter.call(instance); + + // Instantiate sequencer and primitives this.sequencer = new Sequencer(this); this.primitives = new Primitives(); @@ -16,6 +23,11 @@ function Runtime () { this.stacks = []; } +/** + * Inherit from EventEmitter + */ +util.inherits(Runtime, EventEmitter); + Runtime.prototype.createBlock = function (e) { // Create new block this.blocks[e.id] = { @@ -90,8 +102,9 @@ Runtime.prototype.runAllStacks = function () { // @todo }; -Runtime.prototype.runStack = function () { +Runtime.prototype.runStack = function (e) { // @todo + console.dir(e); }; Runtime.prototype.stopAllStacks = function () { From c8ae3121ffed75517a98acb3c51029a762c8d5b0 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Tue, 26 Apr 2016 09:49:52 -0400 Subject: [PATCH 1443/1971] AST create / change / move / delete from blockly --- packages/scratch-vm/src/engine/runtime.js | 93 ++++++++++++----------- 1 file changed, 48 insertions(+), 45 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 6625fa4650..4c81faab60 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1,26 +1,19 @@ var EventEmitter = require('events'); var util = require('util'); -var Primitives = require('./primatives'); -var Sequencer = require('./sequencer'); -var Thread = require('./thread'); - -var STEP_THREADS_INTERVAL = 1000 / 30; - /** * A simple runtime for blocks. */ function Runtime () { // Bind event emitter - EventEmitter.call(instance); - - // Instantiate sequencer and primitives - this.sequencer = new Sequencer(this); - this.primitives = new Primitives(); + EventEmitter.call(this); // State this.blocks = {}; this.stacks = []; + + window._BLOCKS = this.blocks; + window._STACKS = this.stacks; } /** @@ -28,19 +21,34 @@ function Runtime () { */ util.inherits(Runtime, EventEmitter); -Runtime.prototype.createBlock = function (e) { +Runtime.prototype.createBlock = function (block) { // Create new block - this.blocks[e.id] = { - id: e.id, - opcode: e.opcode, - next: null, - inputs: {} - }; + this.blocks[block.id] = block; + + // Walk each field and add any shadow blocks + // @todo Expand this to cover vertical / nested blocks + for (var i in block.fields) { + var shadows = block.fields[i].blocks; + for (var y in shadows) { + var shadow = shadows[y]; + this.blocks[shadow.id] = shadow; + }; + } // Push block id to stacks array. New blocks are always a stack even if only // momentary. If the new block is added to an existing stack this stack will // be removed by the `moveBlock` method below. - this.stacks.push(e.id); + this.stacks.push(block.id); +}; + +Runtime.prototype.changeBlock = function (args) { + // Validate + if (args.element !== 'field') return; + if (typeof this.blocks[args.id] === 'undefined') return; + if (typeof this.blocks[args.id].fields[args.name] === 'undefined') return; + + // Update block value + this.blocks[args.id].fields[args.name].value = args.value; }; Runtime.prototype.moveBlock = function (e) { @@ -52,10 +60,14 @@ Runtime.prototype.moveBlock = function (e) { _this._deleteStack(e.id); // Update new parent - if (e.newInput === undefined) { + if (e.newField === undefined) { _this.blocks[e.newParent].next = e.id; } else { - _this.blocks[e.newParent].inputs[e.newInput] = e.id; + _this.blocks[e.newParent].fields[e.newField] = { + name: e.newField, + value: e.id, + blocks: {} + }; } } @@ -65,30 +77,34 @@ Runtime.prototype.moveBlock = function (e) { _this.stacks.push(e.id); // Update old parent - if (e.oldInput === undefined) { + if (e.oldField === undefined) { _this.blocks[e.oldParent].next = null; } else { - delete _this.blocks[e.oldParent].inputs[e.oldInput]; + delete _this.blocks[e.oldParent].fields[e.oldField]; } } }; -Runtime.prototype.changeBlock = function (e) { - // @todo -}; - Runtime.prototype.deleteBlock = function (e) { // @todo Stop threads running on this stack - // Delete children + // Get block var block = this.blocks[e.id]; + + // Delete children if (block.next !== null) { this.deleteBlock({id: block.next}); } - // Delete inputs - for (var i in block.inputs) { - this.deleteBlock({id: block.inputs[i]}); + // Delete substacks and fields + for (var field in block.fields) { + if (field === 'SUBSTACK') { + this.deleteBlock({id: block.fields[field].value}); + } else { + for (var shadow in block.fields[field].blocks) { + this.deleteBlock({id: shadow}); + } + } } // Delete stack @@ -98,19 +114,6 @@ Runtime.prototype.deleteBlock = function (e) { delete this.blocks[e.id]; }; -Runtime.prototype.runAllStacks = function () { - // @todo -}; - -Runtime.prototype.runStack = function (e) { - // @todo - console.dir(e); -}; - -Runtime.prototype.stopAllStacks = function () { - // @todo -}; - // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- @@ -126,7 +129,7 @@ Runtime.prototype._getNextBlock = function (id) { Runtime.prototype._getSubstack = function (id) { if (typeof this.blocks[id] === 'undefined') return null; - return this.blocks[id].inputs['SUBSTACK']; + return this.blocks[id].fields['SUBSTACK']; }; module.exports = Runtime; From 2f47912363b9aabb54bee0455e9315caccf3572b Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Tue, 26 Apr 2016 09:54:14 -0400 Subject: [PATCH 1444/1971] All tests passing --- packages/scratch-vm/src/engine/runtime.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 4c81faab60..62f9d8e51e 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -11,9 +11,6 @@ function Runtime () { // State this.blocks = {}; this.stacks = []; - - window._BLOCKS = this.blocks; - window._STACKS = this.stacks; } /** @@ -32,7 +29,7 @@ Runtime.prototype.createBlock = function (block) { for (var y in shadows) { var shadow = shadows[y]; this.blocks[shadow.id] = shadow; - }; + } } // Push block id to stacks array. New blocks are always a stack even if only From 7290556df3189e164a67ab5148fa8c0eb56cb088 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Tue, 26 Apr 2016 17:33:48 -0400 Subject: [PATCH 1445/1971] Merge pull request #4 from tmickel/bugfix/too-many-stacks Remove reference to old newParentId --- packages/scratch-vm/src/engine/runtime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 62f9d8e51e..ad7c21fb5d 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -69,7 +69,7 @@ Runtime.prototype.moveBlock = function (e) { } // Block was removed from parent - if (e.newParentId === undefined && e.oldParent !== undefined) { + if (e.newParent === undefined && e.oldParent !== undefined) { // Add stack _this.stacks.push(e.id); From 5872caf98ced4058e45470520619856beee0e718 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Fri, 29 Apr 2016 15:45:50 -0400 Subject: [PATCH 1446/1971] Merge pull request #5 from tmickel/feature/sequencing Add basics of threads and sequencing --- packages/scratch-vm/src/engine/runtime.js | 105 +++++++++++++++++++++- 1 file changed, 103 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index ad7c21fb5d..7e66abc311 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1,16 +1,39 @@ var EventEmitter = require('events'); +var Sequencer = require('./sequencer'); +var Thread = require('./thread'); var util = require('util'); /** - * A simple runtime for blocks. + * Manages blocks, stacks, and the sequencer. */ function Runtime () { // Bind event emitter EventEmitter.call(this); - // State + // State for the runtime + /** + * All blocks in the workspace. + * Keys are block IDs, values are metadata about the block. + * @type {Object.} + */ this.blocks = {}; + + /** + * All stacks in the workspace. + * A list of block IDs that represent stacks (first block in stack). + * @type {Array.} + */ this.stacks = []; + + /** + * A list of threads that are currently running in the VM. + * Threads are added when execution starts and pruned when execution ends. + * @type {Array.} + */ + this.threads = []; + + /** @type {!Sequencer} */ + this.sequencer = new Sequencer(this); } /** @@ -18,6 +41,15 @@ function Runtime () { */ util.inherits(Runtime, EventEmitter); +/** + * How rapidly we try to step threads, in ms. + */ +Runtime.THREAD_STEP_INTERVAL = 1000 / 60; + +/** + * Block management: create blocks and stacks from a `create` event + * @param {!Object} block Blockly create event to be processed + */ Runtime.prototype.createBlock = function (block) { // Create new block this.blocks[block.id] = block; @@ -38,6 +70,10 @@ Runtime.prototype.createBlock = function (block) { this.stacks.push(block.id); }; +/** + * Block management: change block field values + * @param {!Object} args Blockly change event to be processed + */ Runtime.prototype.changeBlock = function (args) { // Validate if (args.element !== 'field') return; @@ -48,6 +84,10 @@ Runtime.prototype.changeBlock = function (args) { this.blocks[args.id].fields[args.name].value = args.value; }; +/** + * Block management: move blocks from parent to parent + * @param {!Object} e Blockly move event to be processed + */ Runtime.prototype.moveBlock = function (e) { var _this = this; @@ -82,6 +122,10 @@ Runtime.prototype.moveBlock = function (e) { } }; +/** + * Block management: delete blocks and their associated stacks + * @param {!Object} e Blockly delete event to be processed + */ Runtime.prototype.deleteBlock = function (e) { // @todo Stop threads running on this stack @@ -114,16 +158,73 @@ Runtime.prototype.deleteBlock = function (e) { // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- +/** + * Create a thread and push it to the list of threads. + * @param {!string} id ID of block that starts the stack + */ +Runtime.prototype._pushThread = function (id) { + if (this.stacks.indexOf(id) < -1) return; + var thread = new Thread(id); + this.threads.push(thread); +}; + +/** + * Remove a thread from the list of threads. + * @param {!string} id ID of block that starts the stack + */ +Runtime.prototype._removeThread = function (id) { + var i = this.threads.indexOf(id); + if (i > -1) this.threads.splice(i, 1); +}; + +/** + * Repeatedly run `sequencer.stepThreads` and filter out + * inactive threads after each iteration. + */ +Runtime.prototype._step = function () { + var inactiveThreads = this.sequencer.stepThreads(this.threads); + for (var i = 0; i < inactiveThreads.length; i++) { + this._removeThread(inactiveThreads[i]); + } +}; + +/** + * Set up timers to repeatedly step in a browser + */ +Runtime.prototype.start = function () { + if (!window.setInterval) return; + window.setInterval(function() { + this._step(); + }.bind(this), Runtime.THREAD_STEP_INTERVAL); +}; + +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- + +/** + * Helper to remove a stack from `this.stacks` + * @param {?string} id ID of block that starts the stack + */ Runtime.prototype._deleteStack = function (id) { var i = this.stacks.indexOf(id); if (i > -1) this.stacks.splice(i, 1); }; +/** + * Helper to get the next block for a particular block + * @param {?string} id ID of block to get the next block for + * @return {?string} ID of next block in the sequence + */ Runtime.prototype._getNextBlock = function (id) { if (typeof this.blocks[id] === 'undefined') return null; return this.blocks[id].next; }; +/** + * Helper to get the substack for a particular C-shaped block + * @param {?string} id ID for block to get the substack for + * @return {?string} ID of block in the substack + */ Runtime.prototype._getSubstack = function (id) { if (typeof this.blocks[id] === 'undefined') return null; return this.blocks[id].fields['SUBSTACK']; From 6226e910a957b54534cae2f027d83b1bcdc6b6ac Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Fri, 29 Apr 2016 17:17:49 -0400 Subject: [PATCH 1447/1971] Merge pull request #9 from tmickel/feature/flyout-listening Add listener for new blocks that doesn't add to stacks --- packages/scratch-vm/src/engine/runtime.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 7e66abc311..629d34314e 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -50,7 +50,7 @@ Runtime.THREAD_STEP_INTERVAL = 1000 / 60; * Block management: create blocks and stacks from a `create` event * @param {!Object} block Blockly create event to be processed */ -Runtime.prototype.createBlock = function (block) { +Runtime.prototype.createBlock = function (block, opt_isFlyoutBlock) { // Create new block this.blocks[block.id] = block; @@ -67,7 +67,9 @@ Runtime.prototype.createBlock = function (block) { // Push block id to stacks array. New blocks are always a stack even if only // momentary. If the new block is added to an existing stack this stack will // be removed by the `moveBlock` method below. - this.stacks.push(block.id); + if (!opt_isFlyoutBlock) { + this.stacks.push(block.id); + } }; /** From 74935a449fac416c4971162208bcbbdb6307189d Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Mon, 2 May 2016 10:50:29 -0400 Subject: [PATCH 1448/1971] Merge pull request #10 from tmickel/feature/toggle-thread Fix _removeThread, add toggleStack, green flag, stop button functions --- packages/scratch-vm/src/engine/runtime.js | 50 +++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 629d34314e..2d0a2c0125 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -172,13 +172,57 @@ Runtime.prototype._pushThread = function (id) { /** * Remove a thread from the list of threads. - * @param {!string} id ID of block that starts the stack + * @param {?Thread} thread Thread object to remove from actives */ -Runtime.prototype._removeThread = function (id) { - var i = this.threads.indexOf(id); +Runtime.prototype._removeThread = function (thread) { + var i = this.threads.indexOf(thread); if (i > -1) this.threads.splice(i, 1); }; +/** + * Toggle a stack + * @param {!string} stackId ID of block that starts the stack + */ +Runtime.prototype.toggleStack = function (stackId) { + // Remove any existing thread + for (var i = 0; i < this.threads.length; i++) { + if (this.threads[i].topBlock == stackId) { + this._removeThread(this.threads[i]); + return; + } + } + // Otherwise add it + this._pushThread(stackId); +}; + +/** + * Green flag, which stops currently running threads + * and adds all top-level stacks that start with the green flag + */ +Runtime.prototype.greenFlag = function () { + // Remove all existing threads + for (var i = 0; i < this.threads.length; i++) { + this._removeThread(this.threads[i]); + } + // Add all top stacks with green flag + for (var j = 0; j < this.stacks.length; j++) { + var topBlock = this.stacks[j]; + if (this.blocks[topBlock].opcode === 'event_whenflagclicked') { + this._pushThread(this.stacks[j]); + } + } +}; + +/** + * Stop "everything" + */ +Runtime.prototype.stopAll = function () { + for (var i = 0; i < this.threads.length; i++) { + this._removeThread(this.threads[i]); + } + // @todo call stop function in all extensions/packages/WeDo stub +}; + /** * Repeatedly run `sequencer.stepThreads` and filter out * inactive threads after each iteration. From 14e5ad1d642904099a4db2f4496cae9d46268d2b Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Mon, 2 May 2016 14:07:19 -0400 Subject: [PATCH 1449/1971] Merge pull request #11 from tmickel/feature/blockly-feedback Block and stack glowing --- packages/scratch-vm/src/engine/runtime.js | 34 +++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 2d0a2c0125..c8b3988209 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -36,6 +36,30 @@ function Runtime () { this.sequencer = new Sequencer(this); } +/** + * Event name for glowing a stack + * @const {string} + */ +Runtime.STACK_GLOW_ON = 'STACK_GLOW_ON'; + +/** + * Event name for unglowing a stack + * @const {string} + */ +Runtime.STACK_GLOW_OFF = 'STACK_GLOW_OFF'; + +/** + * Event name for glowing a block + * @const {string} + */ +Runtime.BLOCK_GLOW_ON = 'BLOCK_GLOW_ON'; + +/** + * Event name for unglowing a block + * @const {string} + */ +Runtime.BLOCK_GLOW_OFF = 'BLOCK_GLOW_OFF'; + /** * Inherit from EventEmitter */ @@ -165,7 +189,10 @@ Runtime.prototype.deleteBlock = function (e) { * @param {!string} id ID of block that starts the stack */ Runtime.prototype._pushThread = function (id) { - if (this.stacks.indexOf(id) < -1) return; + if (this.stacks.indexOf(id) < -1) { + return; + } + this.emit(Runtime.STACK_GLOW_ON, id); var thread = new Thread(id); this.threads.push(thread); }; @@ -176,7 +203,10 @@ Runtime.prototype._pushThread = function (id) { */ Runtime.prototype._removeThread = function (thread) { var i = this.threads.indexOf(thread); - if (i > -1) this.threads.splice(i, 1); + if (i > -1) { + this.emit(Runtime.STACK_GLOW_OFF, thread.topBlock); + this.threads.splice(i, 1); + } }; /** From c51150bb749d9f536163cc7986591cac6aef7836 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Mon, 2 May 2016 15:27:51 -0400 Subject: [PATCH 1450/1971] Merge pull request #12 from cwillisf/feature/execute-blocks Allow the Sequencer to execute a JS function for each block --- packages/scratch-vm/src/engine/runtime.js | 56 +++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index c8b3988209..ac1652c7be 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -3,6 +3,11 @@ var Sequencer = require('./sequencer'); var Thread = require('./thread'); var util = require('util'); +var defaultBlockPackages = { + 'scratch3': require('../blocks/scratch3'), + 'wedo2': require('../blocks/wedo2') +}; + /** * Manages blocks, stacks, and the sequencer. */ @@ -34,6 +39,14 @@ function Runtime () { /** @type {!Sequencer} */ this.sequencer = new Sequencer(this); + + /** + * Map to look up a block primitive's implementation function by its opcode. + * This is a two-step lookup: package name first, then primitive name. + * @type {Object.} + */ + this._primitives = {}; + this._registerBlockPackages(); } /** @@ -184,6 +197,39 @@ Runtime.prototype.deleteBlock = function (e) { // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- +/** + * Register default block packages with this runtime. + * @todo Prefix opcodes with package name. + * @private + */ +Runtime.prototype._registerBlockPackages = function () { + for (var packageName in defaultBlockPackages) { + if (defaultBlockPackages.hasOwnProperty(packageName)) { + // @todo pass a different runtime depending on package privilege? + var packageObject = new (defaultBlockPackages[packageName])(this); + var packageContents = packageObject.getPrimitives(); + for (var op in packageContents) { + if (packageContents.hasOwnProperty(op)) { + this._primitives[op] = + packageContents[op].bind(packageObject); + } + } + } + } +}; + +/** + * Retrieve the function associated with the given opcode. + * @param {!string} opcode The opcode to look up. + * @return {Function} The function which implements the opcode. + */ +Runtime.prototype.getOpcodeFunction = function (opcode) { + return this._primitives[opcode]; +}; + +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- + /** * Create a thread and push it to the list of threads. * @param {!string} id ID of block that starts the stack @@ -306,4 +352,14 @@ Runtime.prototype._getSubstack = function (id) { return this.blocks[id].fields['SUBSTACK']; }; +/** + * Helper to get the opcode for a particular block + * @param {?string} id ID of block to query + * @return {?string} the opcode corresponding to that block + */ +Runtime.prototype._getOpcode = function (id) { + if (typeof this.blocks[id] === 'undefined') return null; + return this.blocks[id].opcode; +}; + module.exports = Runtime; From 10a6d5ee8b5b58d17a757ba0dcaac6c8be26c0c9 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Tue, 3 May 2016 11:25:22 -0400 Subject: [PATCH 1451/1971] Merge pull request #16 from tmickel/bugfix/fix-move Shuffle moveBlock to allow case where e.oldParent !== undefined --- packages/scratch-vm/src/engine/runtime.js | 32 ++++++++++++----------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index ac1652c7be..88cc2989eb 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -130,8 +130,23 @@ Runtime.prototype.changeBlock = function (args) { Runtime.prototype.moveBlock = function (e) { var _this = this; - // Block has a new parent - if (e.oldParent === undefined && e.newParent !== undefined) { + // Block was removed from parent + if (e.newParent === undefined && e.oldParent !== undefined) { + // Add stack + _this.stacks.push(e.id); + + // Update old parent + if (e.oldField === undefined) { + _this.blocks[e.oldParent].next = null; + } else { + delete _this.blocks[e.oldParent].fields[e.oldField]; + } + } else if (e.newParent !== undefined) { + // Block was moved to a new parent + // Either happens because it was previously parentless + // (e.oldParent === undefined) + // or because a block was moved in front of it. + // Remove stack _this._deleteStack(e.id); @@ -146,19 +161,6 @@ Runtime.prototype.moveBlock = function (e) { }; } } - - // Block was removed from parent - if (e.newParent === undefined && e.oldParent !== undefined) { - // Add stack - _this.stacks.push(e.id); - - // Update old parent - if (e.oldField === undefined) { - _this.blocks[e.oldParent].next = null; - } else { - delete _this.blocks[e.oldParent].fields[e.oldField]; - } - } }; /** From 43e93450f5cb6705a9ebfb18f62572abac991175 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Tue, 3 May 2016 11:27:14 -0400 Subject: [PATCH 1452/1971] Merge pull request #13 from tmickel/feature/s3-blocks Implement thread status, yield timers, block glow, wait --- packages/scratch-vm/src/engine/runtime.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 88cc2989eb..e3072a9ed5 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -312,6 +312,19 @@ Runtime.prototype._step = function () { } }; +/** + * Emit feedback for block glowing (used in the sequencer). + * @param {?string} blockId ID for the block to update glow + * @param {boolean} isGlowing True to turn on glow; false to turn off. + */ +Runtime.prototype.glowBlock = function (blockId, isGlowing) { + if (isGlowing) { + this.emit(Runtime.BLOCK_GLOW_ON, blockId); + } else { + this.emit(Runtime.BLOCK_GLOW_OFF, blockId); + } +}; + /** * Set up timers to repeatedly step in a browser */ From 828d952eee575d3f96fe349208ffcb86dfab3358 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Tue, 3 May 2016 14:14:57 -0400 Subject: [PATCH 1453/1971] Merge pull request #20 from tmickel/bugfix/stops Fix Runtime.stopAll --- packages/scratch-vm/src/engine/runtime.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index e3072a9ed5..c965be42ba 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -295,8 +295,9 @@ Runtime.prototype.greenFlag = function () { * Stop "everything" */ Runtime.prototype.stopAll = function () { - for (var i = 0; i < this.threads.length; i++) { - this._removeThread(this.threads[i]); + var threadsCopy = this.threads.slice(); + while (threadsCopy.length > 0) { + this._removeThread(threadsCopy.pop()); } // @todo call stop function in all extensions/packages/WeDo stub }; From 7b5087603782ffe83dc81a8131444193ec084e62 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Tue, 3 May 2016 15:58:38 -0400 Subject: [PATCH 1454/1971] Merge pull request #28 from tmickel/feature/flyout-run Allow pushing threads that are not on this.stacks --- packages/scratch-vm/src/engine/runtime.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index c965be42ba..1360826914 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -237,9 +237,6 @@ Runtime.prototype.getOpcodeFunction = function (opcode) { * @param {!string} id ID of block that starts the stack */ Runtime.prototype._pushThread = function (id) { - if (this.stacks.indexOf(id) < -1) { - return; - } this.emit(Runtime.STACK_GLOW_ON, id); var thread = new Thread(id); this.threads.push(thread); From d30eae1573c2110e0faa68c1b9485191bec9ce36 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Tue, 3 May 2016 17:40:47 -0400 Subject: [PATCH 1455/1971] Merge pull request #33 from tmickel/feature/wedo-fixes A few WeDo fixes --- packages/scratch-vm/src/engine/runtime.js | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 1360826914..2ce2c6354a 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -288,6 +288,27 @@ Runtime.prototype.greenFlag = function () { } }; +/** + * Distance sensor hack + */ +Runtime.prototype.startDistanceSensors = function () { + // Add all top stacks with distance sensor + for (var j = 0; j < this.stacks.length; j++) { + var topBlock = this.stacks[j]; + if (this.blocks[topBlock].opcode === 'wedo_whendistanceclose') { + var alreadyRunning = false; + for (var k = 0; k < this.threads.length; k++) { + if (this.threads[k].topBlock === topBlock) { + alreadyRunning = true; + } + } + if (!alreadyRunning) { + this._pushThread(this.stacks[j]); + } + } + } +}; + /** * Stop "everything" */ @@ -297,6 +318,9 @@ Runtime.prototype.stopAll = function () { this._removeThread(threadsCopy.pop()); } // @todo call stop function in all extensions/packages/WeDo stub + if (window.native) { + window.native.motorStop(); + } }; /** From a9afb72aa8e823db3996b0713e21b23267bc21e7 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Thu, 5 May 2016 13:10:48 -0400 Subject: [PATCH 1456/1971] Merge pull request #37 from tmickel/feature/wedo-timing Reduce block execution time (fix #35) --- packages/scratch-vm/src/engine/runtime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 2ce2c6354a..0507b96b51 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -81,7 +81,7 @@ util.inherits(Runtime, EventEmitter); /** * How rapidly we try to step threads, in ms. */ -Runtime.THREAD_STEP_INTERVAL = 1000 / 60; +Runtime.THREAD_STEP_INTERVAL = 1000 / 30; /** * Block management: create blocks and stacks from a `create` event From 62a40d3a85071c6d6dd72cdfe65e0944619dd2d2 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Tue, 7 Jun 2016 13:27:41 -0400 Subject: [PATCH 1457/1971] Merge pull request #57 from tmickel/feature/full-tree Update for VM block management --- packages/scratch-vm/src/engine/runtime.js | 188 ++-------------------- 1 file changed, 15 insertions(+), 173 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 0507b96b51..9531eadc73 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -10,25 +10,18 @@ var defaultBlockPackages = { /** * Manages blocks, stacks, and the sequencer. + * @param blocks Blocks instance for this runtime. */ -function Runtime () { +function Runtime (blocks) { // Bind event emitter EventEmitter.call(this); // State for the runtime - /** - * All blocks in the workspace. - * Keys are block IDs, values are metadata about the block. - * @type {Object.} - */ - this.blocks = {}; /** - * All stacks in the workspace. - * A list of block IDs that represent stacks (first block in stack). - * @type {Array.} + * Block management and storage */ - this.stacks = []; + this.blocks = blocks; /** * A list of threads that are currently running in the VM. @@ -83,118 +76,6 @@ util.inherits(Runtime, EventEmitter); */ Runtime.THREAD_STEP_INTERVAL = 1000 / 30; -/** - * Block management: create blocks and stacks from a `create` event - * @param {!Object} block Blockly create event to be processed - */ -Runtime.prototype.createBlock = function (block, opt_isFlyoutBlock) { - // Create new block - this.blocks[block.id] = block; - - // Walk each field and add any shadow blocks - // @todo Expand this to cover vertical / nested blocks - for (var i in block.fields) { - var shadows = block.fields[i].blocks; - for (var y in shadows) { - var shadow = shadows[y]; - this.blocks[shadow.id] = shadow; - } - } - - // Push block id to stacks array. New blocks are always a stack even if only - // momentary. If the new block is added to an existing stack this stack will - // be removed by the `moveBlock` method below. - if (!opt_isFlyoutBlock) { - this.stacks.push(block.id); - } -}; - -/** - * Block management: change block field values - * @param {!Object} args Blockly change event to be processed - */ -Runtime.prototype.changeBlock = function (args) { - // Validate - if (args.element !== 'field') return; - if (typeof this.blocks[args.id] === 'undefined') return; - if (typeof this.blocks[args.id].fields[args.name] === 'undefined') return; - - // Update block value - this.blocks[args.id].fields[args.name].value = args.value; -}; - -/** - * Block management: move blocks from parent to parent - * @param {!Object} e Blockly move event to be processed - */ -Runtime.prototype.moveBlock = function (e) { - var _this = this; - - // Block was removed from parent - if (e.newParent === undefined && e.oldParent !== undefined) { - // Add stack - _this.stacks.push(e.id); - - // Update old parent - if (e.oldField === undefined) { - _this.blocks[e.oldParent].next = null; - } else { - delete _this.blocks[e.oldParent].fields[e.oldField]; - } - } else if (e.newParent !== undefined) { - // Block was moved to a new parent - // Either happens because it was previously parentless - // (e.oldParent === undefined) - // or because a block was moved in front of it. - - // Remove stack - _this._deleteStack(e.id); - - // Update new parent - if (e.newField === undefined) { - _this.blocks[e.newParent].next = e.id; - } else { - _this.blocks[e.newParent].fields[e.newField] = { - name: e.newField, - value: e.id, - blocks: {} - }; - } - } -}; - -/** - * Block management: delete blocks and their associated stacks - * @param {!Object} e Blockly delete event to be processed - */ -Runtime.prototype.deleteBlock = function (e) { - // @todo Stop threads running on this stack - - // Get block - var block = this.blocks[e.id]; - - // Delete children - if (block.next !== null) { - this.deleteBlock({id: block.next}); - } - - // Delete substacks and fields - for (var field in block.fields) { - if (field === 'SUBSTACK') { - this.deleteBlock({id: block.fields[field].value}); - } else { - for (var shadow in block.fields[field].blocks) { - this.deleteBlock({id: shadow}); - } - } - } - - // Delete stack - this._deleteStack(e.id); - - // Delete block - delete this.blocks[e.id]; -}; // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- @@ -280,10 +161,11 @@ Runtime.prototype.greenFlag = function () { this._removeThread(this.threads[i]); } // Add all top stacks with green flag - for (var j = 0; j < this.stacks.length; j++) { - var topBlock = this.stacks[j]; - if (this.blocks[topBlock].opcode === 'event_whenflagclicked') { - this._pushThread(this.stacks[j]); + var stacks = this.blocks.getStacks(); + for (var j = 0; j < stacks.length; j++) { + var topBlock = stacks[j]; + if (this.blocks.getBlock(topBlock).opcode === 'event_whenflagclicked') { + this._pushThread(stacks[j]); } } }; @@ -293,9 +175,11 @@ Runtime.prototype.greenFlag = function () { */ Runtime.prototype.startDistanceSensors = function () { // Add all top stacks with distance sensor - for (var j = 0; j < this.stacks.length; j++) { - var topBlock = this.stacks[j]; - if (this.blocks[topBlock].opcode === 'wedo_whendistanceclose') { + var stacks = this.blocks.getStacks(); + for (var j = 0; j < stacks.length; j++) { + var topBlock = stacks[j]; + if (this.blocks.getBlock(topBlock).opcode === + 'wedo_whendistanceclose') { var alreadyRunning = false; for (var k = 0; k < this.threads.length; k++) { if (this.threads[k].topBlock === topBlock) { @@ -303,7 +187,7 @@ Runtime.prototype.startDistanceSensors = function () { } } if (!alreadyRunning) { - this._pushThread(this.stacks[j]); + this._pushThread(stacks[j]); } } } @@ -357,46 +241,4 @@ Runtime.prototype.start = function () { }.bind(this), Runtime.THREAD_STEP_INTERVAL); }; -// ----------------------------------------------------------------------------- -// ----------------------------------------------------------------------------- - -/** - * Helper to remove a stack from `this.stacks` - * @param {?string} id ID of block that starts the stack - */ -Runtime.prototype._deleteStack = function (id) { - var i = this.stacks.indexOf(id); - if (i > -1) this.stacks.splice(i, 1); -}; - -/** - * Helper to get the next block for a particular block - * @param {?string} id ID of block to get the next block for - * @return {?string} ID of next block in the sequence - */ -Runtime.prototype._getNextBlock = function (id) { - if (typeof this.blocks[id] === 'undefined') return null; - return this.blocks[id].next; -}; - -/** - * Helper to get the substack for a particular C-shaped block - * @param {?string} id ID for block to get the substack for - * @return {?string} ID of block in the substack - */ -Runtime.prototype._getSubstack = function (id) { - if (typeof this.blocks[id] === 'undefined') return null; - return this.blocks[id].fields['SUBSTACK']; -}; - -/** - * Helper to get the opcode for a particular block - * @param {?string} id ID of block to query - * @return {?string} the opcode corresponding to that block - */ -Runtime.prototype._getOpcode = function (id) { - if (typeof this.blocks[id] === 'undefined') return null; - return this.blocks[id].opcode; -}; - module.exports = Runtime; From f2b2546d6156fd65a20cbb5e319c8ab754121cfe Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Wed, 8 Jun 2016 17:15:36 -0400 Subject: [PATCH 1458/1971] Merge pull request #60 from tmickel/feature/vm-playground-execution Instrument VM and playground for better execution debugging --- packages/scratch-vm/src/engine/runtime.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 9531eadc73..c4aa06e498 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -199,7 +199,13 @@ Runtime.prototype.startDistanceSensors = function () { Runtime.prototype.stopAll = function () { var threadsCopy = this.threads.slice(); while (threadsCopy.length > 0) { - this._removeThread(threadsCopy.pop()); + var poppedThread = threadsCopy.pop(); + // Unglow any blocks on this thread's stack. + for (var i = 0; i < poppedThread.stack.length; i++) { + this.glowBlock(poppedThread.stack[i], false); + } + // Actually remove the thread. + this._removeThread(poppedThread); } // @todo call stop function in all extensions/packages/WeDo stub if (window.native) { From a9ae9ea5e535a64b26fe20f90d1c3807402716ab Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Wed, 8 Jun 2016 17:27:14 -0400 Subject: [PATCH 1459/1971] Merge pull request #67 from tmickel/feature/eslint-jsdoc Add .eslintrc rule to check JSDoc syntax --- packages/scratch-vm/src/engine/runtime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index c4aa06e498..930fa0ea5d 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -10,7 +10,7 @@ var defaultBlockPackages = { /** * Manages blocks, stacks, and the sequencer. - * @param blocks Blocks instance for this runtime. + * @param {!Blocks} blocks Blocks instance for this runtime. */ function Runtime (blocks) { // Bind event emitter From d84b1750b890e22714d40f54b04cd8c028541943 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Thu, 9 Jun 2016 11:53:21 -0400 Subject: [PATCH 1460/1971] Merge pull request #70 from tmickel/enhancement/execution-refactors Move scratch3 blocks into separate packages. --- packages/scratch-vm/src/engine/runtime.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 930fa0ea5d..5b4fab48d8 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -4,7 +4,8 @@ var Thread = require('./thread'); var util = require('util'); var defaultBlockPackages = { - 'scratch3': require('../blocks/scratch3'), + 'scratch3_control': require('../blocks/scratch3_control'), + 'scratch3_event': require('../blocks/scratch3_event'), 'wedo2': require('../blocks/wedo2') }; From 1742f55ea536afdab2692ae62f36180f6d8f2601 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Mon, 13 Jun 2016 11:05:00 -0400 Subject: [PATCH 1461/1971] Merge pull request #72 from tmickel/feature/execute-with-args Refactor for block execution --- packages/scratch-vm/src/engine/runtime.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 5b4fab48d8..aff2a3aecb 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -6,6 +6,7 @@ var util = require('util'); var defaultBlockPackages = { 'scratch3_control': require('../blocks/scratch3_control'), 'scratch3_event': require('../blocks/scratch3_event'), + 'scratch3_operators': require('../blocks/scratch3_operators'), 'wedo2': require('../blocks/wedo2') }; @@ -121,6 +122,7 @@ Runtime.prototype.getOpcodeFunction = function (opcode) { Runtime.prototype._pushThread = function (id) { this.emit(Runtime.STACK_GLOW_ON, id); var thread = new Thread(id); + thread.pushStack(id); this.threads.push(thread); }; @@ -231,6 +233,9 @@ Runtime.prototype._step = function () { * @param {boolean} isGlowing True to turn on glow; false to turn off. */ Runtime.prototype.glowBlock = function (blockId, isGlowing) { + if (!this.blocks.getBlock(blockId)) { + return; + } if (isGlowing) { this.emit(Runtime.BLOCK_GLOW_ON, blockId); } else { From f5f23e3b4736df07ae0a024b97d48b8e04747afe Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Wed, 22 Jun 2016 12:20:22 -0400 Subject: [PATCH 1462/1971] Merge pull request #85 from tmickel/feature/worker Update to allow running in a WebWorker --- packages/scratch-vm/src/engine/runtime.js | 25 +++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index aff2a3aecb..f6ad79f0b8 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -210,10 +210,6 @@ Runtime.prototype.stopAll = function () { // Actually remove the thread. this._removeThread(poppedThread); } - // @todo call stop function in all extensions/packages/WeDo stub - if (window.native) { - window.native.motorStop(); - } }; /** @@ -243,12 +239,29 @@ Runtime.prototype.glowBlock = function (blockId, isGlowing) { } }; +/** + * setInterval implementation that works in a WebWorker or not. + * @param {?Function} fcn Function to call. + * @param {number} interval Interval at which to call it. + * @return {number} Value returned by setInterval. + */ +Runtime.prototype._setInterval = function(fcn, interval) { + var setInterval = null; + if (typeof window !== 'undefined' && window.setInterval) { + setInterval = window.setInterval; + } else if (typeof self !== 'undefined' && self.setInterval) { + setInterval = self.setInterval; + } else { + return; + } + return setInterval(fcn, interval); +}; + /** * Set up timers to repeatedly step in a browser */ Runtime.prototype.start = function () { - if (!window.setInterval) return; - window.setInterval(function() { + this._setInterval(function() { this._step(); }.bind(this), Runtime.THREAD_STEP_INTERVAL); }; From 99ab5c776eb9362b1b0d1eceab9c21412400556a Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Thu, 30 Jun 2016 18:58:03 -0400 Subject: [PATCH 1463/1971] Merge pull request #83 from tmickel/feature/yielding-reporters Yielding reporters, blocking yields --- packages/scratch-vm/src/engine/runtime.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index f6ad79f0b8..8c4c862e16 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -6,8 +6,7 @@ var util = require('util'); var defaultBlockPackages = { 'scratch3_control': require('../blocks/scratch3_control'), 'scratch3_event': require('../blocks/scratch3_event'), - 'scratch3_operators': require('../blocks/scratch3_operators'), - 'wedo2': require('../blocks/wedo2') + 'scratch3_operators': require('../blocks/scratch3_operators') }; /** From a366d877b37c812accc07f4026b33ff7ea370514 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Tue, 9 Aug 2016 07:50:38 -0400 Subject: [PATCH 1464/1971] Merge pull request #87 from tmickel/feature/sprites Straw-man implementation of targets/sprites/clones --- packages/scratch-vm/src/engine/runtime.js | 101 +++++++++++----------- 1 file changed, 52 insertions(+), 49 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 8c4c862e16..f92a91db76 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -6,23 +6,25 @@ var util = require('util'); var defaultBlockPackages = { 'scratch3_control': require('../blocks/scratch3_control'), 'scratch3_event': require('../blocks/scratch3_event'), + 'scratch3_looks': require('../blocks/scratch3_looks'), + 'scratch3_motion': require('../blocks/scratch3_motion'), 'scratch3_operators': require('../blocks/scratch3_operators') }; /** - * Manages blocks, stacks, and the sequencer. - * @param {!Blocks} blocks Blocks instance for this runtime. + * Manages targets, stacks, and the sequencer. + * @param {!Array.} targets List of targets for this runtime. */ -function Runtime (blocks) { +function Runtime (targets) { // Bind event emitter EventEmitter.call(this); // State for the runtime /** - * Block management and storage + * Target management and storage. */ - this.blocks = blocks; + this.targets = targets; /** * A list of threads that are currently running in the VM. @@ -67,6 +69,12 @@ Runtime.BLOCK_GLOW_ON = 'BLOCK_GLOW_ON'; */ Runtime.BLOCK_GLOW_OFF = 'BLOCK_GLOW_OFF'; +/** + * Event name for visual value report. + * @const {string} + */ +Runtime.VISUAL_REPORT = 'VISUAL_REPORT'; + /** * Inherit from EventEmitter */ @@ -75,7 +83,7 @@ util.inherits(Runtime, EventEmitter); /** * How rapidly we try to step threads, in ms. */ -Runtime.THREAD_STEP_INTERVAL = 1000 / 30; +Runtime.THREAD_STEP_INTERVAL = 1000 / 60; // ----------------------------------------------------------------------------- @@ -163,32 +171,13 @@ Runtime.prototype.greenFlag = function () { this._removeThread(this.threads[i]); } // Add all top stacks with green flag - var stacks = this.blocks.getStacks(); - for (var j = 0; j < stacks.length; j++) { - var topBlock = stacks[j]; - if (this.blocks.getBlock(topBlock).opcode === 'event_whenflagclicked') { - this._pushThread(stacks[j]); - } - } -}; - -/** - * Distance sensor hack - */ -Runtime.prototype.startDistanceSensors = function () { - // Add all top stacks with distance sensor - var stacks = this.blocks.getStacks(); - for (var j = 0; j < stacks.length; j++) { - var topBlock = stacks[j]; - if (this.blocks.getBlock(topBlock).opcode === - 'wedo_whendistanceclose') { - var alreadyRunning = false; - for (var k = 0; k < this.threads.length; k++) { - if (this.threads[k].topBlock === topBlock) { - alreadyRunning = true; - } - } - if (!alreadyRunning) { + for (var t = 0; t < this.targets.length; t++) { + var target = this.targets[t]; + var stacks = target.blocks.getStacks(); + for (var j = 0; j < stacks.length; j++) { + var topBlock = stacks[j]; + if (target.blocks.getBlock(topBlock).opcode === + 'event_whenflagclicked') { this._pushThread(stacks[j]); } } @@ -228,9 +217,6 @@ Runtime.prototype._step = function () { * @param {boolean} isGlowing True to turn on glow; false to turn off. */ Runtime.prototype.glowBlock = function (blockId, isGlowing) { - if (!this.blocks.getBlock(blockId)) { - return; - } if (isGlowing) { this.emit(Runtime.BLOCK_GLOW_ON, blockId); } else { @@ -239,28 +225,45 @@ Runtime.prototype.glowBlock = function (blockId, isGlowing) { }; /** - * setInterval implementation that works in a WebWorker or not. - * @param {?Function} fcn Function to call. - * @param {number} interval Interval at which to call it. - * @return {number} Value returned by setInterval. + * Emit value for reporter to show in the blocks. + * @param {string} blockId ID for the block. + * @param {string} value Value to show associated with the block. */ -Runtime.prototype._setInterval = function(fcn, interval) { - var setInterval = null; - if (typeof window !== 'undefined' && window.setInterval) { - setInterval = window.setInterval; - } else if (typeof self !== 'undefined' && self.setInterval) { - setInterval = self.setInterval; - } else { - return; +Runtime.prototype.visualReport = function (blockId, value) { + this.emit(Runtime.VISUAL_REPORT, blockId, String(value)); +}; + +/** + * Return the Target for a particular thread. + * @param {!Thread} thread Thread to determine target for. + * @return {?Target} Target object, if one exists. + */ +Runtime.prototype.targetForThread = function (thread) { + // @todo This is a messy solution, + // but prevents having circular data references. + // Have a map or some other way to associate target with threads. + for (var t = 0; t < this.targets.length; t++) { + var target = this.targets[t]; + if (target.blocks.getBlock(thread.topBlock)) { + return target; + } + } +}; + +/** + * Handle an animation frame from the main thread. + */ +Runtime.prototype.animationFrame = function () { + if (self.renderer) { + self.renderer.draw(); } - return setInterval(fcn, interval); }; /** * Set up timers to repeatedly step in a browser */ Runtime.prototype.start = function () { - this._setInterval(function() { + self.setInterval(function() { this._step(); }.bind(this), Runtime.THREAD_STEP_INTERVAL); }; From 68a3829c20df8113c427c775bf137aa0b3ecf22e Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Thu, 11 Aug 2016 15:45:18 -0400 Subject: [PATCH 1465/1971] Merge pull request #100 from tmickel/refactor/stacks-to-scripts Rename `stacks` to `scripts` --- packages/scratch-vm/src/engine/runtime.js | 28 +++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index f92a91db76..c32a72115e 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -12,7 +12,7 @@ var defaultBlockPackages = { }; /** - * Manages targets, stacks, and the sequencer. + * Manages targets, scripts, and the sequencer. * @param {!Array.} targets List of targets for this runtime. */ function Runtime (targets) { @@ -146,39 +146,39 @@ Runtime.prototype._removeThread = function (thread) { }; /** - * Toggle a stack - * @param {!string} stackId ID of block that starts the stack + * Toggle a script. + * @param {!string} topBlockId ID of block that starts the script. */ -Runtime.prototype.toggleStack = function (stackId) { - // Remove any existing thread +Runtime.prototype.toggleScript = function (topBlockId) { + // Remove any existing thread. for (var i = 0; i < this.threads.length; i++) { - if (this.threads[i].topBlock == stackId) { + if (this.threads[i].topBlock == topBlockId) { this._removeThread(this.threads[i]); return; } } - // Otherwise add it - this._pushThread(stackId); + // Otherwise add it. + this._pushThread(topBlockId); }; /** * Green flag, which stops currently running threads - * and adds all top-level stacks that start with the green flag + * and adds all top-level scripts that start with the green flag */ Runtime.prototype.greenFlag = function () { // Remove all existing threads for (var i = 0; i < this.threads.length; i++) { this._removeThread(this.threads[i]); } - // Add all top stacks with green flag + // Add all top scripts with green flag for (var t = 0; t < this.targets.length; t++) { var target = this.targets[t]; - var stacks = target.blocks.getStacks(); - for (var j = 0; j < stacks.length; j++) { - var topBlock = stacks[j]; + var scripts = target.blocks.getScripts(); + for (var j = 0; j < scripts.length; j++) { + var topBlock = scripts[j]; if (target.blocks.getBlock(topBlock).opcode === 'event_whenflagclicked') { - this._pushThread(stacks[j]); + this._pushThread(scripts[j]); } } } From 2b1b425e0583d25e4de12ccc2edcba5b18c30c51 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Tue, 16 Aug 2016 15:59:14 -0400 Subject: [PATCH 1466/1971] Merge pull request #104 from tmickel/feature/io VM "I/O devices"; clock, mouse as demo --- packages/scratch-vm/src/engine/runtime.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index c32a72115e..d244544aae 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -3,12 +3,17 @@ var Sequencer = require('./sequencer'); var Thread = require('./thread'); var util = require('util'); +// Virtual I/O devices. +var Clock = require('../io/clock'); +var Mouse = require('../io/mouse'); + var defaultBlockPackages = { 'scratch3_control': require('../blocks/scratch3_control'), 'scratch3_event': require('../blocks/scratch3_event'), 'scratch3_looks': require('../blocks/scratch3_looks'), 'scratch3_motion': require('../blocks/scratch3_motion'), - 'scratch3_operators': require('../blocks/scratch3_operators') + 'scratch3_operators': require('../blocks/scratch3_operators'), + 'scratch3_sensing': require('../blocks/scratch3_sensing') }; /** @@ -43,6 +48,11 @@ function Runtime (targets) { */ this._primitives = {}; this._registerBlockPackages(); + + this.ioDevices = { + 'clock': new Clock(), + 'mouse': new Mouse() + }; } /** From 7256ebddcc01526dd2944f79606127709afa033c Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Fri, 2 Sep 2016 11:12:07 -0400 Subject: [PATCH 1467/1971] Merge pull request #107 from tmickel/feature/hats Hat blocks --- packages/scratch-vm/src/engine/runtime.js | 200 +++++++++++++++++++--- 1 file changed, 179 insertions(+), 21 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index d244544aae..921839497e 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -47,6 +47,8 @@ function Runtime (targets) { * @type {Object.} */ this._primitives = {}; + this._hats = {}; + this._edgeActivatedHatValues = {}; this._registerBlockPackages(); this.ioDevices = { @@ -109,11 +111,23 @@ Runtime.prototype._registerBlockPackages = function () { if (defaultBlockPackages.hasOwnProperty(packageName)) { // @todo pass a different runtime depending on package privilege? var packageObject = new (defaultBlockPackages[packageName])(this); - var packageContents = packageObject.getPrimitives(); - for (var op in packageContents) { - if (packageContents.hasOwnProperty(op)) { - this._primitives[op] = - packageContents[op].bind(packageObject); + // Collect primitives from package. + if (packageObject.getPrimitives) { + var packagePrimitives = packageObject.getPrimitives(); + for (var op in packagePrimitives) { + if (packagePrimitives.hasOwnProperty(op)) { + this._primitives[op] = + packagePrimitives[op].bind(packageObject); + } + } + } + // Collect hat metadata from package. + if (packageObject.getHats) { + var packageHats = packageObject.getHats(); + for (var hatName in packageHats) { + if (packageHats.hasOwnProperty(hatName)) { + this._hats[hatName] = packageHats[hatName]; + } } } } @@ -132,15 +146,58 @@ Runtime.prototype.getOpcodeFunction = function (opcode) { // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- +/** + * Return whether an opcode represents a hat block. + * @param {!string} opcode The opcode to look up. + * @return {Boolean} True if the op is known to be a hat. + */ +Runtime.prototype.getIsHat = function (opcode) { + return this._hats.hasOwnProperty(opcode); +}; + +/** + * Return whether an opcode represents an edge-activated hat block. + * @param {!string} opcode The opcode to look up. + * @return {Boolean} True if the op is known to be a edge-activated hat. + */ +Runtime.prototype.getIsEdgeActivatedHat = function (opcode) { + return this._hats.hasOwnProperty(opcode) && + this._hats[opcode].edgeActivated; +}; + +/** + * Update an edge-activated hat block value. + * @param {!string} blockId ID of hat to store value for. + * @param {*} newValue Value to store for edge-activated hat. + * @return {*} The old value for the edge-activated hat. + */ +Runtime.prototype.updateEdgeActivatedValue = function (blockId, newValue) { + var oldValue = this._edgeActivatedHatValues[blockId]; + this._edgeActivatedHatValues[blockId] = newValue; + return oldValue; +}; + +/** + * Clear all edge-activaed hat values. + */ +Runtime.prototype.clearEdgeActivatedValues = function () { + this._edgeActivatedHatValues = {}; +}; + +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- + /** * Create a thread and push it to the list of threads. * @param {!string} id ID of block that starts the stack + * @return {!Thread} The newly created thread. */ Runtime.prototype._pushThread = function (id) { - this.emit(Runtime.STACK_GLOW_ON, id); var thread = new Thread(id); + this.glowScript(id, true); thread.pushStack(id); this.threads.push(thread); + return thread; }; /** @@ -150,11 +207,20 @@ Runtime.prototype._pushThread = function (id) { Runtime.prototype._removeThread = function (thread) { var i = this.threads.indexOf(thread); if (i > -1) { - this.emit(Runtime.STACK_GLOW_OFF, thread.topBlock); + this.glowScript(thread.topBlock, false); this.threads.splice(i, 1); } }; +/** + * Return whether a thread is currently active/running. + * @param {?Thread} thread Thread object to check. + * @return {Boolean} True if the thread is active/running. + */ +Runtime.prototype.isActiveThread = function (thread) { + return this.threads.indexOf(thread) > -1; +}; + /** * Toggle a script. * @param {!string} topBlockId ID of block that starts the script. @@ -172,28 +238,100 @@ Runtime.prototype.toggleScript = function (topBlockId) { }; /** - * Green flag, which stops currently running threads - * and adds all top-level scripts that start with the green flag + * Run a function `f` for all scripts in a workspace. + * `f` will be called with two parameters: + * - the top block ID of the script. + * - the target that owns the script. + * @param {!Function} f Function to call for each script. + * @param {Target=} opt_target Optionally, a target to restrict to. */ -Runtime.prototype.greenFlag = function () { - // Remove all existing threads - for (var i = 0; i < this.threads.length; i++) { - this._removeThread(this.threads[i]); +Runtime.prototype.allScriptsDo = function (f, opt_target) { + var targets = this.targets; + if (opt_target) { + targets = [opt_target]; } - // Add all top scripts with green flag - for (var t = 0; t < this.targets.length; t++) { - var target = this.targets[t]; + for (var t = 0; t < targets.length; t++) { + var target = targets[t]; var scripts = target.blocks.getScripts(); for (var j = 0; j < scripts.length; j++) { - var topBlock = scripts[j]; - if (target.blocks.getBlock(topBlock).opcode === - 'event_whenflagclicked') { - this._pushThread(scripts[j]); - } + var topBlockId = scripts[j]; + f(topBlockId, target); } } }; +/** + * Start all relevant hats. + * @param {!string} requestedHatOpcode Opcode of hats to start. + * @param {Object=} opt_matchFields Optionally, fields to match on the hat. + * @param {Target=} opt_target Optionally, a target to restrict to. + * @return {Array.} List of threads started by this function. + */ +Runtime.prototype.startHats = function (requestedHatOpcode, + opt_matchFields, opt_target) { + if (!this._hats.hasOwnProperty(requestedHatOpcode)) { + // No known hat with this opcode. + return; + } + var instance = this; + var newThreads = []; + // Consider all scripts, looking for hats with opcode `requestedHatOpcode`. + this.allScriptsDo(function(topBlockId, target) { + var potentialHatOpcode = target.blocks.getBlock(topBlockId).opcode; + if (potentialHatOpcode !== requestedHatOpcode) { + // Not the right hat. + return; + } + // Match any requested fields. + // For example: ensures that broadcasts match. + // This needs to happen before the block is evaluated + // (i.e., before the predicate can be run) because "broadcast and wait" + // needs to have a precise collection of started threads. + var hatFields = target.blocks.getFields(topBlockId); + if (opt_matchFields) { + for (var matchField in opt_matchFields) { + if (hatFields[matchField].value !== + opt_matchFields[matchField]) { + // Field mismatch. + return; + } + } + } + // Look up metadata for the relevant hat. + var hatMeta = instance._hats[requestedHatOpcode]; + if (hatMeta.restartExistingThreads) { + // If `restartExistingThreads` is true, we should stop + // any existing threads starting with the top block. + for (var i = 0; i < instance.threads.length; i++) { + if (instance.threads[i].topBlock === topBlockId) { + instance._removeThread(instance.threads[i]); + } + } + } else { + // If `restartExistingThreads` is false, we should + // give up if any threads with the top block are running. + for (var j = 0; j < instance.threads.length; j++) { + if (instance.threads[j].topBlock === topBlockId) { + // Some thread is already running. + return; + } + } + } + // Start the thread with this top block. + newThreads.push(instance._pushThread(topBlockId)); + }, opt_target); + return newThreads; +}; + +/** + * Start all threads that start with the green flag. + */ +Runtime.prototype.greenFlag = function () { + this.ioDevices.clock.resetProjectTimer(); + this.clearEdgeActivatedValues(); + this.startHats('event_whenflagclicked'); +}; + /** * Stop "everything" */ @@ -215,6 +353,13 @@ Runtime.prototype.stopAll = function () { * inactive threads after each iteration. */ Runtime.prototype._step = function () { + // Find all edge-activated hats, and add them to threads to be evaluated. + for (var hatType in this._hats) { + var hat = this._hats[hatType]; + if (hat.edgeActivated) { + this.startHats(hatType); + } + } var inactiveThreads = this.sequencer.stepThreads(this.threads); for (var i = 0; i < inactiveThreads.length; i++) { this._removeThread(inactiveThreads[i]); @@ -234,6 +379,19 @@ Runtime.prototype.glowBlock = function (blockId, isGlowing) { } }; +/** + * Emit feedback for script glowing. + * @param {?string} topBlockId ID for the top block to update glow + * @param {boolean} isGlowing True to turn on glow; false to turn off. + */ +Runtime.prototype.glowScript = function (topBlockId, isGlowing) { + if (isGlowing) { + this.emit(Runtime.STACK_GLOW_ON, topBlockId); + } else { + this.emit(Runtime.STACK_GLOW_OFF, topBlockId); + } +}; + /** * Emit value for reporter to show in the blocks. * @param {string} blockId ID for the block. From e9842520a9a43f1e74d3e7f6fbae94d3042ee052 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Fri, 2 Sep 2016 11:16:35 -0400 Subject: [PATCH 1468/1971] Merge pull request #115 from tmickel/feature/2.0-loader Start of a 2.0 project loader --- packages/scratch-vm/src/engine/runtime.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 921839497e..2ebd9de531 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -18,9 +18,8 @@ var defaultBlockPackages = { /** * Manages targets, scripts, and the sequencer. - * @param {!Array.} targets List of targets for this runtime. */ -function Runtime (targets) { +function Runtime () { // Bind event emitter EventEmitter.call(this); @@ -28,8 +27,9 @@ function Runtime (targets) { /** * Target management and storage. + * @type {Array.} */ - this.targets = targets; + this.targets = []; /** * A list of threads that are currently running in the VM. @@ -418,6 +418,20 @@ Runtime.prototype.targetForThread = function (thread) { } }; +/** + * Get a target by its id. + * @param {string} targetId Id of target to find. + * @return {?Target} The target, if found. + */ +Runtime.prototype.getTargetById = function (targetId) { + for (var i = 0; i < this.targets.length; i++) { + var target = this.targets[i]; + if (target.id == targetId) { + return target; + } + } +}; + /** * Handle an animation frame from the main thread. */ From 87ce6d3ec551aae2644fddd64f9d7b20a4cc6bbb Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Fri, 2 Sep 2016 11:23:09 -0400 Subject: [PATCH 1469/1971] Keyboard events, "key is down" reporter (#117) * Keyboard events, "key is down" reporter * Update comment about inputs * Simplify/generalize keyMap into a keys-pressed list * Cast values passed to scratchKeyToKeyCode appropriately. * Cut unspaced key values * Fix lint from merge commit --- packages/scratch-vm/src/engine/runtime.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 2ebd9de531..fe539dfcd5 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -5,6 +5,7 @@ var util = require('util'); // Virtual I/O devices. var Clock = require('../io/clock'); +var Keyboard = require('../io/keyboard'); var Mouse = require('../io/mouse'); var defaultBlockPackages = { @@ -53,6 +54,7 @@ function Runtime () { this.ioDevices = { 'clock': new Clock(), + 'keyboard': new Keyboard(), 'mouse': new Mouse() }; } From 382ba99052fbafbd113a1ab723773d0a0b4ee4b6 Mon Sep 17 00:00:00 2001 From: dekrain Date: Sat, 3 Sep 2016 22:33:45 +0200 Subject: [PATCH 1470/1971] More hats (#143) * Key pressed hat * Tabs -> Spaces * Oops * Edge activate * Fix hat Phew... * I forgot to change it also here :/ :/ :\ * Minor fixes for TravisCi * Minor docs * Line length --- packages/scratch-vm/src/engine/runtime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index fe539dfcd5..db157783b2 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -54,7 +54,7 @@ function Runtime () { this.ioDevices = { 'clock': new Clock(), - 'keyboard': new Keyboard(), + 'keyboard': new Keyboard(this), 'mouse': new Mouse() }; } From c8f37fff4a34528c68f8123ec62639022b50e2a7 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Thu, 8 Sep 2016 09:40:27 -0400 Subject: [PATCH 1471/1971] Stage, costumes, backdrops (#149) * Add `Clone.prototype.getCostumeIndexByName`, keep in range * Add basic costume primitives from Scratch 2.0 * Add costume getter block * Add properties and methods for distinguishing stage and sprites-vs-clones * Add backdrop-related looks blocks * Fix up "switch to backdrop" to be working * Costume/backdrop reporters are 1-indexed * Fire "when backdrop switched" hats * Cut cloning helpers for a separate PR * Disable many blocks on the stage * Refactor into _setCostumeOrBackdrop; implement switch backdrop and wait * Fire hats even when backdrop unchanged --- packages/scratch-vm/src/engine/runtime.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index db157783b2..143f30aa7c 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -434,6 +434,19 @@ Runtime.prototype.getTargetById = function (targetId) { } }; +/** + * Get a target representing the Scratch stage, if one exists. + * @return {?Target} The target, if found. + */ +Runtime.prototype.getTargetForStage = function () { + for (var i = 0; i < this.targets.length; i++) { + var target = this.targets[i]; + if (target.isStage) { + return target; + } + } +}; + /** * Handle an animation frame from the main thread. */ From 48cb4be3d3583dd785bc3db8141b03524279108e Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Thu, 8 Sep 2016 09:40:53 -0400 Subject: [PATCH 1472/1971] Better glows (#152) * Strip out old script glowing in thread management * Add new tracking mechanism for glowing scripts * Track parents and use them to determine script glows * Use top-block for a thread if there's nothing on the stack * Remove `console.log` --- packages/scratch-vm/src/engine/runtime.js | 70 ++++++++++++++++++----- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 143f30aa7c..473227ba8d 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -57,28 +57,31 @@ function Runtime () { 'keyboard': new Keyboard(this), 'mouse': new Mouse() }; + + this._scriptGlowsPreviousFrame = []; + this._editingTarget = null; } /** - * Event name for glowing a stack + * Event name for glowing a script. * @const {string} */ -Runtime.STACK_GLOW_ON = 'STACK_GLOW_ON'; +Runtime.SCRIPT_GLOW_ON = 'STACK_GLOW_ON'; /** - * Event name for unglowing a stack + * Event name for unglowing a script. * @const {string} */ -Runtime.STACK_GLOW_OFF = 'STACK_GLOW_OFF'; +Runtime.SCRIPT_GLOW_OFF = 'STACK_GLOW_OFF'; /** - * Event name for glowing a block + * Event name for glowing a block. * @const {string} */ Runtime.BLOCK_GLOW_ON = 'BLOCK_GLOW_ON'; /** - * Event name for unglowing a block + * Event name for unglowing a block. * @const {string} */ Runtime.BLOCK_GLOW_OFF = 'BLOCK_GLOW_OFF'; @@ -196,7 +199,6 @@ Runtime.prototype.clearEdgeActivatedValues = function () { */ Runtime.prototype._pushThread = function (id) { var thread = new Thread(id); - this.glowScript(id, true); thread.pushStack(id); this.threads.push(thread); return thread; @@ -209,7 +211,6 @@ Runtime.prototype._pushThread = function (id) { Runtime.prototype._removeThread = function (thread) { var i = this.threads.indexOf(thread); if (i > -1) { - this.glowScript(thread.topBlock, false); this.threads.splice(i, 1); } }; @@ -341,11 +342,6 @@ Runtime.prototype.stopAll = function () { var threadsCopy = this.threads.slice(); while (threadsCopy.length > 0) { var poppedThread = threadsCopy.pop(); - // Unglow any blocks on this thread's stack. - for (var i = 0; i < poppedThread.stack.length; i++) { - this.glowBlock(poppedThread.stack[i], false); - } - // Actually remove the thread. this._removeThread(poppedThread); } }; @@ -363,11 +359,55 @@ Runtime.prototype._step = function () { } } var inactiveThreads = this.sequencer.stepThreads(this.threads); + this._updateScriptGlows(); for (var i = 0; i < inactiveThreads.length; i++) { this._removeThread(inactiveThreads[i]); } }; +Runtime.prototype.setEditingTarget = function (editingTarget) { + this._scriptGlowsPreviousFrame = []; + this._editingTarget = editingTarget; + this._updateScriptGlows(); +}; + +Runtime.prototype._updateScriptGlows = function () { + // Set of scripts that request a glow this frame. + var requestedGlowsThisFrame = []; + // Final set of scripts glowing during this frame. + var finalScriptGlows = []; + // Find all scripts that should be glowing. + for (var i = 0; i < this.threads.length; i++) { + var thread = this.threads[i]; + var target = this.targetForThread(thread); + if (thread.requestScriptGlowInFrame && target == this._editingTarget) { + var blockForThread = thread.peekStack() || thread.topBlock; + var script = target.blocks.getTopLevelScript(blockForThread); + requestedGlowsThisFrame.push(script); + } + } + // Compare to previous frame. + for (var j = 0; j < this._scriptGlowsPreviousFrame.length; j++) { + var previousFrameGlow = this._scriptGlowsPreviousFrame[j]; + if (requestedGlowsThisFrame.indexOf(previousFrameGlow) < 0) { + // Glow turned off. + this.glowScript(previousFrameGlow, false); + } else { + // Still glowing. + finalScriptGlows.push(previousFrameGlow); + } + } + for (var k = 0; k < requestedGlowsThisFrame.length; k++) { + var currentFrameGlow = requestedGlowsThisFrame[k]; + if (this._scriptGlowsPreviousFrame.indexOf(currentFrameGlow) < 0) { + // Glow turned on. + this.glowScript(currentFrameGlow, true); + finalScriptGlows.push(currentFrameGlow); + } + } + this._scriptGlowsPreviousFrame = finalScriptGlows; +}; + /** * Emit feedback for block glowing (used in the sequencer). * @param {?string} blockId ID for the block to update glow @@ -388,9 +428,9 @@ Runtime.prototype.glowBlock = function (blockId, isGlowing) { */ Runtime.prototype.glowScript = function (topBlockId, isGlowing) { if (isGlowing) { - this.emit(Runtime.STACK_GLOW_ON, topBlockId); + this.emit(Runtime.SCRIPT_GLOW_ON, topBlockId); } else { - this.emit(Runtime.STACK_GLOW_OFF, topBlockId); + this.emit(Runtime.SCRIPT_GLOW_OFF, topBlockId); } }; From 7d98706984e99cc683e5afceb15164a6c8ad39ba Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Mon, 12 Sep 2016 17:16:10 -0400 Subject: [PATCH 1473/1971] When clicked hats (#156) --- packages/scratch-vm/src/engine/runtime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 473227ba8d..6c5f36eb15 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -55,7 +55,7 @@ function Runtime () { this.ioDevices = { 'clock': new Clock(), 'keyboard': new Keyboard(this), - 'mouse': new Mouse() + 'mouse': new Mouse(this) }; this._scriptGlowsPreviousFrame = []; From 2331235684f3e5e559042a578dd4ee23d5f27f12 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Thu, 15 Sep 2016 13:51:40 -0400 Subject: [PATCH 1474/1971] Cleanly handle deleting running scripts (#162) * Cleanly handle deleting running scripts * Turn off glow request on retire thread; add null check --- packages/scratch-vm/src/engine/runtime.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 6c5f36eb15..b085dd3d98 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -383,7 +383,9 @@ Runtime.prototype._updateScriptGlows = function () { if (thread.requestScriptGlowInFrame && target == this._editingTarget) { var blockForThread = thread.peekStack() || thread.topBlock; var script = target.blocks.getTopLevelScript(blockForThread); - requestedGlowsThisFrame.push(script); + if (script) { + requestedGlowsThisFrame.push(script); + } } } // Compare to previous frame. @@ -408,6 +410,19 @@ Runtime.prototype._updateScriptGlows = function () { this._scriptGlowsPreviousFrame = finalScriptGlows; }; +/** + * "Quiet" a script's glow: stop the VM from generating glow/unglow events + * about that script. Use when a script has just been deleted, but we may + * still be tracking glow data about it. + * @param {!string} scriptBlockId Id of top-level block in script to quiet. + */ +Runtime.prototype.quietGlow = function (scriptBlockId) { + var index = this._scriptGlowsPreviousFrame.indexOf(scriptBlockId); + if (index > -1) { + this._scriptGlowsPreviousFrame.splice(index, 1); + } +}; + /** * Emit feedback for block glowing (used in the sequencer). * @param {?string} blockId ID for the block to update glow From b19615914adf183f59d0f2f04f81e6e224a0b00d Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Thu, 15 Sep 2016 19:37:12 -0400 Subject: [PATCH 1475/1971] Clones (#150) * Provide property to Clone to distinguish "original" clones * Provide method to clone a clone's properties * Don't report clones in the UI target list * Add target info to Thread * Allow hats to skip clones (for green flag) * Green flag skips clones * Implement "create clone" and hat * Pass the runtime to sprites and clones (for start hats) * Clone disposal; trigger hats after drawable initializes. * Separate stop threads for target; fix handling of stop button * Remove extraneous `skipClones` property * Add global clone limit * Don't allow a non-clone to delete itself. * Rename `cloneClone` -> `makeClone` * Variable updates in runtime.js * Synchronous drawable initialization (until we put it back to promises) --- packages/scratch-vm/src/engine/runtime.js | 113 +++++++++++++++++----- 1 file changed, 90 insertions(+), 23 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index b085dd3d98..ddca44f294 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -60,6 +60,11 @@ function Runtime () { this._scriptGlowsPreviousFrame = []; this._editingTarget = null; + /** + * Currently known number of clones. + * @type {number} + */ + this._cloneCounter = 0; } /** @@ -102,6 +107,11 @@ util.inherits(Runtime, EventEmitter); */ Runtime.THREAD_STEP_INTERVAL = 1000 / 60; +/** + * How many clones can be created at a time. + * @const {number} + */ +Runtime.MAX_CLONES = 300; // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- @@ -194,11 +204,13 @@ Runtime.prototype.clearEdgeActivatedValues = function () { /** * Create a thread and push it to the list of threads. - * @param {!string} id ID of block that starts the stack + * @param {!string} id ID of block that starts the stack. + * @param {!Target} target Target to run thread on. * @return {!Thread} The newly created thread. */ -Runtime.prototype._pushThread = function (id) { +Runtime.prototype._pushThread = function (id, target) { var thread = new Thread(id); + thread.setTarget(target); thread.pushStack(id); this.threads.push(thread); return thread; @@ -237,7 +249,7 @@ Runtime.prototype.toggleScript = function (topBlockId) { } } // Otherwise add it. - this._pushThread(topBlockId); + this._pushThread(topBlockId, this._editingTarget); }; /** @@ -306,7 +318,8 @@ Runtime.prototype.startHats = function (requestedHatOpcode, // If `restartExistingThreads` is true, we should stop // any existing threads starting with the top block. for (var i = 0; i < instance.threads.length; i++) { - if (instance.threads[i].topBlock === topBlockId) { + if (instance.threads[i].topBlock === topBlockId && + (!opt_target || instance.threads[i].target == opt_target)) { instance._removeThread(instance.threads[i]); } } @@ -314,31 +327,72 @@ Runtime.prototype.startHats = function (requestedHatOpcode, // If `restartExistingThreads` is false, we should // give up if any threads with the top block are running. for (var j = 0; j < instance.threads.length; j++) { - if (instance.threads[j].topBlock === topBlockId) { + if (instance.threads[j].topBlock === topBlockId && + (!opt_target || instance.threads[j].target == opt_target)) { // Some thread is already running. return; } } } // Start the thread with this top block. - newThreads.push(instance._pushThread(topBlockId)); + newThreads.push(instance._pushThread(topBlockId, target)); }, opt_target); return newThreads; }; +/** + * Dispose of a target. + * @param {!Target} target Target to dispose of. + */ +Runtime.prototype.disposeTarget = function (target) { + // Allow target to do dispose actions. + target.dispose(); + // Remove from list of targets. + var index = this.targets.indexOf(target); + if (index > -1) { + this.targets.splice(index, 1); + } +}; + +/** + * Stop any threads acting on the target. + * @param {!Target} target Target to stop threads for. + */ +Runtime.prototype.stopForTarget = function (target) { + // Stop any threads on the target. + for (var i = 0; i < this.threads.length; i++) { + if (this.threads[i].target == target) { + this._removeThread(this.threads[i]); + } + } +}; + /** * Start all threads that start with the green flag. */ Runtime.prototype.greenFlag = function () { + this.stopAll(); this.ioDevices.clock.resetProjectTimer(); this.clearEdgeActivatedValues(); this.startHats('event_whenflagclicked'); }; /** - * Stop "everything" + * Stop "everything." */ Runtime.prototype.stopAll = function () { + // Dispose all clones. + var newTargets = []; + for (var i = 0; i < this.targets.length; i++) { + if (this.targets[i].hasOwnProperty('isOriginal') && + !this.targets[i].isOriginal) { + this.targets[i].dispose(); + } else { + newTargets.push(this.targets[i]); + } + } + this.targets = newTargets; + // Dispose all threads. var threadsCopy = this.threads.slice(); while (threadsCopy.length > 0) { var poppedThread = threadsCopy.pop(); @@ -379,7 +433,7 @@ Runtime.prototype._updateScriptGlows = function () { // Find all scripts that should be glowing. for (var i = 0; i < this.threads.length; i++) { var thread = this.threads[i]; - var target = this.targetForThread(thread); + var target = thread.target; if (thread.requestScriptGlowInFrame && target == this._editingTarget) { var blockForThread = thread.peekStack() || thread.topBlock; var script = target.blocks.getTopLevelScript(blockForThread); @@ -459,36 +513,49 @@ Runtime.prototype.visualReport = function (blockId, value) { }; /** - * Return the Target for a particular thread. - * @param {!Thread} thread Thread to determine target for. - * @return {?Target} Target object, if one exists. + * Get a target by its id. + * @param {string} targetId Id of target to find. + * @return {?Target} The target, if found. */ -Runtime.prototype.targetForThread = function (thread) { - // @todo This is a messy solution, - // but prevents having circular data references. - // Have a map or some other way to associate target with threads. - for (var t = 0; t < this.targets.length; t++) { - var target = this.targets[t]; - if (target.blocks.getBlock(thread.topBlock)) { +Runtime.prototype.getTargetById = function (targetId) { + for (var i = 0; i < this.targets.length; i++) { + var target = this.targets[i]; + if (target.id == targetId) { return target; } } }; /** - * Get a target by its id. - * @param {string} targetId Id of target to find. - * @return {?Target} The target, if found. + * Get the first original (non-clone-block-created) sprite given a name. + * @param {string} spriteName Name of sprite to look for. + * @return {?Target} Target representing a sprite of the given name. */ -Runtime.prototype.getTargetById = function (targetId) { +Runtime.prototype.getSpriteTargetByName = function (spriteName) { for (var i = 0; i < this.targets.length; i++) { var target = this.targets[i]; - if (target.id == targetId) { + if (target.sprite && target.sprite.name == spriteName) { return target; } } }; +/** + * Update the clone counter to track how many clones are created. + * @param {number} changeAmount How many clones have been created/destroyed. + */ +Runtime.prototype.changeCloneCounter = function (changeAmount) { + this._cloneCounter += changeAmount; +}; + +/** + * Return whether there are clones available. + * @return {boolean} True until the number of clones hits Runtime.MAX_CLONES. + */ +Runtime.prototype.clonesAvailable = function () { + return this._cloneCounter < Runtime.MAX_CLONES; +}; + /** * Get a target representing the Scratch stage, if one exists. * @return {?Target} The target, if found. From 2d1a2b6780271d31d7b2a76554871fe44600c285 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 20 Sep 2016 15:29:28 -0400 Subject: [PATCH 1476/1971] Merge pull request #200 from rschamp/render-scope Scope renderer to instance --- packages/scratch-vm/src/engine/runtime.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index ddca44f294..041ee625c8 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -199,6 +199,14 @@ Runtime.prototype.clearEdgeActivatedValues = function () { this._edgeActivatedHatValues = {}; }; +/** + * Attach the renderer + * @param {!RenderWebGL} renderer The renderer to attach + */ +Runtime.prototype.attachRenderer = function (renderer) { + this.renderer = renderer; +}; + // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- @@ -573,8 +581,8 @@ Runtime.prototype.getTargetForStage = function () { * Handle an animation frame from the main thread. */ Runtime.prototype.animationFrame = function () { - if (self.renderer) { - self.renderer.draw(); + if (this.renderer) { + this.renderer.draw(); } }; From a11eb29adc7fc5f75112526186cff6a9d86c073f Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Wed, 21 Sep 2016 16:31:07 -0400 Subject: [PATCH 1477/1971] Clear graphic effects on green flag (#199) --- packages/scratch-vm/src/engine/runtime.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 041ee625c8..803069c56a 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -382,6 +382,10 @@ Runtime.prototype.greenFlag = function () { this.stopAll(); this.ioDevices.clock.resetProjectTimer(); this.clearEdgeActivatedValues(); + // Inform all targets of the green flag. + for (var i = 0; i < this.targets.length; i++) { + this.targets[i].onGreenFlag(); + } this.startHats('event_whenflagclicked'); }; From d6fcdfdaa78881a891987957262fa9b33ea74120 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Wed, 21 Sep 2016 16:38:33 -0400 Subject: [PATCH 1478/1971] Variables and lists (#187) * Import lists and variables from SB2 * Switch to Variable and List objects * Add Clone.lookupOrCreateVariable, Clone.getVariable, Clone.setVariable * Add (get, set, change) variable blocks. * Copy variables and lists on clone instantiation * Move variable options closer to blocks * Add list primitives * Move variable and lists storage to `Target` instead of `Clone` * Move _computeIndex to a Cast function * Rename `getList` -> `getListAsString` * Renames renames * Remove extra check in Cast.isNaN --- packages/scratch-vm/src/engine/runtime.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 803069c56a..75067be3dc 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -14,7 +14,8 @@ var defaultBlockPackages = { 'scratch3_looks': require('../blocks/scratch3_looks'), 'scratch3_motion': require('../blocks/scratch3_motion'), 'scratch3_operators': require('../blocks/scratch3_operators'), - 'scratch3_sensing': require('../blocks/scratch3_sensing') + 'scratch3_sensing': require('../blocks/scratch3_sensing'), + 'scratch3_data': require('../blocks/scratch3_data') }; /** From b25599c59f1cc746b093f5f053db2db34a71e834 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Mon, 3 Oct 2016 17:43:24 -0400 Subject: [PATCH 1479/1971] Mutations in block representation; an unfeatured procedure call (#212) * Add scratch3_procedures and no-op for defnoreturn * Add mutation adapter to parse mutations in CREATE/CHANGE events * Add mutation-to-XML * Update spec map for Blockly procedure names * Placeholder for procedure special cases * Basic stepping to procedures * Remove extra case * Validation for changeBlock --- packages/scratch-vm/src/engine/runtime.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 75067be3dc..18cffd8ec3 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -15,7 +15,8 @@ var defaultBlockPackages = { 'scratch3_motion': require('../blocks/scratch3_motion'), 'scratch3_operators': require('../blocks/scratch3_operators'), 'scratch3_sensing': require('../blocks/scratch3_sensing'), - 'scratch3_data': require('../blocks/scratch3_data') + 'scratch3_data': require('../blocks/scratch3_data'), + 'scratch3_procedures': require('../blocks/scratch3_procedures') }; /** From 12582257ecb6223ad6a615488b6957695baf587e Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 17 Oct 2016 10:50:24 -0400 Subject: [PATCH 1480/1971] Resolve merge conflicts --- packages/scratch-vm/src/engine/runtime.js | 32 ++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 18cffd8ec3..4a101147d7 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1,5 +1,6 @@ var EventEmitter = require('events'); var Sequencer = require('./sequencer'); +var Blocks = require('./blocks'); var Thread = require('./thread'); var util = require('util'); @@ -44,6 +45,8 @@ function Runtime () { /** @type {!Sequencer} */ this.sequencer = new Sequencer(this); + this.flyoutBlocks = new Blocks(); + /** * Map to look up a block primitive's implementation function by its opcode. * This is a two-step lookup: package name first, then primitive name. @@ -69,6 +72,18 @@ function Runtime () { this._cloneCounter = 0; } +/** + * Width of the stage, in pixels. + * @const {number} + */ +Runtime.STAGE_WIDTH = 480; + +/** + * Height of the stage, in pixels. + * @const {number} + */ +Runtime.STAGE_HEIGHT = 360; + /** * Event name for glowing a script. * @const {string} @@ -231,6 +246,9 @@ Runtime.prototype._pushThread = function (id, target) { * @param {?Thread} thread Thread object to remove from actives */ Runtime.prototype._removeThread = function (thread) { + // Inform sequencer to stop executing that thread. + this.sequencer.retireThread(thread); + // Remove from the list. var i = this.threads.indexOf(thread); if (i > -1) { this.threads.splice(i, 1); @@ -329,7 +347,7 @@ Runtime.prototype.startHats = function (requestedHatOpcode, // any existing threads starting with the top block. for (var i = 0; i < instance.threads.length; i++) { if (instance.threads[i].topBlock === topBlockId && - (!opt_target || instance.threads[i].target == opt_target)) { + instance.threads[i].target == target) { instance._removeThread(instance.threads[i]); } } @@ -338,7 +356,7 @@ Runtime.prototype.startHats = function (requestedHatOpcode, // give up if any threads with the top block are running. for (var j = 0; j < instance.threads.length; j++) { if (instance.threads[j].topBlock === topBlockId && - (!opt_target || instance.threads[j].target == opt_target)) { + instance.threads[j].target == target) { // Some thread is already running. return; } @@ -367,10 +385,14 @@ Runtime.prototype.disposeTarget = function (target) { /** * Stop any threads acting on the target. * @param {!Target} target Target to stop threads for. + * @param {Thread=} opt_threadException Optional thread to skip. */ -Runtime.prototype.stopForTarget = function (target) { +Runtime.prototype.stopForTarget = function (target, opt_threadException) { // Stop any threads on the target. for (var i = 0; i < this.threads.length; i++) { + if (this.threads[i] === opt_threadException) { + continue; + } if (this.threads[i].target == target) { this._removeThread(this.threads[i]); } @@ -451,6 +473,10 @@ Runtime.prototype._updateScriptGlows = function () { if (thread.requestScriptGlowInFrame && target == this._editingTarget) { var blockForThread = thread.peekStack() || thread.topBlock; var script = target.blocks.getTopLevelScript(blockForThread); + if (!script) { + // Attempt to find in flyout blocks. + script = this.flyoutBlocks.getTopLevelScript(blockForThread); + } if (script) { requestedGlowsThisFrame.push(script); } From 32006eaac5ab04ed7bb9fc7785ae709cbfa3b424 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 17 Oct 2016 13:56:37 -0400 Subject: [PATCH 1481/1971] Merge pull request #282 from rschamp/feature/vm.clear Add VM.clear method --- packages/scratch-vm/src/engine/runtime.js | 28 +++++++++++++++-------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 4a101147d7..ea47a3ba93 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -369,17 +369,25 @@ Runtime.prototype.startHats = function (requestedHatOpcode, }; /** - * Dispose of a target. - * @param {!Target} target Target to dispose of. + * Dispose all targets. Return to clean state. */ -Runtime.prototype.disposeTarget = function (target) { - // Allow target to do dispose actions. - target.dispose(); - // Remove from list of targets. - var index = this.targets.indexOf(target); - if (index > -1) { - this.targets.splice(index, 1); - } +Runtime.prototype.dispose = function () { + this.stopAll(); + this.targets.map(this.disposeTarget, this); +}; + +/** + * Dispose of a target. + * @param {!Target} disposingTarget Target to dispose of. + */ +Runtime.prototype.disposeTarget = function (disposingTarget) { + this.targets = this.targets.filter(function (target) { + if (disposingTarget !== target) return true; + // Allow target to do dispose actions. + target.dispose(); + // Remove from list of targets. + return false; + }); }; /** From 169fb841bd52f5bbe29b64442075ab5aabc1b074 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Mon, 17 Oct 2016 23:23:16 -0400 Subject: [PATCH 1482/1971] Interpreter fixes, enhancements, features (#280) * Thread stepping rework; interp.redraw equivalent * Add turbo mode and pause mode * Yielding behavior to match Scratch 2.0 * Implement warp-mode procedure threads * Add check for recursive call * Inline wait block timer * Revert to setInterval and always drawing * Restore yielding in glide * 30TPS compatibility mode * 5-call count recursion limit * Removing dead primitive code * To simplify, access runtime.threads inline in `stepThreads`. * Warp mode/timer fixes; recursive check fixes; clean-up * Add basic single-stepping * Add single-stepping speed slider * Allow yielding threads to run in single-stepping * Restore inactive threads tracking for block glows * Add clock pausing during pause mode * Documentation and clean-up throughout * Don't look for block glows in `thread.topBlock`. * Add null check for block glows; rename `_updateScriptGlows` to reflect block glowing * Use the current executed block for glow, instead of stack * Add more comments to `stepToProcedure`, and re-arrange to match 2.0 * Tweak to Blocks.prototype.getTopLevelScript * Revert previous * Fix threads array to be resilient to changes during `stepThreads` * Restore inactive threads filtering * Fix typo in "procedure" * !! instead of == true --- packages/scratch-vm/src/engine/runtime.js | 290 +++++++++++++++++++--- 1 file changed, 251 insertions(+), 39 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index ea47a3ba93..14df355288 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -27,8 +27,6 @@ function Runtime () { // Bind event emitter EventEmitter.call(this); - // State for the runtime - /** * Target management and storage. * @type {Array.} @@ -45,33 +43,131 @@ function Runtime () { /** @type {!Sequencer} */ this.sequencer = new Sequencer(this); + /** + * Storage container for flyout blocks. + * These will execute on `_editingTarget.` + * @type {!Blocks} + */ this.flyoutBlocks = new Blocks(); + /** + * Currently known editing target for the VM. + * @type {?Target} + */ + this._editingTarget = null; + /** * Map to look up a block primitive's implementation function by its opcode. * This is a two-step lookup: package name first, then primitive name. * @type {Object.} */ this._primitives = {}; + + /** + * Map to look up hat blocks' metadata. + * Keys are opcode for hat, values are metadata objects. + * @type {Object.} + */ this._hats = {}; + + /** + * Currently known values for edge-activated hats. + * Keys are block ID for the hat; values are the currently known values. + * @type {Object.} + */ this._edgeActivatedHatValues = {}; + + /** + * A list of script block IDs that were glowing during the previous frame. + * @type {!Array.} + */ + this._scriptGlowsPreviousFrame = []; + + /** + * A list of block IDs that were glowing during the previous frame. + * @type {!Array.} + */ + this._blockGlowsPreviousFrame = []; + + /** + * Currently known number of clones, used to enforce clone limit. + * @type {number} + */ + this._cloneCounter = 0; + + /** + * Whether the project is in "turbo mode." + * @type {Boolean} + */ + this.turboMode = false; + + /** + * Whether the project is in "pause mode." + * @type {Boolean} + */ + this.pauseMode = false; + + /** + * Whether the project is in "compatibility mode" (30 TPS). + * @type {Boolean} + */ + this.compatibilityMode = false; + + /** + * Whether the project is in "single stepping mode." + * @type {Boolean} + */ + this.singleStepping = false; + + /** + * How fast in ms "single stepping mode" should run, in ms. + * Can be updated dynamically. + * @type {!number} + */ + this.singleStepInterval = 1000 / 10; + + /** + * A reference to the current runtime stepping interval, set + * by a `setInterval`. + * @type {!number} + */ + this._steppingInterval = null; + + /** + * Current length of a step. + * Changes as mode switches, and used by the sequencer to calculate + * WORK_TIME. + * @type {!number} + */ + this.currentStepTime = null; + + /** + * Whether any primitive has requested a redraw. + * Affects whether `Sequencer.stepThreads` will yield + * after stepping each thread. + * Reset on every frame. + * @type {boolean} + */ + this.redrawRequested = false; + + // Register all given block packages. this._registerBlockPackages(); + // Register and initialize "IO devices", containers for processing + // I/O related data. + /** @type {Object.} */ this.ioDevices = { 'clock': new Clock(), 'keyboard': new Keyboard(this), 'mouse': new Mouse(this) }; - - this._scriptGlowsPreviousFrame = []; - this._editingTarget = null; - /** - * Currently known number of clones. - * @type {number} - */ - this._cloneCounter = 0; } +/** + * Inherit from EventEmitter + */ +util.inherits(Runtime, EventEmitter); + /** * Width of the stage, in pixels. * @const {number} @@ -115,14 +211,14 @@ Runtime.BLOCK_GLOW_OFF = 'BLOCK_GLOW_OFF'; Runtime.VISUAL_REPORT = 'VISUAL_REPORT'; /** - * Inherit from EventEmitter + * How rapidly we try to step threads by default, in ms. */ -util.inherits(Runtime, EventEmitter); +Runtime.THREAD_STEP_INTERVAL = 1000 / 60; /** - * How rapidly we try to step threads, in ms. + * In compatibility mode, how rapidly we try to step threads, in ms. */ -Runtime.THREAD_STEP_INTERVAL = 1000 / 60; +Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY = 1000 / 30; /** * How many clones can be created at a time. @@ -175,9 +271,6 @@ Runtime.prototype.getOpcodeFunction = function (opcode) { return this._primitives[opcode]; }; -// ----------------------------------------------------------------------------- -// ----------------------------------------------------------------------------- - /** * Return whether an opcode represents a hat block. * @param {!string} opcode The opcode to look up. @@ -235,7 +328,7 @@ Runtime.prototype.attachRenderer = function (renderer) { */ Runtime.prototype._pushThread = function (id, target) { var thread = new Thread(id); - thread.setTarget(target); + thread.target = target; thread.pushStack(id); this.threads.push(thread); return thread; @@ -449,6 +542,10 @@ Runtime.prototype.stopAll = function () { * inactive threads after each iteration. */ Runtime.prototype._step = function () { + if (this.pauseMode) { + // Don't do any execution while in pause mode. + return; + } // Find all edge-activated hats, and add them to threads to be evaluated. for (var hatType in this._hats) { var hat = this._hats[hatType]; @@ -456,37 +553,113 @@ Runtime.prototype._step = function () { this.startHats(hatType); } } - var inactiveThreads = this.sequencer.stepThreads(this.threads); - this._updateScriptGlows(); - for (var i = 0; i < inactiveThreads.length; i++) { - this._removeThread(inactiveThreads[i]); - } + this.redrawRequested = false; + var inactiveThreads = this.sequencer.stepThreads(); + this._updateGlows(inactiveThreads); }; +/** + * Set the current editing target known by the runtime. + * @param {!Target} editingTarget New editing target. + */ Runtime.prototype.setEditingTarget = function (editingTarget) { - this._scriptGlowsPreviousFrame = []; this._editingTarget = editingTarget; - this._updateScriptGlows(); + // Script glows must be cleared. + this._scriptGlowsPreviousFrame = []; + this._updateGlows(); }; -Runtime.prototype._updateScriptGlows = function () { +/** + * Set whether we are in pause mode. + * @param {boolean} pauseModeOn True iff in pause mode. + */ +Runtime.prototype.setPauseMode = function (pauseModeOn) { + // Inform the project clock/timer to pause/resume its time. + if (this.ioDevices.clock) { + if (pauseModeOn && !this.pauseMode) { + this.ioDevices.clock.pause(); + } + if (!pauseModeOn && this.pauseMode) { + this.ioDevices.clock.resume(); + } + } + this.pauseMode = pauseModeOn; +}; + +/** + * Set whether we are in 30 TPS compatibility mode. + * @param {boolean} compatibilityModeOn True iff in compatibility mode. + */ +Runtime.prototype.setCompatibilityMode = function (compatibilityModeOn) { + this.compatibilityMode = compatibilityModeOn; + if (this._steppingInterval) { + self.clearInterval(this._steppingInterval); + this.start(); + } +}; + +/** + * Set whether we are in single-stepping mode. + * @param {boolean} singleSteppingOn True iff in single-stepping mode. + */ +Runtime.prototype.setSingleSteppingMode = function (singleSteppingOn) { + this.singleStepping = singleSteppingOn; + if (this._steppingInterval) { + self.clearInterval(this._steppingInterval); + this.start(); + } +}; + +/** + * Set the speed during single-stepping mode. + * @param {number} speed Interval length to step threads, in ms. + */ +Runtime.prototype.setSingleSteppingSpeed = function (speed) { + this.singleStepInterval = 1000 / speed; + if (this._steppingInterval) { + self.clearInterval(this._steppingInterval); + this.start(); + } +}; + +/** + * Emit glows/glow clears for blocks and scripts after a single tick. + * Looks at `this.threads` and notices which have turned on/off new glows. + * @param {Array.=} opt_extraThreads Optional list of inactive threads. + */ +Runtime.prototype._updateGlows = function (opt_extraThreads) { + var searchThreads = []; + searchThreads.push.apply(searchThreads, this.threads); + if (opt_extraThreads) { + searchThreads.push.apply(searchThreads, opt_extraThreads); + } // Set of scripts that request a glow this frame. var requestedGlowsThisFrame = []; + var requestedBlockGlowsThisFrame = []; // Final set of scripts glowing during this frame. var finalScriptGlows = []; + var finalBlockGlows = []; // Find all scripts that should be glowing. - for (var i = 0; i < this.threads.length; i++) { - var thread = this.threads[i]; + for (var i = 0; i < searchThreads.length; i++) { + var thread = searchThreads[i]; var target = thread.target; - if (thread.requestScriptGlowInFrame && target == this._editingTarget) { - var blockForThread = thread.peekStack() || thread.topBlock; - var script = target.blocks.getTopLevelScript(blockForThread); - if (!script) { - // Attempt to find in flyout blocks. - script = this.flyoutBlocks.getTopLevelScript(blockForThread); + if (target == this._editingTarget) { + var blockForThread = thread.blockGlowInFrame; + if (thread.requestScriptGlowInFrame) { + var script = target.blocks.getTopLevelScript(blockForThread); + if (!script) { + // Attempt to find in flyout blocks. + script = this.flyoutBlocks.getTopLevelScript( + blockForThread + ); + } + if (script) { + requestedGlowsThisFrame.push(script); + } } - if (script) { - requestedGlowsThisFrame.push(script); + // Only show block glows in single-stepping mode. + if (this.singleStepping && blockForThread) { + requestedBlockGlowsThisFrame.push(blockForThread); } } } @@ -509,7 +682,30 @@ Runtime.prototype._updateScriptGlows = function () { finalScriptGlows.push(currentFrameGlow); } } + for (var m = 0; m < this._blockGlowsPreviousFrame.length; m++) { + var previousBlockGlow = this._blockGlowsPreviousFrame[m]; + if (requestedBlockGlowsThisFrame.indexOf(previousBlockGlow) < 0) { + // Glow turned off. + try { + this.glowBlock(previousBlockGlow, false); + } catch (e) { + // Block has been removed. + } + } else { + // Still glowing. + finalBlockGlows.push(previousBlockGlow); + } + } + for (var p = 0; p < requestedBlockGlowsThisFrame.length; p++) { + var currentBlockFrameGlow = requestedBlockGlowsThisFrame[p]; + if (this._blockGlowsPreviousFrame.indexOf(currentBlockFrameGlow) < 0) { + // Glow turned on. + this.glowBlock(currentBlockFrameGlow, true); + finalBlockGlows.push(currentBlockFrameGlow); + } + } this._scriptGlowsPreviousFrame = finalScriptGlows; + this._blockGlowsPreviousFrame = finalBlockGlows; }; /** @@ -617,22 +813,38 @@ Runtime.prototype.getTargetForStage = function () { } }; +/** + * Tell the runtime to request a redraw. + * Use after a clone/sprite has completed some visible operation on the stage. + */ +Runtime.prototype.requestRedraw = function () { + this.redrawRequested = true; +}; + /** * Handle an animation frame from the main thread. */ Runtime.prototype.animationFrame = function () { if (this.renderer) { + // @todo: Only render when this.redrawRequested or clones rendered. this.renderer.draw(); } }; /** - * Set up timers to repeatedly step in a browser + * Set up timers to repeatedly step in a browser. */ Runtime.prototype.start = function () { - self.setInterval(function() { + var interval = Runtime.THREAD_STEP_INTERVAL; + if (this.singleStepping) { + interval = this.singleStepInterval; + } else if (this.compatibilityMode) { + interval = Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY; + } + this.currentStepTime = interval; + this._steppingInterval = self.setInterval(function() { this._step(); - }.bind(this), Runtime.THREAD_STEP_INTERVAL); + }.bind(this), interval); }; module.exports = Runtime; From dd6d3f638600785092385cd591aa892b1989c7e5 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Thu, 20 Oct 2016 11:42:16 -0400 Subject: [PATCH 1483/1971] Drop single-stepping and pause modes (#294) --- packages/scratch-vm/src/engine/runtime.js | 94 +---------------------- 1 file changed, 1 insertion(+), 93 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 14df355288..6b5bffbeba 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -83,12 +83,6 @@ function Runtime () { */ this._scriptGlowsPreviousFrame = []; - /** - * A list of block IDs that were glowing during the previous frame. - * @type {!Array.} - */ - this._blockGlowsPreviousFrame = []; - /** * Currently known number of clones, used to enforce clone limit. * @type {number} @@ -101,24 +95,12 @@ function Runtime () { */ this.turboMode = false; - /** - * Whether the project is in "pause mode." - * @type {Boolean} - */ - this.pauseMode = false; - /** * Whether the project is in "compatibility mode" (30 TPS). * @type {Boolean} */ this.compatibilityMode = false; - /** - * Whether the project is in "single stepping mode." - * @type {Boolean} - */ - this.singleStepping = false; - /** * How fast in ms "single stepping mode" should run, in ms. * Can be updated dynamically. @@ -542,10 +524,6 @@ Runtime.prototype.stopAll = function () { * inactive threads after each iteration. */ Runtime.prototype._step = function () { - if (this.pauseMode) { - // Don't do any execution while in pause mode. - return; - } // Find all edge-activated hats, and add them to threads to be evaluated. for (var hatType in this._hats) { var hat = this._hats[hatType]; @@ -569,23 +547,6 @@ Runtime.prototype.setEditingTarget = function (editingTarget) { this._updateGlows(); }; -/** - * Set whether we are in pause mode. - * @param {boolean} pauseModeOn True iff in pause mode. - */ -Runtime.prototype.setPauseMode = function (pauseModeOn) { - // Inform the project clock/timer to pause/resume its time. - if (this.ioDevices.clock) { - if (pauseModeOn && !this.pauseMode) { - this.ioDevices.clock.pause(); - } - if (!pauseModeOn && this.pauseMode) { - this.ioDevices.clock.resume(); - } - } - this.pauseMode = pauseModeOn; -}; - /** * Set whether we are in 30 TPS compatibility mode. * @param {boolean} compatibilityModeOn True iff in compatibility mode. @@ -599,31 +560,7 @@ Runtime.prototype.setCompatibilityMode = function (compatibilityModeOn) { }; /** - * Set whether we are in single-stepping mode. - * @param {boolean} singleSteppingOn True iff in single-stepping mode. - */ -Runtime.prototype.setSingleSteppingMode = function (singleSteppingOn) { - this.singleStepping = singleSteppingOn; - if (this._steppingInterval) { - self.clearInterval(this._steppingInterval); - this.start(); - } -}; - -/** - * Set the speed during single-stepping mode. - * @param {number} speed Interval length to step threads, in ms. - */ -Runtime.prototype.setSingleSteppingSpeed = function (speed) { - this.singleStepInterval = 1000 / speed; - if (this._steppingInterval) { - self.clearInterval(this._steppingInterval); - this.start(); - } -}; - -/** - * Emit glows/glow clears for blocks and scripts after a single tick. + * Emit glows/glow clears for scripts after a single tick. * Looks at `this.threads` and notices which have turned on/off new glows. * @param {Array.=} opt_extraThreads Optional list of inactive threads. */ @@ -635,10 +572,8 @@ Runtime.prototype._updateGlows = function (opt_extraThreads) { } // Set of scripts that request a glow this frame. var requestedGlowsThisFrame = []; - var requestedBlockGlowsThisFrame = []; // Final set of scripts glowing during this frame. var finalScriptGlows = []; - var finalBlockGlows = []; // Find all scripts that should be glowing. for (var i = 0; i < searchThreads.length; i++) { var thread = searchThreads[i]; @@ -657,10 +592,6 @@ Runtime.prototype._updateGlows = function (opt_extraThreads) { requestedGlowsThisFrame.push(script); } } - // Only show block glows in single-stepping mode. - if (this.singleStepping && blockForThread) { - requestedBlockGlowsThisFrame.push(blockForThread); - } } } // Compare to previous frame. @@ -682,30 +613,7 @@ Runtime.prototype._updateGlows = function (opt_extraThreads) { finalScriptGlows.push(currentFrameGlow); } } - for (var m = 0; m < this._blockGlowsPreviousFrame.length; m++) { - var previousBlockGlow = this._blockGlowsPreviousFrame[m]; - if (requestedBlockGlowsThisFrame.indexOf(previousBlockGlow) < 0) { - // Glow turned off. - try { - this.glowBlock(previousBlockGlow, false); - } catch (e) { - // Block has been removed. - } - } else { - // Still glowing. - finalBlockGlows.push(previousBlockGlow); - } - } - for (var p = 0; p < requestedBlockGlowsThisFrame.length; p++) { - var currentBlockFrameGlow = requestedBlockGlowsThisFrame[p]; - if (this._blockGlowsPreviousFrame.indexOf(currentBlockFrameGlow) < 0) { - // Glow turned on. - this.glowBlock(currentBlockFrameGlow, true); - finalBlockGlows.push(currentBlockFrameGlow); - } - } this._scriptGlowsPreviousFrame = finalScriptGlows; - this._blockGlowsPreviousFrame = finalBlockGlows; }; /** From 1e753d426ab4d2f61f7320a2296a8065e5a48927 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 24 Oct 2016 15:43:31 -0400 Subject: [PATCH 1484/1971] Merge pull request #298 from rschamp/scratch-lint Use shared lint config: eslint-config-scratch --- packages/scratch-vm/src/engine/runtime.js | 80 +++++++++++------------ 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 6b5bffbeba..0a8930a1d5 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -10,20 +10,20 @@ var Keyboard = require('../io/keyboard'); var Mouse = require('../io/mouse'); var defaultBlockPackages = { - 'scratch3_control': require('../blocks/scratch3_control'), - 'scratch3_event': require('../blocks/scratch3_event'), - 'scratch3_looks': require('../blocks/scratch3_looks'), - 'scratch3_motion': require('../blocks/scratch3_motion'), - 'scratch3_operators': require('../blocks/scratch3_operators'), - 'scratch3_sensing': require('../blocks/scratch3_sensing'), - 'scratch3_data': require('../blocks/scratch3_data'), - 'scratch3_procedures': require('../blocks/scratch3_procedures') + scratch3_control: require('../blocks/scratch3_control'), + scratch3_event: require('../blocks/scratch3_event'), + scratch3_looks: require('../blocks/scratch3_looks'), + scratch3_motion: require('../blocks/scratch3_motion'), + scratch3_operators: require('../blocks/scratch3_operators'), + scratch3_sensing: require('../blocks/scratch3_sensing'), + scratch3_data: require('../blocks/scratch3_data'), + scratch3_procedures: require('../blocks/scratch3_procedures') }; /** * Manages targets, scripts, and the sequencer. */ -function Runtime () { +var Runtime = function () { // Bind event emitter EventEmitter.call(this); @@ -139,11 +139,11 @@ function Runtime () { // I/O related data. /** @type {Object.} */ this.ioDevices = { - 'clock': new Clock(), - 'keyboard': new Keyboard(this), - 'mouse': new Mouse(this) + clock: new Clock(), + keyboard: new Keyboard(this), + mouse: new Mouse(this) }; -} +}; /** * Inherit from EventEmitter @@ -346,7 +346,7 @@ Runtime.prototype.isActiveThread = function (thread) { Runtime.prototype.toggleScript = function (topBlockId) { // Remove any existing thread. for (var i = 0; i < this.threads.length; i++) { - if (this.threads[i].topBlock == topBlockId) { + if (this.threads[i].topBlock === topBlockId) { this._removeThread(this.threads[i]); return; } @@ -361,12 +361,12 @@ Runtime.prototype.toggleScript = function (topBlockId) { * - the top block ID of the script. * - the target that owns the script. * @param {!Function} f Function to call for each script. - * @param {Target=} opt_target Optionally, a target to restrict to. + * @param {Target=} optTarget Optionally, a target to restrict to. */ -Runtime.prototype.allScriptsDo = function (f, opt_target) { +Runtime.prototype.allScriptsDo = function (f, optTarget) { var targets = this.targets; - if (opt_target) { - targets = [opt_target]; + if (optTarget) { + targets = [optTarget]; } for (var t = 0; t < targets.length; t++) { var target = targets[t]; @@ -381,12 +381,12 @@ Runtime.prototype.allScriptsDo = function (f, opt_target) { /** * Start all relevant hats. * @param {!string} requestedHatOpcode Opcode of hats to start. - * @param {Object=} opt_matchFields Optionally, fields to match on the hat. - * @param {Target=} opt_target Optionally, a target to restrict to. + * @param {Object=} optMatchFields Optionally, fields to match on the hat. + * @param {Target=} optTarget Optionally, a target to restrict to. * @return {Array.} List of threads started by this function. */ Runtime.prototype.startHats = function (requestedHatOpcode, - opt_matchFields, opt_target) { + optMatchFields, optTarget) { if (!this._hats.hasOwnProperty(requestedHatOpcode)) { // No known hat with this opcode. return; @@ -394,7 +394,7 @@ Runtime.prototype.startHats = function (requestedHatOpcode, var instance = this; var newThreads = []; // Consider all scripts, looking for hats with opcode `requestedHatOpcode`. - this.allScriptsDo(function(topBlockId, target) { + this.allScriptsDo(function (topBlockId, target) { var potentialHatOpcode = target.blocks.getBlock(topBlockId).opcode; if (potentialHatOpcode !== requestedHatOpcode) { // Not the right hat. @@ -406,10 +406,10 @@ Runtime.prototype.startHats = function (requestedHatOpcode, // (i.e., before the predicate can be run) because "broadcast and wait" // needs to have a precise collection of started threads. var hatFields = target.blocks.getFields(topBlockId); - if (opt_matchFields) { - for (var matchField in opt_matchFields) { + if (optMatchFields) { + for (var matchField in optMatchFields) { if (hatFields[matchField].value !== - opt_matchFields[matchField]) { + optMatchFields[matchField]) { // Field mismatch. return; } @@ -422,7 +422,7 @@ Runtime.prototype.startHats = function (requestedHatOpcode, // any existing threads starting with the top block. for (var i = 0; i < instance.threads.length; i++) { if (instance.threads[i].topBlock === topBlockId && - instance.threads[i].target == target) { + instance.threads[i].target === target) { instance._removeThread(instance.threads[i]); } } @@ -431,7 +431,7 @@ Runtime.prototype.startHats = function (requestedHatOpcode, // give up if any threads with the top block are running. for (var j = 0; j < instance.threads.length; j++) { if (instance.threads[j].topBlock === topBlockId && - instance.threads[j].target == target) { + instance.threads[j].target === target) { // Some thread is already running. return; } @@ -439,7 +439,7 @@ Runtime.prototype.startHats = function (requestedHatOpcode, } // Start the thread with this top block. newThreads.push(instance._pushThread(topBlockId, target)); - }, opt_target); + }, optTarget); return newThreads; }; @@ -468,15 +468,15 @@ Runtime.prototype.disposeTarget = function (disposingTarget) { /** * Stop any threads acting on the target. * @param {!Target} target Target to stop threads for. - * @param {Thread=} opt_threadException Optional thread to skip. + * @param {Thread=} optThreadException Optional thread to skip. */ -Runtime.prototype.stopForTarget = function (target, opt_threadException) { +Runtime.prototype.stopForTarget = function (target, optThreadException) { // Stop any threads on the target. for (var i = 0; i < this.threads.length; i++) { - if (this.threads[i] === opt_threadException) { + if (this.threads[i] === optThreadException) { continue; } - if (this.threads[i].target == target) { + if (this.threads[i].target === target) { this._removeThread(this.threads[i]); } } @@ -562,13 +562,13 @@ Runtime.prototype.setCompatibilityMode = function (compatibilityModeOn) { /** * Emit glows/glow clears for scripts after a single tick. * Looks at `this.threads` and notices which have turned on/off new glows. - * @param {Array.=} opt_extraThreads Optional list of inactive threads. + * @param {Array.=} optExtraThreads Optional list of inactive threads. */ -Runtime.prototype._updateGlows = function (opt_extraThreads) { +Runtime.prototype._updateGlows = function (optExtraThreads) { var searchThreads = []; searchThreads.push.apply(searchThreads, this.threads); - if (opt_extraThreads) { - searchThreads.push.apply(searchThreads, opt_extraThreads); + if (optExtraThreads) { + searchThreads.push.apply(searchThreads, optExtraThreads); } // Set of scripts that request a glow this frame. var requestedGlowsThisFrame = []; @@ -578,7 +578,7 @@ Runtime.prototype._updateGlows = function (opt_extraThreads) { for (var i = 0; i < searchThreads.length; i++) { var thread = searchThreads[i]; var target = thread.target; - if (target == this._editingTarget) { + if (target === this._editingTarget) { var blockForThread = thread.blockGlowInFrame; if (thread.requestScriptGlowInFrame) { var script = target.blocks.getTopLevelScript(blockForThread); @@ -672,7 +672,7 @@ Runtime.prototype.visualReport = function (blockId, value) { Runtime.prototype.getTargetById = function (targetId) { for (var i = 0; i < this.targets.length; i++) { var target = this.targets[i]; - if (target.id == targetId) { + if (target.id === targetId) { return target; } } @@ -686,7 +686,7 @@ Runtime.prototype.getTargetById = function (targetId) { Runtime.prototype.getSpriteTargetByName = function (spriteName) { for (var i = 0; i < this.targets.length; i++) { var target = this.targets[i]; - if (target.sprite && target.sprite.name == spriteName) { + if (target.sprite && target.sprite.name === spriteName) { return target; } } @@ -750,7 +750,7 @@ Runtime.prototype.start = function () { interval = Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY; } this.currentStepTime = interval; - this._steppingInterval = self.setInterval(function() { + this._steppingInterval = self.setInterval(function () { this._step(); }.bind(this), interval); }; From 9fdfef732dcca49023fd35497d5d9ca1c75ea736 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Wed, 26 Oct 2016 11:19:43 -0400 Subject: [PATCH 1485/1971] Rename Clone -> RenderedTarget (#317) --- .../scratch-vm/src/sprites/rendered-target.js | 617 ++++++++++++++++++ 1 file changed, 617 insertions(+) create mode 100644 packages/scratch-vm/src/sprites/rendered-target.js diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js new file mode 100644 index 0000000000..18aa4e10ee --- /dev/null +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -0,0 +1,617 @@ +var util = require('util'); + +var log = require('../util/log'); +var MathUtil = require('../util/math-util'); +var Target = require('../engine/target'); + +/** + * Rendered target: instance of a sprite (clone), or the stage. + * @param {!Sprite} sprite Reference to the parent sprite. + * @param {Runtime} runtime Reference to the runtime. + * @constructor + */ +var RenderedTarget = function (sprite, runtime) { + Target.call(this, sprite.blocks); + this.runtime = runtime; + /** + * Reference to the sprite that this is a render of. + * @type {!Sprite} + */ + this.sprite = sprite; + /** + * Reference to the global renderer for this VM, if one exists. + * @type {?RenderWebGLWorker} + */ + this.renderer = null; + if (this.runtime) { + this.renderer = this.runtime.renderer; + } + /** + * ID of the drawable for this rendered target, + * returned by the renderer, if rendered. + * @type {?Number} + */ + this.drawableID = null; + + /** + * Map of current graphic effect values. + * @type {!Object.} + */ + this.effects = { + color: 0, + fisheye: 0, + whirl: 0, + pixelate: 0, + mosaic: 0, + brightness: 0, + ghost: 0 + }; +}; +util.inherits(RenderedTarget, Target); + +/** + * Create a drawable with the this.renderer. + */ +RenderedTarget.prototype.initDrawable = function () { + if (this.renderer) { + this.drawableID = this.renderer.createDrawable(); + } + // If we're a clone, start the hats. + if (!this.isOriginal) { + this.runtime.startHats( + 'control_start_as_clone', null, this + ); + } +}; + +/** + * Whether this represents an "original" non-clone rendered-target for a sprite, + * i.e., created by the editor and not clone blocks. + * @type {boolean} + */ +RenderedTarget.prototype.isOriginal = true; + +/** + * Whether this rendered target represents the Scratch stage. + * @type {boolean} + */ +RenderedTarget.prototype.isStage = false; + +/** + * Scratch X coordinate. Currently should range from -240 to 240. + * @type {Number} + */ +RenderedTarget.prototype.x = 0; + +/** + * Scratch Y coordinate. Currently should range from -180 to 180. + * @type {number} + */ +RenderedTarget.prototype.y = 0; + +/** + * Scratch direction. Currently should range from -179 to 180. + * @type {number} + */ +RenderedTarget.prototype.direction = 90; + +/** + * Whether the rendered target is currently visible. + * @type {boolean} + */ +RenderedTarget.prototype.visible = true; + +/** + * Size of rendered target as a percent of costume size. + * @type {number} + */ +RenderedTarget.prototype.size = 100; + +/** + * Currently selected costume index. + * @type {number} + */ +RenderedTarget.prototype.currentCostume = 0; + +/** + * Rotation style for "all around"/spinning. + * @enum + */ +RenderedTarget.ROTATION_STYLE_ALL_AROUND = 'all around'; + +/** + * Rotation style for "left-right"/flipping. + * @enum + */ +RenderedTarget.ROTATION_STYLE_LEFT_RIGHT = 'left-right'; + +/** + * Rotation style for "no rotation." + * @enum + */ +RenderedTarget.ROTATION_STYLE_NONE = 'don\'t rotate'; + +/** + * Current rotation style. + * @type {!string} + */ +RenderedTarget.prototype.rotationStyle = ( + RenderedTarget.ROTATION_STYLE_ALL_AROUND +); + +/** + * Set the X and Y coordinates. + * @param {!number} x New X coordinate, in Scratch coordinates. + * @param {!number} y New Y coordinate, in Scratch coordinates. + */ +RenderedTarget.prototype.setXY = function (x, y) { + if (this.isStage) { + return; + } + this.x = x; + this.y = y; + if (this.renderer) { + this.renderer.updateDrawableProperties(this.drawableID, { + position: [this.x, this.y] + }); + if (this.visible) { + this.runtime.requestRedraw(); + } + } +}; + +/** + * Get the rendered direction and scale, after applying rotation style. + * @return {Object} Direction and scale to render. + */ +RenderedTarget.prototype._getRenderedDirectionAndScale = function () { + // Default: no changes to `this.direction` or `this.scale`. + var finalDirection = this.direction; + var finalScale = [this.size, this.size]; + if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_NONE) { + // Force rendered direction to be 90. + finalDirection = 90; + } else if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_LEFT_RIGHT) { + // Force rendered direction to be 90, and flip drawable if needed. + finalDirection = 90; + var scaleFlip = (this.direction < 0) ? -1 : 1; + finalScale = [scaleFlip * this.size, this.size]; + } + return {direction: finalDirection, scale: finalScale}; +}; + +/** + * Set the direction. + * @param {!number} direction New direction. + */ +RenderedTarget.prototype.setDirection = function (direction) { + if (this.isStage) { + return; + } + // Keep direction between -179 and +180. + this.direction = MathUtil.wrapClamp(direction, -179, 180); + if (this.renderer) { + var renderedDirectionScale = this._getRenderedDirectionAndScale(); + this.renderer.updateDrawableProperties(this.drawableID, { + direction: renderedDirectionScale.direction, + scale: renderedDirectionScale.scale + }); + if (this.visible) { + this.runtime.requestRedraw(); + } + } +}; + +/** + * Set a say bubble. + * @param {?string} type Type of say bubble: "say", "think", or null. + * @param {?string} message Message to put in say bubble. + */ +RenderedTarget.prototype.setSay = function (type, message) { + if (this.isStage) { + return; + } + // @todo: Render to stage. + if (!type || !message) { + log.info('Clearing say bubble'); + return; + } + log.info('Setting say bubble:', type, message); +}; + +/** + * Set visibility; i.e., whether it's shown or hidden. + * @param {!boolean} visible True if should be shown. + */ +RenderedTarget.prototype.setVisible = function (visible) { + if (this.isStage) { + return; + } + this.visible = visible; + if (this.renderer) { + this.renderer.updateDrawableProperties(this.drawableID, { + visible: this.visible + }); + if (this.visible) { + this.runtime.requestRedraw(); + } + } +}; + +/** + * Set size, as a percentage of the costume size. + * @param {!number} size Size of rendered target, as % of costume size. + */ +RenderedTarget.prototype.setSize = function (size) { + if (this.isStage) { + return; + } + // Keep size between 5% and 535%. + this.size = MathUtil.clamp(size, 5, 535); + if (this.renderer) { + var renderedDirectionScale = this._getRenderedDirectionAndScale(); + this.renderer.updateDrawableProperties(this.drawableID, { + direction: renderedDirectionScale.direction, + scale: renderedDirectionScale.scale + }); + if (this.visible) { + this.runtime.requestRedraw(); + } + } +}; + +/** + * Set a particular graphic effect value. + * @param {!string} effectName Name of effect (see `RenderedTarget.prototype.effects`). + * @param {!number} value Numerical magnitude of effect. + */ +RenderedTarget.prototype.setEffect = function (effectName, value) { + if (!this.effects.hasOwnProperty(effectName)) return; + this.effects[effectName] = value; + if (this.renderer) { + var props = {}; + props[effectName] = this.effects[effectName]; + this.renderer.updateDrawableProperties(this.drawableID, props); + if (this.visible) { + this.runtime.requestRedraw(); + } + } +}; + +/** + * Clear all graphic effects on this rendered target. + */ +RenderedTarget.prototype.clearEffects = function () { + for (var effectName in this.effects) { + this.effects[effectName] = 0; + } + if (this.renderer) { + this.renderer.updateDrawableProperties(this.drawableID, this.effects); + if (this.visible) { + this.runtime.requestRedraw(); + } + } +}; + +/** + * Set the current costume. + * @param {number} index New index of costume. + */ +RenderedTarget.prototype.setCostume = function (index) { + // Keep the costume index within possible values. + index = Math.round(index); + this.currentCostume = MathUtil.wrapClamp( + index, 0, this.sprite.costumes.length - 1 + ); + if (this.renderer) { + var costume = this.sprite.costumes[this.currentCostume]; + this.renderer.updateDrawableProperties(this.drawableID, { + skin: costume.skin, + costumeResolution: costume.bitmapResolution, + rotationCenter: [ + costume.rotationCenterX / costume.bitmapResolution, + costume.rotationCenterY / costume.bitmapResolution + ] + }); + if (this.visible) { + this.runtime.requestRedraw(); + } + } +}; + +/** + * Update the rotation style. + * @param {!string} rotationStyle New rotation style. + */ +RenderedTarget.prototype.setRotationStyle = function (rotationStyle) { + if (rotationStyle === RenderedTarget.ROTATION_STYLE_NONE) { + this.rotationStyle = RenderedTarget.ROTATION_STYLE_NONE; + } else if (rotationStyle === RenderedTarget.ROTATION_STYLE_ALL_AROUND) { + this.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND; + } else if (rotationStyle === RenderedTarget.ROTATION_STYLE_LEFT_RIGHT) { + this.rotationStyle = RenderedTarget.ROTATION_STYLE_LEFT_RIGHT; + } + if (this.renderer) { + var renderedDirectionScale = this._getRenderedDirectionAndScale(); + this.renderer.updateDrawableProperties(this.drawableID, { + direction: renderedDirectionScale.direction, + scale: renderedDirectionScale.scale + }); + if (this.visible) { + this.runtime.requestRedraw(); + } + } +}; + +/** + * Get a costume index of this rendered target, by name of the costume. + * @param {?string} costumeName Name of a costume. + * @return {number} Index of the named costume, or -1 if not present. + */ +RenderedTarget.prototype.getCostumeIndexByName = function (costumeName) { + for (var i = 0; i < this.sprite.costumes.length; i++) { + if (this.sprite.costumes[i].name === costumeName) { + return i; + } + } + return -1; +}; + +/** + * Update all drawable properties for this rendered target. + * Use when a batch has changed, e.g., when the drawable is first created. + */ +RenderedTarget.prototype.updateAllDrawableProperties = function () { + if (this.renderer) { + var renderedDirectionScale = this._getRenderedDirectionAndScale(); + var costume = this.sprite.costumes[this.currentCostume]; + this.renderer.updateDrawableProperties(this.drawableID, { + position: [this.x, this.y], + direction: renderedDirectionScale.direction, + scale: renderedDirectionScale.scale, + visible: this.visible, + skin: costume.skin, + costumeResolution: costume.bitmapResolution, + rotationCenter: [ + costume.rotationCenterX / costume.bitmapResolution, + costume.rotationCenterY / costume.bitmapResolution + ] + }); + if (this.visible) { + this.runtime.requestRedraw(); + } + } +}; + +/** + * Return the human-readable name for this rendered target, e.g., the sprite's name. + * @override + * @returns {string} Human-readable name. + */ +RenderedTarget.prototype.getName = function () { + return this.sprite.name; +}; + +/** + * Return the rendered target's tight bounding box. + * Includes top, left, bottom, right attributes in Scratch coordinates. + * @return {?Object} Tight bounding box, or null. + */ +RenderedTarget.prototype.getBounds = function () { + if (this.renderer) { + return this.runtime.renderer.getBounds(this.drawableID); + } + return null; +}; + +/** + * Return whether touching a point. + * @param {number} x X coordinate of test point. + * @param {number} y Y coordinate of test point. + * @return {Boolean} True iff the rendered target is touching the point. + */ +RenderedTarget.prototype.isTouchingPoint = function (x, y) { + if (this.renderer) { + // @todo: Update once pick is in Scratch coordinates. + // Limits test to this Drawable, so this will return true + // even if the clone is obscured by another Drawable. + var pickResult = this.runtime.renderer.pick( + x + (this.runtime.constructor.STAGE_WIDTH / 2), + -y + (this.runtime.constructor.STAGE_HEIGHT / 2), + null, null, + [this.drawableID] + ); + return pickResult === this.drawableID; + } + return false; +}; + +/** + * Return whether touching a stage edge. + * @return {Boolean} True iff the rendered target is touching the stage edge. + */ +RenderedTarget.prototype.isTouchingEdge = function () { + if (this.renderer) { + var stageWidth = this.runtime.constructor.STAGE_WIDTH; + var stageHeight = this.runtime.constructor.STAGE_HEIGHT; + var bounds = this.getBounds(); + if (bounds.left < -stageWidth / 2 || + bounds.right > stageWidth / 2 || + bounds.top > stageHeight / 2 || + bounds.bottom < -stageHeight / 2) { + return true; + } + } + return false; +}; + +/** + * Return whether touching any of a named sprite's clones. + * @param {string} spriteName Name of the sprite. + * @return {Boolean} True iff touching a clone of the sprite. + */ +RenderedTarget.prototype.isTouchingSprite = function (spriteName) { + var firstClone = this.runtime.getSpriteTargetByName(spriteName); + if (!firstClone || !this.renderer) { + return false; + } + var drawableCandidates = firstClone.sprite.clones.map(function (clone) { + return clone.drawableID; + }); + return this.renderer.isTouchingDrawables( + this.drawableID, drawableCandidates); +}; + +/** + * Return whether touching a color. + * @param {Array.} rgb [r,g,b], values between 0-255. + * @return {Promise.} True iff the rendered target is touching the color. + */ +RenderedTarget.prototype.isTouchingColor = function (rgb) { + if (this.renderer) { + return this.renderer.isTouchingColor(this.drawableID, rgb); + } + return false; +}; + +/** + * Return whether rendered target's color is touching a color. + * @param {Object} targetRgb {Array.} [r,g,b], values between 0-255. + * @param {Object} maskRgb {Array.} [r,g,b], values between 0-255. + * @return {Promise.} True iff the color is touching the color. + */ +RenderedTarget.prototype.colorIsTouchingColor = function (targetRgb, maskRgb) { + if (this.renderer) { + return this.renderer.isTouchingColor( + this.drawableID, + targetRgb, + maskRgb + ); + } + return false; +}; + +/** + * Move to the front layer. + */ +RenderedTarget.prototype.goToFront = function () { + if (this.renderer) { + this.renderer.setDrawableOrder(this.drawableID, Infinity); + } +}; + +/** + * Move back a number of layers. + * @param {number} nLayers How many layers to go back. + */ +RenderedTarget.prototype.goBackLayers = function (nLayers) { + if (this.renderer) { + this.renderer.setDrawableOrder(this.drawableID, -nLayers, true, 1); + } +}; + +/** + * Move behind some other rendered target. + * @param {!Clone} other Other rendered target to move behind. + */ +RenderedTarget.prototype.goBehindOther = function (other) { + if (this.renderer) { + var otherLayer = this.renderer.setDrawableOrder( + other.drawableID, 0, true); + this.renderer.setDrawableOrder(this.drawableID, otherLayer); + } +}; + +/** + * Keep a desired position within a fence. + * @param {number} newX New desired X position. + * @param {number} newY New desired Y position. + * @param {Object=} optFence Optional fence with left, right, top bottom. + * @return {Array.} Fenced X and Y coordinates. + */ +RenderedTarget.prototype.keepInFence = function (newX, newY, optFence) { + var fence = optFence; + if (!fence) { + fence = { + left: -this.runtime.constructor.STAGE_WIDTH / 2, + right: this.runtime.constructor.STAGE_WIDTH / 2, + top: this.runtime.constructor.STAGE_HEIGHT / 2, + bottom: -this.runtime.constructor.STAGE_HEIGHT / 2 + }; + } + var bounds = this.getBounds(); + if (!bounds) return; + // Adjust the known bounds to the target position. + bounds.left += (newX - this.x); + bounds.right += (newX - this.x); + bounds.top += (newY - this.y); + bounds.bottom += (newY - this.y); + // Find how far we need to move the target position. + var dx = 0; + var dy = 0; + if (bounds.left < fence.left) { + dx += fence.left - bounds.left; + } + if (bounds.right > fence.right) { + dx += fence.right - bounds.right; + } + if (bounds.top > fence.top) { + dy += fence.top - bounds.top; + } + if (bounds.bottom < fence.bottom) { + dy += fence.bottom - bounds.bottom; + } + return [newX + dx, newY + dy]; +}; + +/** + * Make a clone, copying any run-time properties. + * If we've hit the global clone limit, returns null. + * @return {!RenderedTarget} New clone. + */ +RenderedTarget.prototype.makeClone = function () { + if (!this.runtime.clonesAvailable() || this.isStage) { + return; // Hit max clone limit, or this is the stage. + } + this.runtime.changeCloneCounter(1); + var newClone = this.sprite.createClone(); + // Copy all properties. + newClone.x = this.x; + newClone.y = this.y; + newClone.direction = this.direction; + newClone.visible = this.visible; + newClone.size = this.size; + newClone.currentCostume = this.currentCostume; + newClone.rotationStyle = this.rotationStyle; + newClone.effects = JSON.parse(JSON.stringify(this.effects)); + newClone.variables = JSON.parse(JSON.stringify(this.variables)); + newClone.lists = JSON.parse(JSON.stringify(this.lists)); + newClone.initDrawable(); + newClone.updateAllDrawableProperties(); + // Place behind the current target. + newClone.goBehindOther(this); + return newClone; +}; + +/** + * Called when the project receives a "green flag." + * For a rendered target, this clears graphic effects. + */ +RenderedTarget.prototype.onGreenFlag = function () { + this.clearEffects(); +}; + +/** + * Dispose, destroying any run-time properties. + */ +RenderedTarget.prototype.dispose = function () { + this.runtime.changeCloneCounter(-1); + if (this.renderer && this.drawableID !== null) { + this.renderer.destroyDrawable(this.drawableID); + if (this.visible) { + this.runtime.requestRedraw(); + } + } +}; + +module.exports = RenderedTarget; From 5ff73b29238fe973c8b6597e97dbfd80bbcdc6e3 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Wed, 26 Oct 2016 11:32:15 -0400 Subject: [PATCH 1486/1971] Remove rAF usage and inline into _step (#318) --- packages/scratch-vm/src/engine/runtime.js | 25 +++++------------------ 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 0a8930a1d5..15dc76296f 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -101,13 +101,6 @@ var Runtime = function () { */ this.compatibilityMode = false; - /** - * How fast in ms "single stepping mode" should run, in ms. - * Can be updated dynamically. - * @type {!number} - */ - this.singleStepInterval = 1000 / 10; - /** * A reference to the current runtime stepping interval, set * by a `setInterval`. @@ -534,6 +527,10 @@ Runtime.prototype._step = function () { this.redrawRequested = false; var inactiveThreads = this.sequencer.stepThreads(); this._updateGlows(inactiveThreads); + if (this.renderer) { + // @todo: Only render when this.redrawRequested or clones rendered. + this.renderer.draw(); + } }; /** @@ -729,24 +726,12 @@ Runtime.prototype.requestRedraw = function () { this.redrawRequested = true; }; -/** - * Handle an animation frame from the main thread. - */ -Runtime.prototype.animationFrame = function () { - if (this.renderer) { - // @todo: Only render when this.redrawRequested or clones rendered. - this.renderer.draw(); - } -}; - /** * Set up timers to repeatedly step in a browser. */ Runtime.prototype.start = function () { var interval = Runtime.THREAD_STEP_INTERVAL; - if (this.singleStepping) { - interval = this.singleStepInterval; - } else if (this.compatibilityMode) { + if (this.compatibilityMode) { interval = Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY; } this.currentStepTime = interval; From 14d5da52167ebd6f1839088f00a7fd3c5eab28b7 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Wed, 26 Oct 2016 13:27:12 -0400 Subject: [PATCH 1487/1971] Implement sprite info emitting and posting (#320) --- packages/scratch-vm/src/engine/runtime.js | 24 +++++++++++++++ .../scratch-vm/src/sprites/rendered-target.js | 29 ++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 15dc76296f..4fe871a65d 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -185,6 +185,12 @@ Runtime.BLOCK_GLOW_OFF = 'BLOCK_GLOW_OFF'; */ Runtime.VISUAL_REPORT = 'VISUAL_REPORT'; +/** + * Event name for sprite info report. + * @const {string} + */ +Runtime.SPRITE_INFO_REPORT = 'SPRITE_INFO_REPORT'; + /** * How rapidly we try to step threads by default, in ms. */ @@ -542,6 +548,7 @@ Runtime.prototype.setEditingTarget = function (editingTarget) { // Script glows must be cleared. this._scriptGlowsPreviousFrame = []; this._updateGlows(); + this.spriteInfoReport(editingTarget); }; /** @@ -661,6 +668,23 @@ Runtime.prototype.visualReport = function (blockId, value) { this.emit(Runtime.VISUAL_REPORT, blockId, String(value)); }; +/** + * Emit a sprite info report if the provided target is the editing target. + * @param {!Target} target Target to report sprite info for. + */ +Runtime.prototype.spriteInfoReport = function (target) { + if (target !== this._editingTarget) { + return; + } + this.emit(Runtime.SPRITE_INFO_REPORT, { + x: target.x, + y: target.y, + direction: target.direction, + visible: target.visible, + rotationStyle: target.rotationStyle + }); +}; + /** * Get a target by its id. * @param {string} targetId Id of target to find. diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 18aa4e10ee..80cd79aa3a 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -158,6 +158,7 @@ RenderedTarget.prototype.setXY = function (x, y) { this.runtime.requestRedraw(); } } + this.runtime.spriteInfoReport(this); }; /** @@ -200,6 +201,7 @@ RenderedTarget.prototype.setDirection = function (direction) { this.runtime.requestRedraw(); } } + this.runtime.spriteInfoReport(this); }; /** @@ -227,7 +229,7 @@ RenderedTarget.prototype.setVisible = function (visible) { if (this.isStage) { return; } - this.visible = visible; + this.visible = !!visible; if (this.renderer) { this.renderer.updateDrawableProperties(this.drawableID, { visible: this.visible @@ -236,6 +238,7 @@ RenderedTarget.prototype.setVisible = function (visible) { this.runtime.requestRedraw(); } } + this.runtime.spriteInfoReport(this); }; /** @@ -341,6 +344,7 @@ RenderedTarget.prototype.setRotationStyle = function (rotationStyle) { this.runtime.requestRedraw(); } } + this.runtime.spriteInfoReport(this); }; /** @@ -381,6 +385,7 @@ RenderedTarget.prototype.updateAllDrawableProperties = function () { this.runtime.requestRedraw(); } } + this.runtime.spriteInfoReport(this); }; /** @@ -601,6 +606,28 @@ RenderedTarget.prototype.onGreenFlag = function () { this.clearEffects(); }; +/** + * Post/edit sprite info. + * @param {object} data An object with sprite info data to set. + */ +RenderedTarget.prototype.postSpriteInfo = function (data) { + if (data.hasOwnProperty('x')) { + this.setXY(data.x, this.y); + } + if (data.hasOwnProperty('y')) { + this.setXY(this.x, data.y); + } + if (data.hasOwnProperty('direction')) { + this.setDirection(data.direction); + } + if (data.hasOwnProperty('rotationStyle')) { + this.setRotationStyle(data.rotationStyle); + } + if (data.hasOwnProperty('visible')) { + this.setVisible(data.visible); + } +}; + /** * Dispose, destroying any run-time properties. */ From cab8711a9d5d5e77a9530f4b0bbc7f3ad07aae81 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Wed, 26 Oct 2016 13:27:24 -0400 Subject: [PATCH 1488/1971] Rename and delete sprites (#319) * Add function to rename sprites * Add delete sprite function * Add `isSprite` helper and duplicate check for rename --- packages/scratch-vm/src/sprites/rendered-target.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 80cd79aa3a..89270397a5 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -397,6 +397,14 @@ RenderedTarget.prototype.getName = function () { return this.sprite.name; }; +/** + * Return whether this rendered target is a sprite (not a clone, not the stage). + * @return {boolean} True if not a clone and not the stage. + */ +RenderedTarget.prototype.isSprite = function () { + return !this.isStage && this.isOriginal; +}; + /** * Return the rendered target's tight bounding box. * Includes top, left, bottom, right attributes in Scratch coordinates. From c9cb91e4228f5595036773de5a16305b366749d7 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Thu, 10 Nov 2016 15:05:49 -0500 Subject: [PATCH 1489/1971] Don't copy threads in step; stable restartThread (#331) --- packages/scratch-vm/src/engine/runtime.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 4fe871a65d..36722a458b 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -329,6 +329,24 @@ Runtime.prototype._removeThread = function (thread) { } }; +/** + * Restart a thread in place, maintaining its position in the list of threads. + * This is used by `startHats` to and is necessary to ensure 2.0-like execution order. + * Test project: https://scratch.mit.edu/projects/130183108/ + * @param {!Thread} thread Thread object to restart. + */ +Runtime.prototype._restartThread = function (thread) { + var newThread = new Thread(thread.topBlock); + newThread.target = thread.target; + newThread.pushStack(thread.topBlock); + var i = this.threads.indexOf(thread); + if (i > -1) { + this.threads[i] = newThread; + } else { + this.threads.push(thread); + } +}; + /** * Return whether a thread is currently active/running. * @param {?Thread} thread Thread object to check. @@ -422,7 +440,8 @@ Runtime.prototype.startHats = function (requestedHatOpcode, for (var i = 0; i < instance.threads.length; i++) { if (instance.threads[i].topBlock === topBlockId && instance.threads[i].target === target) { - instance._removeThread(instance.threads[i]); + instance._restartThread(instance.threads[i]); + return; } } } else { From f8b900b72fbc8cabfcc6f0442ebd952a837e65ad Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Mon, 14 Nov 2016 12:32:45 -0500 Subject: [PATCH 1490/1971] Keep min and max scale in relative bounds as in Scratch 2.0 (#333) --- packages/scratch-vm/src/sprites/rendered-target.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 89270397a5..50ad7e8f5b 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -249,9 +249,18 @@ RenderedTarget.prototype.setSize = function (size) { if (this.isStage) { return; } - // Keep size between 5% and 535%. - this.size = MathUtil.clamp(size, 5, 535); if (this.renderer) { + // Clamp to scales relative to costume and stage size. + // See original ScratchSprite.as:setSize. + var costumeSize = this.renderer.getSkinSize(this.drawableID); + var origW = Math.round(costumeSize[0]); + var origH = Math.round(costumeSize[1]); + var minScale = Math.min(1, Math.max(5 / origW, 5 / origH)); + var maxScale = Math.min( + (1.5 * this.runtime.constructor.STAGE_WIDTH) / origW, + (1.5 * this.runtime.constructor.STAGE_HEIGHT) / origH + ); + this.size = Math.round(MathUtil.clamp(size / 100, minScale, maxScale) * 100); var renderedDirectionScale = this._getRenderedDirectionAndScale(); this.renderer.updateDrawableProperties(this.drawableID, { direction: renderedDirectionScale.direction, From d7617ff36b55fd9fc0871817b9fcd314ddbb3aee Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 28 Nov 2016 11:41:34 -0500 Subject: [PATCH 1491/1971] Merge pull request #348 from rschamp/feature/flag-glows Add PROJECT_RUN_START/STOP events --- packages/scratch-vm/src/engine/runtime.js | 53 +++++++++++++++++++---- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 36722a458b..5c7b788c4c 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -83,6 +83,12 @@ var Runtime = function () { */ this._scriptGlowsPreviousFrame = []; + /** + * Number of threads running during the previous frame + * @type {number} + */ + this._threadCount = 0; + /** * Currently known number of clones, used to enforce clone limit. * @type {number} @@ -159,13 +165,13 @@ Runtime.STAGE_HEIGHT = 360; * Event name for glowing a script. * @const {string} */ -Runtime.SCRIPT_GLOW_ON = 'STACK_GLOW_ON'; +Runtime.SCRIPT_GLOW_ON = 'SCRIPT_GLOW_ON'; /** * Event name for unglowing a script. * @const {string} */ -Runtime.SCRIPT_GLOW_OFF = 'STACK_GLOW_OFF'; +Runtime.SCRIPT_GLOW_OFF = 'SCRIPT_GLOW_OFF'; /** * Event name for glowing a block. @@ -179,6 +185,18 @@ Runtime.BLOCK_GLOW_ON = 'BLOCK_GLOW_ON'; */ Runtime.BLOCK_GLOW_OFF = 'BLOCK_GLOW_OFF'; +/** + * Event name for glowing the green flag + * @const {string} + */ +Runtime.PROJECT_RUN_START = 'PROJECT_RUN_START'; + +/** + * Event name for unglowing the green flag + * @const {string} + */ +Runtime.PROJECT_RUN_STOP = 'PROJECT_RUN_STOP'; + /** * Event name for visual value report. * @const {string} @@ -550,8 +568,9 @@ Runtime.prototype._step = function () { } } this.redrawRequested = false; - var inactiveThreads = this.sequencer.stepThreads(); - this._updateGlows(inactiveThreads); + var doneThreads = this.sequencer.stepThreads(); + this._updateGlows(doneThreads); + this._setThreadCount(this.threads.length + doneThreads.length); if (this.renderer) { // @todo: Only render when this.redrawRequested or clones rendered. this.renderer.draw(); @@ -639,6 +658,22 @@ Runtime.prototype._updateGlows = function (optExtraThreads) { this._scriptGlowsPreviousFrame = finalScriptGlows; }; +/** + * Emit run start/stop after each tick. Emits when `this.threads.length` goes + * between non-zero and zero + * + * @param {number} threadCount The new threadCount + */ +Runtime.prototype._setThreadCount = function (threadCount) { + if (this._threadCount === 0 && threadCount > 0) { + this.emit(Runtime.PROJECT_RUN_START); + } + if (this._threadCount > 0 && threadCount === 0) { + this.emit(Runtime.PROJECT_RUN_STOP); + } + this._threadCount = threadCount; +}; + /** * "Quiet" a script's glow: stop the VM from generating glow/unglow events * about that script. Use when a script has just been deleted, but we may @@ -659,9 +694,9 @@ Runtime.prototype.quietGlow = function (scriptBlockId) { */ Runtime.prototype.glowBlock = function (blockId, isGlowing) { if (isGlowing) { - this.emit(Runtime.BLOCK_GLOW_ON, blockId); + this.emit(Runtime.BLOCK_GLOW_ON, {id: blockId}); } else { - this.emit(Runtime.BLOCK_GLOW_OFF, blockId); + this.emit(Runtime.BLOCK_GLOW_OFF, {id: blockId}); } }; @@ -672,9 +707,9 @@ Runtime.prototype.glowBlock = function (blockId, isGlowing) { */ Runtime.prototype.glowScript = function (topBlockId, isGlowing) { if (isGlowing) { - this.emit(Runtime.SCRIPT_GLOW_ON, topBlockId); + this.emit(Runtime.SCRIPT_GLOW_ON, {id: topBlockId}); } else { - this.emit(Runtime.SCRIPT_GLOW_OFF, topBlockId); + this.emit(Runtime.SCRIPT_GLOW_OFF, {id: topBlockId}); } }; @@ -684,7 +719,7 @@ Runtime.prototype.glowScript = function (topBlockId, isGlowing) { * @param {string} value Value to show associated with the block. */ Runtime.prototype.visualReport = function (blockId, value) { - this.emit(Runtime.VISUAL_REPORT, blockId, String(value)); + this.emit(Runtime.VISUAL_REPORT, {id: blockId, value: String(value)}); }; /** From 144ba3683d7508fad66639600754a50768435ea0 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 9 Dec 2016 09:43:01 -0500 Subject: [PATCH 1492/1971] Merge pull request #353 from rschamp/feature/sprite-info Report costume data in sprite info reports and targetsUpdate --- packages/scratch-vm/src/engine/runtime.js | 15 +++----- .../scratch-vm/src/sprites/rendered-target.js | 36 +++++++++++++++++++ 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 5c7b788c4c..a404ce4211 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -723,20 +723,13 @@ Runtime.prototype.visualReport = function (blockId, value) { }; /** - * Emit a sprite info report if the provided target is the editing target. + * Emit a sprite info report if the provided target is the original sprite * @param {!Target} target Target to report sprite info for. */ Runtime.prototype.spriteInfoReport = function (target) { - if (target !== this._editingTarget) { - return; - } - this.emit(Runtime.SPRITE_INFO_REPORT, { - x: target.x, - y: target.y, - direction: target.direction, - visible: target.visible, - rotationStyle: target.rotationStyle - }); + if (!target.isOriginal) return; + + this.emit(Runtime.SPRITE_INFO_REPORT, target.toJSON()); }; /** diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 50ad7e8f5b..08ee727f8b 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -329,6 +329,7 @@ RenderedTarget.prototype.setCostume = function (index) { this.runtime.requestRedraw(); } } + this.runtime.spriteInfoReport(this); }; /** @@ -370,6 +371,22 @@ RenderedTarget.prototype.getCostumeIndexByName = function (costumeName) { return -1; }; +/** + * Get a costume of this rendered target by id. + * @return {object} current costume + */ +RenderedTarget.prototype.getCurrentCostume = function () { + return this.sprite.costumes[this.currentCostume]; +}; + +/** + * Get full costume list + * @return {object[]} list of costumes + */ +RenderedTarget.prototype.getCostumes = function () { + return this.sprite.costumes; +}; + /** * Update all drawable properties for this rendered target. * Use when a batch has changed, e.g., when the drawable is first created. @@ -645,6 +662,25 @@ RenderedTarget.prototype.postSpriteInfo = function (data) { } }; +/** + * Serialize sprite info, used when emitting events about the sprite + * @returns {object} sprite data as a simple object + */ +RenderedTarget.prototype.toJSON = function () { + return { + id: this.id, + name: this.getName(), + isStage: this.isStage, + x: this.x, + y: this.y, + direction: this.direction, + costume: this.getCurrentCostume(), + costumeCount: this.getCostumes().length, + visible: this.visible, + rotationStyle: this.rotationStyle + }; +}; + /** * Dispose, destroying any run-time properties. */ From b692ace71b32587d5be10513319a753ed8a7e487 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Tue, 3 Jan 2017 09:37:42 -0500 Subject: [PATCH 1493/1971] Merge pull request #361 from thisandagain/bugfix/360 Fix hat blocks in horizontal grammar --- packages/scratch-vm/src/engine/runtime.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index a404ce4211..9c49d9ec1e 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -435,12 +435,24 @@ Runtime.prototype.startHats = function (requestedHatOpcode, // Not the right hat. return; } + // Match any requested fields. // For example: ensures that broadcasts match. // This needs to happen before the block is evaluated // (i.e., before the predicate can be run) because "broadcast and wait" // needs to have a precise collection of started threads. var hatFields = target.blocks.getFields(topBlockId); + + // If no fields are present, check inputs (horizontal blocks) + if (Object.keys(hatFields).length === 0) { + var hatInputs = target.blocks.getInputs(topBlockId); + for (var input in hatInputs) { + var id = hatInputs[input].block; + var fields = target.blocks.getFields(id); + hatFields = Object.assign(fields, hatFields); + } + } + if (optMatchFields) { for (var matchField in optMatchFields) { if (hatFields[matchField].value !== @@ -450,6 +462,7 @@ Runtime.prototype.startHats = function (requestedHatOpcode, } } } + // Look up metadata for the relevant hat. var hatMeta = instance._hats[requestedHatOpcode]; if (hatMeta.restartExistingThreads) { From 1ca39d6fa3241388e3244d269c2a15027188ca70 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Tue, 3 Jan 2017 09:38:14 -0500 Subject: [PATCH 1494/1971] Merge pull request #362 from thisandagain/bugfix/coverage Add integration test coverage --- packages/scratch-vm/src/engine/runtime.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 9c49d9ec1e..81f95b1cb8 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -22,6 +22,7 @@ var defaultBlockPackages = { /** * Manages targets, scripts, and the sequencer. + * @constructor */ var Runtime = function () { // Bind event emitter @@ -609,7 +610,7 @@ Runtime.prototype.setEditingTarget = function (editingTarget) { Runtime.prototype.setCompatibilityMode = function (compatibilityModeOn) { this.compatibilityMode = compatibilityModeOn; if (this._steppingInterval) { - self.clearInterval(this._steppingInterval); + clearInterval(this._steppingInterval); this.start(); } }; @@ -819,7 +820,7 @@ Runtime.prototype.start = function () { interval = Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY; } this.currentStepTime = interval; - this._steppingInterval = self.setInterval(function () { + this._steppingInterval = setInterval(function () { this._step(); }.bind(this), interval); }; From 09d95a4535858c0cf887986d7a8f08fcc3be6ae7 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 6 Jan 2017 16:28:09 -0500 Subject: [PATCH 1495/1971] Merge pull request #373 from ericrosenbaum/sound Integrate audio engine --- packages/scratch-vm/src/engine/runtime.js | 10 +++++ .../scratch-vm/src/sprites/rendered-target.js | 37 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 81f95b1cb8..80b86d926f 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -15,6 +15,7 @@ var defaultBlockPackages = { scratch3_looks: require('../blocks/scratch3_looks'), scratch3_motion: require('../blocks/scratch3_motion'), scratch3_operators: require('../blocks/scratch3_operators'), + scratch3_sound: require('../blocks/scratch3_sound'), scratch3_sensing: require('../blocks/scratch3_sensing'), scratch3_data: require('../blocks/scratch3_data'), scratch3_procedures: require('../blocks/scratch3_procedures') @@ -317,6 +318,14 @@ Runtime.prototype.attachRenderer = function (renderer) { this.renderer = renderer; }; +/** + * Attach the audio engine + * @param {!AudioEngine} audioEngine The audio engine to attach + */ +Runtime.prototype.attachAudioEngine = function (audioEngine) { + this.audioEngine = audioEngine; +}; + // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- @@ -553,6 +562,7 @@ Runtime.prototype.stopAll = function () { // Dispose all clones. var newTargets = []; for (var i = 0; i < this.targets.length; i++) { + this.targets[i].onStopAll(); if (this.targets[i].hasOwnProperty('isOriginal') && !this.targets[i].isOriginal) { this.targets[i].dispose(); diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 08ee727f8b..b5b89365c9 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -62,6 +62,18 @@ RenderedTarget.prototype.initDrawable = function () { 'control_start_as_clone', null, this ); } + + /** + * Audio player + */ + this.audioPlayer = null; + if (this.runtime && this.runtime.audioEngine) { + if (this.isOriginal) { + this.sprite.audioPlayer = this.runtime.audioEngine.createPlayer(); + this.sprite.audioPlayer.loadSounds(this.sprite.sounds); + } + this.audioPlayer = this.sprite.audioPlayer; + } }; /** @@ -371,6 +383,20 @@ RenderedTarget.prototype.getCostumeIndexByName = function (costumeName) { return -1; }; +/** + * Get a sound index of this rendered target, by name of the sound. + * @param {?string} soundName Name of a sound. + * @return {number} Index of the named sound, or -1 if not present. + */ +RenderedTarget.prototype.getSoundIndexByName = function (soundName) { + for (var i = 0; i < this.sprite.sounds.length; i++) { + if (this.sprite.sounds[i].name === soundName) { + return i; + } + } + return -1; +}; + /** * Get a costume of this rendered target by id. * @return {object} current costume @@ -640,6 +666,17 @@ RenderedTarget.prototype.onGreenFlag = function () { this.clearEffects(); }; +/** + * Called when the project receives a "stop all" + * Stop all sounds + */ +RenderedTarget.prototype.onStopAll = function () { + if (this.audioPlayer) { + this.audioPlayer.stopAllSounds(); + this.audioPlayer.clearEffects(); + } +}; + /** * Post/edit sprite info. * @param {object} data An object with sprite info data to set. From 1d708ece0d7c0be2734ac5a74d6cec37a1e09d7a Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 12 Jan 2017 15:38:11 -0500 Subject: [PATCH 1496/1971] Merge pull request #378 from rschamp/bugfix/svg-costume-add Fix adding SVG costumes --- packages/scratch-vm/src/sprites/rendered-target.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index b5b89365c9..69177b9aca 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -329,13 +329,17 @@ RenderedTarget.prototype.setCostume = function (index) { ); if (this.renderer) { var costume = this.sprite.costumes[this.currentCostume]; + var rotationCenter = costume.bitmapResolution ? [ + costume.rotationCenterX / costume.bitmapResolution, + costume.rotationCenterY / costume.bitmapResolution + ] : [ + costume.rotationCenterX, + costume.rotationCenterY + ]; this.renderer.updateDrawableProperties(this.drawableID, { skin: costume.skin, costumeResolution: costume.bitmapResolution, - rotationCenter: [ - costume.rotationCenterX / costume.bitmapResolution, - costume.rotationCenterY / costume.bitmapResolution - ] + rotationCenter: rotationCenter }); if (this.visible) { this.runtime.requestRedraw(); From 320858239c5714784288c50eea2074cf54bf2f82 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Fri, 13 Jan 2017 13:34:26 -0800 Subject: [PATCH 1497/1971] Move build outputs into a `dist/` subdirectory (#375) * Move Node output: /dist.js => /dist/node/scratch-vm.js * Move web output: /vm{.js,.min.js} => /dist/web/scratch-vm{.js,.min.js} * Update build output references in package.json and the playground's index.html * Move the VirtualMachine class out of index.js into its own file, referenced by index.js. The VirtualMachine class is otherwise unchanged. * Add .gitattributes rules for new file types which were added to this repository without specifying their text/binary attributes * Turn on source maps in webpack and add corresponding .gitignore rule --- packages/scratch-vm/src/virtual-machine.js | 339 +++++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 packages/scratch-vm/src/virtual-machine.js diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js new file mode 100644 index 0000000000..8e9b4a9402 --- /dev/null +++ b/packages/scratch-vm/src/virtual-machine.js @@ -0,0 +1,339 @@ +var EventEmitter = require('events'); +var util = require('util'); + +var Runtime = require('./engine/runtime'); +var sb2import = require('./import/sb2import'); + +/** + * Handles connections between blocks, stage, and extensions. + * @constructor + */ +var VirtualMachine = function () { + var instance = this; + // Bind event emitter and runtime to VM instance + EventEmitter.call(instance); + /** + * VM runtime, to store blocks, I/O devices, sprites/targets, etc. + * @type {!Runtime} + */ + instance.runtime = new Runtime(); + /** + * The "currently editing"/selected target ID for the VM. + * Block events from any Blockly workspace are routed to this target. + * @type {!string} + */ + instance.editingTarget = null; + // Runtime emits are passed along as VM emits. + instance.runtime.on(Runtime.SCRIPT_GLOW_ON, function (glowData) { + instance.emit(Runtime.SCRIPT_GLOW_ON, glowData); + }); + instance.runtime.on(Runtime.SCRIPT_GLOW_OFF, function (glowData) { + instance.emit(Runtime.SCRIPT_GLOW_OFF, glowData); + }); + instance.runtime.on(Runtime.BLOCK_GLOW_ON, function (glowData) { + instance.emit(Runtime.BLOCK_GLOW_ON, glowData); + }); + instance.runtime.on(Runtime.BLOCK_GLOW_OFF, function (glowData) { + instance.emit(Runtime.BLOCK_GLOW_OFF, glowData); + }); + instance.runtime.on(Runtime.PROJECT_RUN_START, function () { + instance.emit(Runtime.PROJECT_RUN_START); + }); + instance.runtime.on(Runtime.PROJECT_RUN_STOP, function () { + instance.emit(Runtime.PROJECT_RUN_STOP); + }); + instance.runtime.on(Runtime.VISUAL_REPORT, function (visualReport) { + instance.emit(Runtime.VISUAL_REPORT, visualReport); + }); + instance.runtime.on(Runtime.SPRITE_INFO_REPORT, function (spriteInfo) { + instance.emit(Runtime.SPRITE_INFO_REPORT, spriteInfo); + }); + + this.blockListener = this.blockListener.bind(this); + this.flyoutBlockListener = this.flyoutBlockListener.bind(this); +}; + +/** + * Inherit from EventEmitter + */ +util.inherits(VirtualMachine, EventEmitter); + +/** + * Start running the VM - do this before anything else. + */ +VirtualMachine.prototype.start = function () { + this.runtime.start(); +}; + +/** + * "Green flag" handler - start all threads starting with a green flag. + */ +VirtualMachine.prototype.greenFlag = function () { + this.runtime.greenFlag(); +}; + +/** + * Set whether the VM is in "turbo mode." + * When true, loops don't yield to redraw. + * @param {Boolean} turboModeOn Whether turbo mode should be set. + */ +VirtualMachine.prototype.setTurboMode = function (turboModeOn) { + this.runtime.turboMode = !!turboModeOn; +}; + +/** + * Set whether the VM is in 2.0 "compatibility mode." + * When true, ticks go at 2.0 speed (30 TPS). + * @param {Boolean} compatibilityModeOn Whether compatibility mode is set. + */ +VirtualMachine.prototype.setCompatibilityMode = function (compatibilityModeOn) { + this.runtime.setCompatibilityMode(!!compatibilityModeOn); +}; + +/** + * Stop all threads and running activities. + */ +VirtualMachine.prototype.stopAll = function () { + this.runtime.stopAll(); +}; + +/** + * Clear out current running project data. + */ +VirtualMachine.prototype.clear = function () { + this.runtime.dispose(); + this.editingTarget = null; + this.emitTargetsUpdate(); +}; + +/** + * Get data for playground. Data comes back in an emitted event. + */ +VirtualMachine.prototype.getPlaygroundData = function () { + var instance = this; + // Only send back thread data for the current editingTarget. + var threadData = this.runtime.threads.filter(function (thread) { + return thread.target === instance.editingTarget; + }); + // Remove the target key, since it's a circular reference. + var filteredThreadData = JSON.stringify(threadData, function (key, value) { + if (key === 'target') return; + return value; + }, 2); + this.emit('playgroundData', { + blocks: this.editingTarget.blocks, + threads: filteredThreadData + }); +}; + +/** + * Post I/O data to the virtual devices. + * @param {?string} device Name of virtual I/O device. + * @param {Object} data Any data object to post to the I/O device. + */ +VirtualMachine.prototype.postIOData = function (device, data) { + if (this.runtime.ioDevices[device]) { + this.runtime.ioDevices[device].postData(data); + } +}; + +/** + * Load a project from a Scratch 2.0 JSON representation. + * @param {?string} json JSON string representing the project. + */ +VirtualMachine.prototype.loadProject = function (json) { + this.clear(); + // @todo: Handle other formats, e.g., Scratch 1.4, Scratch 3.0. + sb2import(json, this.runtime); + // Select the first target for editing, e.g., the first sprite. + this.editingTarget = this.runtime.targets[1]; + // Update the VM user's knowledge of targets and blocks on the workspace. + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); +}; + +/** + * Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format. + * @param {?string} json JSON string representing the sprite. + */ +VirtualMachine.prototype.addSprite2 = function (json) { + // Select new sprite. + this.editingTarget = sb2import(json, this.runtime, true); + // Update the VM user's knowledge of targets and blocks on the workspace. + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); +}; + +/** + * Add a costume to the current editing target. + * @param {!Object} costumeObject Object representing the costume. + */ +VirtualMachine.prototype.addCostume = function (costumeObject) { + this.editingTarget.sprite.costumes.push(costumeObject); + // Switch to the costume. + this.editingTarget.setCostume( + this.editingTarget.sprite.costumes.length - 1 + ); +}; + +/** + * Add a backdrop to the stage. + * @param {!Object} backdropObject Object representing the backdrop. + */ +VirtualMachine.prototype.addBackdrop = function (backdropObject) { + var stage = this.runtime.getTargetForStage(); + stage.sprite.costumes.push(backdropObject); + // Switch to the backdrop. + stage.setCostume(stage.sprite.costumes.length - 1); +}; + +/** + * Rename a sprite. + * @param {string} targetId ID of a target whose sprite to rename. + * @param {string} newName New name of the sprite. + */ +VirtualMachine.prototype.renameSprite = function (targetId, newName) { + var target = this.runtime.getTargetById(targetId); + if (target) { + if (!target.isSprite()) { + throw new Error('Cannot rename non-sprite targets.'); + } + var sprite = target.sprite; + if (!sprite) { + throw new Error('No sprite associated with this target.'); + } + sprite.name = newName; + this.emitTargetsUpdate(); + } else { + throw new Error('No target with the provided id.'); + } +}; + +/** + * Delete a sprite and all its clones. + * @param {string} targetId ID of a target whose sprite to delete. + */ +VirtualMachine.prototype.deleteSprite = function (targetId) { + var target = this.runtime.getTargetById(targetId); + if (target) { + if (!target.isSprite()) { + throw new Error('Cannot delete non-sprite targets.'); + } + var sprite = target.sprite; + if (!sprite) { + throw new Error('No sprite associated with this target.'); + } + var currentEditingTarget = this.editingTarget; + for (var i = 0; i < sprite.clones.length; i++) { + var clone = sprite.clones[i]; + this.runtime.stopForTarget(sprite.clones[i]); + this.runtime.disposeTarget(sprite.clones[i]); + // Ensure editing target is switched if we are deleting it. + if (clone === currentEditingTarget) { + this.setEditingTarget(this.runtime.targets[0].id); + } + } + // Sprite object should be deleted by GC. + this.emitTargetsUpdate(); + } else { + throw new Error('No target with the provided id.'); + } +}; + +/** + * Set the renderer for the VM/runtime + * @param {!RenderWebGL} renderer The renderer to attach + */ +VirtualMachine.prototype.attachRenderer = function (renderer) { + this.runtime.attachRenderer(renderer); +}; + +/** + * Set the audio engine for the VM/runtime + * @param {!AudioEngine} audioEngine The audio engine to attach + */ +VirtualMachine.prototype.attachAudioEngine = function (audioEngine) { + this.runtime.attachAudioEngine(audioEngine); +}; + +/** + * Handle a Blockly event for the current editing target. + * @param {!Blockly.Event} e Any Blockly event. + */ +VirtualMachine.prototype.blockListener = function (e) { + if (this.editingTarget) { + this.editingTarget.blocks.blocklyListen(e, this.runtime); + } +}; + +/** + * Handle a Blockly event for the flyout. + * @param {!Blockly.Event} e Any Blockly event. + */ +VirtualMachine.prototype.flyoutBlockListener = function (e) { + this.runtime.flyoutBlocks.blocklyListen(e, this.runtime); +}; + +/** + * Set an editing target. An editor UI can use this function to switch + * between editing different targets, sprites, etc. + * After switching the editing target, the VM may emit updates + * to the list of targets and any attached workspace blocks + * (see `emitTargetsUpdate` and `emitWorkspaceUpdate`). + * @param {string} targetId Id of target to set as editing. + */ +VirtualMachine.prototype.setEditingTarget = function (targetId) { + // Has the target id changed? If not, exit. + if (targetId === this.editingTarget.id) { + return; + } + var target = this.runtime.getTargetById(targetId); + if (target) { + this.editingTarget = target; + // Emit appropriate UI updates. + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(target); + } +}; + +/** + * Emit metadata about available targets. + * An editor UI could use this to display a list of targets and show + * the currently editing one. + */ +VirtualMachine.prototype.emitTargetsUpdate = function () { + this.emit('targetsUpdate', { + // [[target id, human readable target name], ...]. + targetList: this.runtime.targets.filter(function (target) { + // Don't report clones. + return !target.hasOwnProperty('isOriginal') || target.isOriginal; + }).map(function (target) { + return target.toJSON(); + }), + // Currently editing target id. + editingTarget: this.editingTarget ? this.editingTarget.id : null + }); +}; + +/** + * Emit an Blockly/scratch-blocks compatible XML representation + * of the current editing target's blocks. + */ +VirtualMachine.prototype.emitWorkspaceUpdate = function () { + this.emit('workspaceUpdate', { + xml: this.editingTarget.blocks.toXML() + }); +}; + +/** + * Post/edit sprite info for the current editing target. + * @param {object} data An object with sprite info data to set. + */ +VirtualMachine.prototype.postSpriteInfo = function (data) { + this.editingTarget.postSpriteInfo(data); +}; + +module.exports = VirtualMachine; From 6cdc09bbc1ac6013b7bcf69ebeb01e54e9ac2d7f Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 13 Jan 2017 16:55:39 -0500 Subject: [PATCH 1498/1971] Merge pull request #379 from rschamp/bugfix/empty-rotation-center Only update costume rotationCenter if it exists --- .../scratch-vm/src/sprites/rendered-target.js | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 69177b9aca..dc7c25d2f5 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -329,18 +329,21 @@ RenderedTarget.prototype.setCostume = function (index) { ); if (this.renderer) { var costume = this.sprite.costumes[this.currentCostume]; - var rotationCenter = costume.bitmapResolution ? [ - costume.rotationCenterX / costume.bitmapResolution, - costume.rotationCenterY / costume.bitmapResolution - ] : [ - costume.rotationCenterX, - costume.rotationCenterY - ]; - this.renderer.updateDrawableProperties(this.drawableID, { + var drawableProperties = { skin: costume.skin, - costumeResolution: costume.bitmapResolution, - rotationCenter: rotationCenter - }); + costumeResolution: costume.bitmapResolution + }; + if ( + typeof costume.rotationCenterX !== 'undefined' && + typeof costume.rotationCenterY !== 'undefined' + ) { + var scale = costume.bitmapResolution || 1; + drawableProperties.rotationCenter = [ + costume.rotationCenterX / scale, + costume.rotationCenterY / scale + ]; + } + this.renderer.updateDrawableProperties(this.drawableID, drawableProperties); if (this.visible) { this.runtime.requestRedraw(); } From 0b34144f34b8adcda9bfac1489bfded5d00121d0 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Fri, 20 Jan 2017 13:22:05 -0800 Subject: [PATCH 1499/1971] Merge pull request #384 from cwillisf/pen Implement pen blocks --- packages/scratch-vm/src/engine/runtime.js | 1 + packages/scratch-vm/src/sprites/rendered-target.js | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 80b86d926f..3a1967baac 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -15,6 +15,7 @@ var defaultBlockPackages = { scratch3_looks: require('../blocks/scratch3_looks'), scratch3_motion: require('../blocks/scratch3_motion'), scratch3_operators: require('../blocks/scratch3_operators'), + scratch3_pen: require('../blocks/scratch3_pen'), scratch3_sound: require('../blocks/scratch3_sound'), scratch3_sensing: require('../blocks/scratch3_sensing'), scratch3_data: require('../blocks/scratch3_data'), diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index dc7c25d2f5..36affd7b12 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -125,6 +125,12 @@ RenderedTarget.prototype.size = 100; */ RenderedTarget.prototype.currentCostume = 0; +/** + * Event which fires when a target moves. + * @type {string} + */ +RenderedTarget.EVENT_TARGET_MOVED = 'TARGET_MOVED'; + /** * Rotation style for "all around"/spinning. * @enum @@ -160,6 +166,8 @@ RenderedTarget.prototype.setXY = function (x, y) { if (this.isStage) { return; } + var oldX = this.x; + var oldY = this.y; this.x = x; this.y = y; if (this.renderer) { @@ -170,6 +178,7 @@ RenderedTarget.prototype.setXY = function (x, y) { this.runtime.requestRedraw(); } } + this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY); this.runtime.spriteInfoReport(this); }; From 976f790e65ce09a5656d5a67c2f605b32b8f0dd7 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Fri, 27 Jan 2017 10:52:24 -0500 Subject: [PATCH 1500/1971] Merge pull request #401 from griffpatch/Effect-not-correctly-copied-to-clones Effect not correctly copied to clones #337 --- packages/scratch-vm/src/sprites/rendered-target.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 36affd7b12..9eb8956b2f 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -437,7 +437,7 @@ RenderedTarget.prototype.updateAllDrawableProperties = function () { if (this.renderer) { var renderedDirectionScale = this._getRenderedDirectionAndScale(); var costume = this.sprite.costumes[this.currentCostume]; - this.renderer.updateDrawableProperties(this.drawableID, { + var props = { position: [this.x, this.y], direction: renderedDirectionScale.direction, scale: renderedDirectionScale.scale, @@ -448,7 +448,11 @@ RenderedTarget.prototype.updateAllDrawableProperties = function () { costume.rotationCenterX / costume.bitmapResolution, costume.rotationCenterY / costume.bitmapResolution ] - }); + }; + for (var effectID in this.effects) { + props[effectID] = this.effects[effectID]; + } + this.renderer.updateDrawableProperties(this.drawableID, props); if (this.visible) { this.runtime.requestRedraw(); } From 9f45e87329ae0ed28c6740125a31916984c6c803 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 1 Feb 2017 17:37:22 -0500 Subject: [PATCH 1501/1971] Merge pull request #423 from LLK/greenkeeper/initial MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update dependencies to enable Greenkeeper 🌴 --- packages/scratch-vm/src/engine/runtime.js | 8 ++++---- .../scratch-vm/src/sprites/rendered-target.js | 20 +++++++++---------- packages/scratch-vm/src/virtual-machine.js | 10 +++++----- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 3a1967baac..f196a6a69d 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -276,7 +276,7 @@ Runtime.prototype.getOpcodeFunction = function (opcode) { /** * Return whether an opcode represents a hat block. * @param {!string} opcode The opcode to look up. - * @return {Boolean} True if the op is known to be a hat. + * @return {boolean} True if the op is known to be a hat. */ Runtime.prototype.getIsHat = function (opcode) { return this._hats.hasOwnProperty(opcode); @@ -285,7 +285,7 @@ Runtime.prototype.getIsHat = function (opcode) { /** * Return whether an opcode represents an edge-activated hat block. * @param {!string} opcode The opcode to look up. - * @return {Boolean} True if the op is known to be a edge-activated hat. + * @return {boolean} True if the op is known to be a edge-activated hat. */ Runtime.prototype.getIsEdgeActivatedHat = function (opcode) { return this._hats.hasOwnProperty(opcode) && @@ -379,7 +379,7 @@ Runtime.prototype._restartThread = function (thread) { /** * Return whether a thread is currently active/running. * @param {?Thread} thread Thread object to check. - * @return {Boolean} True if the thread is active/running. + * @return {boolean} True if the thread is active/running. */ Runtime.prototype.isActiveThread = function (thread) { return this.threads.indexOf(thread) > -1; @@ -427,7 +427,7 @@ Runtime.prototype.allScriptsDo = function (f, optTarget) { /** * Start all relevant hats. * @param {!string} requestedHatOpcode Opcode of hats to start. - * @param {Object=} optMatchFields Optionally, fields to match on the hat. + * @param {object=} optMatchFields Optionally, fields to match on the hat. * @param {Target=} optTarget Optionally, a target to restrict to. * @return {Array.} List of threads started by this function. */ diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 9eb8956b2f..b82afa8468 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -184,7 +184,7 @@ RenderedTarget.prototype.setXY = function (x, y) { /** * Get the rendered direction and scale, after applying rotation style. - * @return {Object} Direction and scale to render. + * @return {object} Direction and scale to render. */ RenderedTarget.prototype._getRenderedDirectionAndScale = function () { // Default: no changes to `this.direction` or `this.scale`. @@ -480,7 +480,7 @@ RenderedTarget.prototype.isSprite = function () { /** * Return the rendered target's tight bounding box. * Includes top, left, bottom, right attributes in Scratch coordinates. - * @return {?Object} Tight bounding box, or null. + * @return {?object} Tight bounding box, or null. */ RenderedTarget.prototype.getBounds = function () { if (this.renderer) { @@ -493,7 +493,7 @@ RenderedTarget.prototype.getBounds = function () { * Return whether touching a point. * @param {number} x X coordinate of test point. * @param {number} y Y coordinate of test point. - * @return {Boolean} True iff the rendered target is touching the point. + * @return {boolean} True iff the rendered target is touching the point. */ RenderedTarget.prototype.isTouchingPoint = function (x, y) { if (this.renderer) { @@ -513,7 +513,7 @@ RenderedTarget.prototype.isTouchingPoint = function (x, y) { /** * Return whether touching a stage edge. - * @return {Boolean} True iff the rendered target is touching the stage edge. + * @return {boolean} True iff the rendered target is touching the stage edge. */ RenderedTarget.prototype.isTouchingEdge = function () { if (this.renderer) { @@ -533,7 +533,7 @@ RenderedTarget.prototype.isTouchingEdge = function () { /** * Return whether touching any of a named sprite's clones. * @param {string} spriteName Name of the sprite. - * @return {Boolean} True iff touching a clone of the sprite. + * @return {boolean} True iff touching a clone of the sprite. */ RenderedTarget.prototype.isTouchingSprite = function (spriteName) { var firstClone = this.runtime.getSpriteTargetByName(spriteName); @@ -550,7 +550,7 @@ RenderedTarget.prototype.isTouchingSprite = function (spriteName) { /** * Return whether touching a color. * @param {Array.} rgb [r,g,b], values between 0-255. - * @return {Promise.} True iff the rendered target is touching the color. + * @return {Promise.} True iff the rendered target is touching the color. */ RenderedTarget.prototype.isTouchingColor = function (rgb) { if (this.renderer) { @@ -561,9 +561,9 @@ RenderedTarget.prototype.isTouchingColor = function (rgb) { /** * Return whether rendered target's color is touching a color. - * @param {Object} targetRgb {Array.} [r,g,b], values between 0-255. - * @param {Object} maskRgb {Array.} [r,g,b], values between 0-255. - * @return {Promise.} True iff the color is touching the color. + * @param {object} targetRgb {Array.} [r,g,b], values between 0-255. + * @param {object} maskRgb {Array.} [r,g,b], values between 0-255. + * @return {Promise.} True iff the color is touching the color. */ RenderedTarget.prototype.colorIsTouchingColor = function (targetRgb, maskRgb) { if (this.renderer) { @@ -611,7 +611,7 @@ RenderedTarget.prototype.goBehindOther = function (other) { * Keep a desired position within a fence. * @param {number} newX New desired X position. * @param {number} newY New desired Y position. - * @param {Object=} optFence Optional fence with left, right, top bottom. + * @param {object=} optFence Optional fence with left, right, top bottom. * @return {Array.} Fenced X and Y coordinates. */ RenderedTarget.prototype.keepInFence = function (newX, newY, optFence) { diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 8e9b4a9402..003c80e9f9 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -75,7 +75,7 @@ VirtualMachine.prototype.greenFlag = function () { /** * Set whether the VM is in "turbo mode." * When true, loops don't yield to redraw. - * @param {Boolean} turboModeOn Whether turbo mode should be set. + * @param {boolean} turboModeOn Whether turbo mode should be set. */ VirtualMachine.prototype.setTurboMode = function (turboModeOn) { this.runtime.turboMode = !!turboModeOn; @@ -84,7 +84,7 @@ VirtualMachine.prototype.setTurboMode = function (turboModeOn) { /** * Set whether the VM is in 2.0 "compatibility mode." * When true, ticks go at 2.0 speed (30 TPS). - * @param {Boolean} compatibilityModeOn Whether compatibility mode is set. + * @param {boolean} compatibilityModeOn Whether compatibility mode is set. */ VirtualMachine.prototype.setCompatibilityMode = function (compatibilityModeOn) { this.runtime.setCompatibilityMode(!!compatibilityModeOn); @@ -129,7 +129,7 @@ VirtualMachine.prototype.getPlaygroundData = function () { /** * Post I/O data to the virtual devices. * @param {?string} device Name of virtual I/O device. - * @param {Object} data Any data object to post to the I/O device. + * @param {object} data Any data object to post to the I/O device. */ VirtualMachine.prototype.postIOData = function (device, data) { if (this.runtime.ioDevices[device]) { @@ -168,7 +168,7 @@ VirtualMachine.prototype.addSprite2 = function (json) { /** * Add a costume to the current editing target. - * @param {!Object} costumeObject Object representing the costume. + * @param {!object} costumeObject Object representing the costume. */ VirtualMachine.prototype.addCostume = function (costumeObject) { this.editingTarget.sprite.costumes.push(costumeObject); @@ -180,7 +180,7 @@ VirtualMachine.prototype.addCostume = function (costumeObject) { /** * Add a backdrop to the stage. - * @param {!Object} backdropObject Object representing the backdrop. + * @param {!object} backdropObject Object representing the backdrop. */ VirtualMachine.prototype.addBackdrop = function (backdropObject) { var stage = this.runtime.getTargetForStage(); From 6ec9b313ceef2fa6b1aefa4a933b0153fa8f068c Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 2 Feb 2017 17:45:45 -0500 Subject: [PATCH 1502/1971] Merge pull request #424 from ericrosenbaum/sound Refactor sound engine --- .../scratch-vm/src/sprites/rendered-target.js | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index b82afa8468..9bbc20b238 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -69,10 +69,9 @@ RenderedTarget.prototype.initDrawable = function () { this.audioPlayer = null; if (this.runtime && this.runtime.audioEngine) { if (this.isOriginal) { - this.sprite.audioPlayer = this.runtime.audioEngine.createPlayer(); - this.sprite.audioPlayer.loadSounds(this.sprite.sounds); + this.runtime.audioEngine.loadSounds(this.sprite.sounds); } - this.audioPlayer = this.sprite.audioPlayer; + this.audioPlayer = this.runtime.audioEngine.createPlayer(); } }; @@ -399,20 +398,6 @@ RenderedTarget.prototype.getCostumeIndexByName = function (costumeName) { return -1; }; -/** - * Get a sound index of this rendered target, by name of the sound. - * @param {?string} soundName Name of a sound. - * @return {number} Index of the named sound, or -1 if not present. - */ -RenderedTarget.prototype.getSoundIndexByName = function (soundName) { - for (var i = 0; i < this.sprite.sounds.length; i++) { - if (this.sprite.sounds[i].name === soundName) { - return i; - } - } - return -1; -}; - /** * Get a costume of this rendered target by id. * @return {object} current costume From 7488ffc34334cbfb691de008362b0549ccc3350d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 8 Feb 2017 10:28:02 -0500 Subject: [PATCH 1503/1971] Merge pull request #440 from griffpatch/bug/CaseInsensitiveBroadcast Bug caseinsensitivebroadcast --- packages/scratch-vm/src/engine/runtime.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index f196a6a69d..fc3b17a068 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -439,6 +439,11 @@ Runtime.prototype.startHats = function (requestedHatOpcode, } var instance = this; var newThreads = []; + + for (var opts in optMatchFields) { + optMatchFields[opts] = optMatchFields[opts].toUpperCase(); + } + // Consider all scripts, looking for hats with opcode `requestedHatOpcode`. this.allScriptsDo(function (topBlockId, target) { var potentialHatOpcode = target.blocks.getBlock(topBlockId).opcode; @@ -466,7 +471,7 @@ Runtime.prototype.startHats = function (requestedHatOpcode, if (optMatchFields) { for (var matchField in optMatchFields) { - if (hatFields[matchField].value !== + if (hatFields[matchField].value.toUpperCase() !== optMatchFields[matchField]) { // Field mismatch. return; From 38daeea8cf9fa5fc4477ba679725cfca8c7a741d Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Tue, 14 Feb 2017 16:55:38 -0800 Subject: [PATCH 1504/1971] Merge pull request #452 from griffpatch/feature/fencing Feature fencing --- packages/scratch-vm/src/sprites/rendered-target.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 9bbc20b238..3e1e06e125 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -167,15 +167,20 @@ RenderedTarget.prototype.setXY = function (x, y) { } var oldX = this.x; var oldY = this.y; - this.x = x; - this.y = y; if (this.renderer) { + var position = this.renderer.getFencedPositionOfDrawable(this.drawableID, [x, y]); + this.x = position.x; + this.y = position.y; + this.renderer.updateDrawableProperties(this.drawableID, { - position: [this.x, this.y] + position: position }); if (this.visible) { this.runtime.requestRedraw(); } + } else { + this.x = x; + this.y = y; } this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY); this.runtime.spriteInfoReport(this); From 2da2a7a27a0113f6dfbcd75c8d8e9b64f629a89a Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 15 Feb 2017 09:44:34 -0500 Subject: [PATCH 1505/1971] Merge pull request #458 from LLK/revert-452-feature/fencing Revert "Feature fencing" --- packages/scratch-vm/src/sprites/rendered-target.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 3e1e06e125..9bbc20b238 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -167,20 +167,15 @@ RenderedTarget.prototype.setXY = function (x, y) { } var oldX = this.x; var oldY = this.y; + this.x = x; + this.y = y; if (this.renderer) { - var position = this.renderer.getFencedPositionOfDrawable(this.drawableID, [x, y]); - this.x = position.x; - this.y = position.y; - this.renderer.updateDrawableProperties(this.drawableID, { - position: position + position: [this.x, this.y] }); if (this.visible) { this.runtime.requestRedraw(); } - } else { - this.x = x; - this.y = y; } this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY); this.runtime.spriteInfoReport(this); From 7077fbec46319af0a7b7e474a598d22388745d1f Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Thu, 16 Feb 2017 12:59:48 -0800 Subject: [PATCH 1506/1971] Merge pull request #457 from cwillisf/fix-cachalot Match Scratch 2.0 order of execution --- packages/scratch-vm/src/engine/runtime.js | 2 +- packages/scratch-vm/src/sprites/rendered-target.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index fc3b17a068..d9e59d36ac 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -414,7 +414,7 @@ Runtime.prototype.allScriptsDo = function (f, optTarget) { if (optTarget) { targets = [optTarget]; } - for (var t = 0; t < targets.length; t++) { + for (var t = targets.length - 1; t >= 0; t--) { var target = targets[t]; var scripts = target.blocks.getScripts(); for (var j = 0; j < scripts.length; j++) { diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 9bbc20b238..0a0a115f97 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -582,7 +582,7 @@ RenderedTarget.prototype.goBackLayers = function (nLayers) { /** * Move behind some other rendered target. - * @param {!Clone} other Other rendered target to move behind. + * @param {!RenderedTarget} other Other rendered target to move behind. */ RenderedTarget.prototype.goBehindOther = function (other) { if (this.renderer) { @@ -637,11 +637,11 @@ RenderedTarget.prototype.keepInFence = function (newX, newY, optFence) { /** * Make a clone, copying any run-time properties. * If we've hit the global clone limit, returns null. - * @return {!RenderedTarget} New clone. + * @return {RenderedTarget} New clone. */ RenderedTarget.prototype.makeClone = function () { if (!this.runtime.clonesAvailable() || this.isStage) { - return; // Hit max clone limit, or this is the stage. + return null; // Hit max clone limit, or this is the stage. } this.runtime.changeCloneCounter(1); var newClone = this.sprite.createClone(); From 1cb5883bf1672f0719998411ee2b648965545226 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 22 Feb 2017 13:55:54 -0800 Subject: [PATCH 1507/1971] Merge pull request #446 from magmaboat/hasownproperty Perform hasOwnProperty validation --- packages/scratch-vm/src/engine/runtime.js | 3 +++ packages/scratch-vm/src/sprites/rendered-target.js | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index d9e59d36ac..a060d63a70 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -441,6 +441,7 @@ Runtime.prototype.startHats = function (requestedHatOpcode, var newThreads = []; for (var opts in optMatchFields) { + if (!optMatchFields.hasOwnProperty(opts)) continue; optMatchFields[opts] = optMatchFields[opts].toUpperCase(); } @@ -463,6 +464,7 @@ Runtime.prototype.startHats = function (requestedHatOpcode, if (Object.keys(hatFields).length === 0) { var hatInputs = target.blocks.getInputs(topBlockId); for (var input in hatInputs) { + if (!hatInputs.hasOwnProperty(input)) continue; var id = hatInputs[input].block; var fields = target.blocks.getFields(id); hatFields = Object.assign(fields, hatFields); @@ -592,6 +594,7 @@ Runtime.prototype.stopAll = function () { Runtime.prototype._step = function () { // Find all edge-activated hats, and add them to threads to be evaluated. for (var hatType in this._hats) { + if (!this._hats.hasOwnProperty(hatType)) continue; var hat = this._hats[hatType]; if (hat.edgeActivated) { this.startHats(hatType); diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 0a0a115f97..fe8193ee28 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -315,6 +315,7 @@ RenderedTarget.prototype.setEffect = function (effectName, value) { */ RenderedTarget.prototype.clearEffects = function () { for (var effectName in this.effects) { + if (!this.effects.hasOwnProperty(effectName)) continue; this.effects[effectName] = 0; } if (this.renderer) { @@ -434,8 +435,9 @@ RenderedTarget.prototype.updateAllDrawableProperties = function () { costume.rotationCenterY / costume.bitmapResolution ] }; - for (var effectID in this.effects) { - props[effectID] = this.effects[effectID]; + for (var effectName in this.effects) { + if (!this.effects.hasOwnProperty(effectName)) continue; + props[effectName] = this.effects[effectName]; } this.renderer.updateDrawableProperties(this.drawableID, props); if (this.visible) { From 742aed332c171d24c2e7a6930879f6422b676ab5 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 24 Feb 2017 11:27:11 -0500 Subject: [PATCH 1508/1971] Merge pull request #459 from griffpatch/feature/FencingAgain Feature fencing again --- packages/scratch-vm/src/sprites/rendered-target.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index fe8193ee28..9c64b9aa07 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -167,15 +167,20 @@ RenderedTarget.prototype.setXY = function (x, y) { } var oldX = this.x; var oldY = this.y; - this.x = x; - this.y = y; if (this.renderer) { + var position = this.renderer.getFencedPositionOfDrawable(this.drawableID, [x, y]); + this.x = position[0]; + this.y = position[1]; + this.renderer.updateDrawableProperties(this.drawableID, { - position: [this.x, this.y] + position: position }); if (this.visible) { this.runtime.requestRedraw(); } + } else { + this.x = x; + this.y = y; } this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY); this.runtime.spriteInfoReport(this); From e179934201a9f90e0189e0b4880d1b5814815ca8 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 27 Feb 2017 14:14:11 -0500 Subject: [PATCH 1509/1971] Merge pull request #461 from rschamp/filter-toolbox Provide method for filtering toolbox XML --- packages/scratch-vm/src/virtual-machine.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 003c80e9f9..1f3bdfa1f4 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -1,6 +1,7 @@ var EventEmitter = require('events'); var util = require('util'); +var filterToolbox = require('./util/filter-toolbox'); var Runtime = require('./engine/runtime'); var sb2import = require('./import/sb2import'); @@ -336,4 +337,17 @@ VirtualMachine.prototype.postSpriteInfo = function (data) { this.editingTarget.postSpriteInfo(data); }; + +/** + * Filter Blockly toolbox XML and return a copy which only contains blocks with + * existent opcodes. Categories with no valid children will be removed. + * @param {HTMLElement} toolbox Blockly toolbox XML node + * @returns {HTMLElement} filtered toolbox XML node + */ +VirtualMachine.prototype.filterToolbox = function (toolbox) { + var opcodes = Object.keys(this.runtime._primitives) + .concat(Object.keys(this.runtime._hats)); + return filterToolbox(toolbox, opcodes); +}; + module.exports = VirtualMachine; From c261570fdf1c0ca6279374218a5af7a0f703a886 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 2 Mar 2017 11:21:01 -0500 Subject: [PATCH 1510/1971] Default bitmapResolution to 1 if not provided in costume metadata (#485) * Default bitmapResolution to 1 if not provided in costume metadata. Resolves GH-484 --- packages/scratch-vm/src/sprites/rendered-target.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 9c64b9aa07..d934f72eb1 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -428,16 +428,17 @@ RenderedTarget.prototype.updateAllDrawableProperties = function () { if (this.renderer) { var renderedDirectionScale = this._getRenderedDirectionAndScale(); var costume = this.sprite.costumes[this.currentCostume]; + var bitmapResolution = costume.bitmapResolution || 1; var props = { position: [this.x, this.y], direction: renderedDirectionScale.direction, scale: renderedDirectionScale.scale, visible: this.visible, skin: costume.skin, - costumeResolution: costume.bitmapResolution, + costumeResolution: bitmapResolution, rotationCenter: [ - costume.rotationCenterX / costume.bitmapResolution, - costume.rotationCenterY / costume.bitmapResolution + costume.rotationCenterX / bitmapResolution, + costume.rotationCenterY / bitmapResolution ] }; for (var effectName in this.effects) { From 2c47ab527103226658baecce66cfe8267c94b1ab Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 2 Mar 2017 15:56:12 -0500 Subject: [PATCH 1511/1971] Merge pull request #487 from rschamp/draggability Add draggable property to sprites --- .../scratch-vm/src/sprites/rendered-target.js | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index d934f72eb1..e4585dd7e5 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -106,6 +106,12 @@ RenderedTarget.prototype.y = 0; */ RenderedTarget.prototype.direction = 90; +/** + * Whether the rendered target is draggable on the stage + * @type {boolean} + */ +RenderedTarget.prototype.draggable = false; + /** * Whether the rendered target is currently visible. * @type {boolean} @@ -229,6 +235,16 @@ RenderedTarget.prototype.setDirection = function (direction) { this.runtime.spriteInfoReport(this); }; +/** + * Set draggability; i.e., whether it's able to be dragged in the player + * @param {!boolean} draggable True if should be draggable. + */ +RenderedTarget.prototype.setDraggable = function (draggable) { + if (this.isStage) return; + this.draggable = !!draggable; + this.runtime.spriteInfoReport(this); +}; + /** * Set a say bubble. * @param {?string} type Type of say bubble: "say", "think", or null. @@ -432,6 +448,7 @@ RenderedTarget.prototype.updateAllDrawableProperties = function () { var props = { position: [this.x, this.y], direction: renderedDirectionScale.direction, + draggable: this.draggable, scale: renderedDirectionScale.scale, visible: this.visible, skin: costume.skin, @@ -657,6 +674,7 @@ RenderedTarget.prototype.makeClone = function () { newClone.x = this.x; newClone.y = this.y; newClone.direction = this.direction; + newClone.draggable = this.draggable; newClone.visible = this.visible; newClone.size = this.size; newClone.currentCostume = this.currentCostume; @@ -704,6 +722,9 @@ RenderedTarget.prototype.postSpriteInfo = function (data) { if (data.hasOwnProperty('direction')) { this.setDirection(data.direction); } + if (data.hasOwnProperty('draggable')) { + this.setDraggable(data.draggable); + } if (data.hasOwnProperty('rotationStyle')) { this.setRotationStyle(data.rotationStyle); } @@ -724,6 +745,7 @@ RenderedTarget.prototype.toJSON = function () { x: this.x, y: this.y, direction: this.direction, + draggable: this.draggable, costume: this.getCurrentCostume(), costumeCount: this.getCostumes().length, visible: this.visible, From deb9878957c3fb9537da26631d5ebf5b0e4b413d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 3 Mar 2017 09:50:34 -0500 Subject: [PATCH 1512/1971] Merge pull request #490 from rschamp/dnd Add methods for dragging and dropping sprites --- packages/scratch-vm/src/engine/runtime.js | 12 +++++++ .../scratch-vm/src/sprites/rendered-target.js | 34 ++++++++++++++---- packages/scratch-vm/src/virtual-machine.js | 35 +++++++++++++++++++ 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index a060d63a70..130c4ae958 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -793,6 +793,18 @@ Runtime.prototype.getSpriteTargetByName = function (spriteName) { } }; +/** + * Get a target by its drawable id. + * @param {number} drawableID drawable id of target to find + * @return {?Target} The target, if found + */ +Runtime.prototype.getTargetByDrawableId = function (drawableID) { + for (var i = 0; i < this.targets.length; i++) { + var target = this.targets[i]; + if (target.drawableID === drawableID) return target; + } +}; + /** * Update the clone counter to track how many clones are created. * @param {number} changeAmount How many clones have been created/destroyed. diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index e4585dd7e5..adf9457398 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -33,6 +33,13 @@ var RenderedTarget = function (sprite, runtime) { */ this.drawableID = null; + /** + * Drag state of this rendered target. If true, x/y position can't be + * changed by blocks. + * @type {boolean} + */ + this.dragging = false; + /** * Map of current graphic effect values. * @type {!Object.} @@ -166,11 +173,11 @@ RenderedTarget.prototype.rotationStyle = ( * Set the X and Y coordinates. * @param {!number} x New X coordinate, in Scratch coordinates. * @param {!number} y New Y coordinate, in Scratch coordinates. + * @param {?boolean} force Force setting X/Y, in case of dragging */ -RenderedTarget.prototype.setXY = function (x, y) { - if (this.isStage) { - return; - } +RenderedTarget.prototype.setXY = function (x, y, force) { + if (this.isStage) return; + if (this.dragging && !force) return; var oldX = this.x; var oldY = this.y; if (this.renderer) { @@ -713,11 +720,12 @@ RenderedTarget.prototype.onStopAll = function () { * @param {object} data An object with sprite info data to set. */ RenderedTarget.prototype.postSpriteInfo = function (data) { + var force = data.hasOwnProperty('force') ? data.force : null; if (data.hasOwnProperty('x')) { - this.setXY(data.x, this.y); + this.setXY(data.x, this.y, force); } if (data.hasOwnProperty('y')) { - this.setXY(this.x, data.y); + this.setXY(this.x, data.y, force); } if (data.hasOwnProperty('direction')) { this.setDirection(data.direction); @@ -733,6 +741,20 @@ RenderedTarget.prototype.postSpriteInfo = function (data) { } }; +/** + * Put the sprite into the drag state. While in effect, setXY must be forced + */ +RenderedTarget.prototype.startDrag = function () { + this.dragging = true; +}; + +/** + * Remove the sprite from the drag state. + */ +RenderedTarget.prototype.stopDrag = function () { + this.dragging = false; +}; + /** * Serialize sprite info, used when emitting events about the sprite * @returns {object} sprite data as a simple object diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 1f3bdfa1f4..9d8477c451 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -329,6 +329,41 @@ VirtualMachine.prototype.emitWorkspaceUpdate = function () { }); }; +/** + * Get a target id for a drawable id. Useful for interacting with the renderer + * @param {int} drawableId The drawable id to request the target id for + * @returns {?string} The target id, if found. Will also be null if the target found is the stage. + */ +VirtualMachine.prototype.getTargetIdForDrawableId = function (drawableId) { + var target = this.runtime.getTargetByDrawableId(drawableId); + if (target.hasOwnProperty('id') && target.hasOwnProperty('isStage') && !target.isStage) { + return target.id; + } + return null; +}; + +/** + * Put a target into a "drag" state, during which its X/Y positions will be unaffected + * by blocks. + * @param {string} targetId The id for the target to put into a drag state + */ +VirtualMachine.prototype.startDrag = function (targetId) { + var target = this.runtime.getTargetById(targetId); + if (target) { + target.startDrag(); + this.setEditingTarget(target.id); + } +}; + +/** + * Remove a target from a drag state, so blocks may begin affecting X/Y position again + * @param {string} targetId The id for the target to remove from the drag state + */ +VirtualMachine.prototype.stopDrag = function (targetId) { + var target = this.runtime.getTargetById(targetId); + if (target) target.stopDrag(); +}; + /** * Post/edit sprite info for the current editing target. * @param {object} data An object with sprite info data to set. From 70be73ee6b5c6e8bfca39d01094fae56a612b584 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 7 Mar 2017 13:56:32 -0500 Subject: [PATCH 1513/1971] Merge pull request #494 from LLK/stop-all-gfx-reset Stop all gfx reset --- packages/scratch-vm/src/sprites/rendered-target.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index adf9457398..e062df53f0 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -706,9 +706,10 @@ RenderedTarget.prototype.onGreenFlag = function () { /** * Called when the project receives a "stop all" - * Stop all sounds + * Stop all sounds and clear graphic effects. */ RenderedTarget.prototype.onStopAll = function () { + this.clearEffects(); if (this.audioPlayer) { this.audioPlayer.stopAllSounds(); this.audioPlayer.clearEffects(); From 7ea6146445432c921794bf21e49f776e57fea053 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 8 Mar 2017 12:13:10 -0500 Subject: [PATCH 1514/1971] Merge pull request #495 from paulkaplan/fix-target-id Add check to make sure `target` exists --- packages/scratch-vm/src/virtual-machine.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 9d8477c451..b7b56af2ce 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -336,7 +336,7 @@ VirtualMachine.prototype.emitWorkspaceUpdate = function () { */ VirtualMachine.prototype.getTargetIdForDrawableId = function (drawableId) { var target = this.runtime.getTargetByDrawableId(drawableId); - if (target.hasOwnProperty('id') && target.hasOwnProperty('isStage') && !target.isStage) { + if (target && target.hasOwnProperty('id') && target.hasOwnProperty('isStage') && !target.isStage) { return target.id; } return null; From 019b344228a334a9dc82c6ccd3ca3296eb9e18e6 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 9 Mar 2017 16:41:06 -0500 Subject: [PATCH 1515/1971] Remove filter toolbox utility (#496) * Revert "Merge pull request #486 from rschamp/fix-filter-tests" This reverts commit ba00db897fe328f34e58ce39975c218059a68a94, reversing changes made to 739c5deb63ab749982c5648a056f0ba7b42c7537. * Revert "Show Categories that use custom code to load (variables, procedures) (#483)" This reverts commit 739c5deb63ab749982c5648a056f0ba7b42c7537. * Revert "Merge pull request #461 from rschamp/filter-toolbox" This reverts commit 343b5bfe8ecc6e497688dc33dd489ab6f8d230f6, reversing changes made to 370f2c6a47733dc3f54fac5a950be291e7042690. --- packages/scratch-vm/src/virtual-machine.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index b7b56af2ce..1431640fb5 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -1,7 +1,6 @@ var EventEmitter = require('events'); var util = require('util'); -var filterToolbox = require('./util/filter-toolbox'); var Runtime = require('./engine/runtime'); var sb2import = require('./import/sb2import'); @@ -372,17 +371,4 @@ VirtualMachine.prototype.postSpriteInfo = function (data) { this.editingTarget.postSpriteInfo(data); }; - -/** - * Filter Blockly toolbox XML and return a copy which only contains blocks with - * existent opcodes. Categories with no valid children will be removed. - * @param {HTMLElement} toolbox Blockly toolbox XML node - * @returns {HTMLElement} filtered toolbox XML node - */ -VirtualMachine.prototype.filterToolbox = function (toolbox) { - var opcodes = Object.keys(this.runtime._primitives) - .concat(Object.keys(this.runtime._hats)); - return filterToolbox(toolbox, opcodes); -}; - module.exports = VirtualMachine; From b959e95a76187de41edda301e1b6bc65d1a3d86f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 21 Mar 2017 07:30:41 -0400 Subject: [PATCH 1516/1971] Merge pull request #504 from paulkaplan/validate-sprite-names Don't rename sprites to empty string names --- packages/scratch-vm/src/virtual-machine.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 1431640fb5..576e3a78ac 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -3,6 +3,9 @@ var util = require('util'); var Runtime = require('./engine/runtime'); var sb2import = require('./import/sb2import'); +var StringUtil = require('./util/string-util'); + +var RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_']; /** * Handles connections between blocks, stage, and extensions. @@ -204,7 +207,15 @@ VirtualMachine.prototype.renameSprite = function (targetId, newName) { if (!sprite) { throw new Error('No sprite associated with this target.'); } - sprite.name = newName; + if (newName && RESERVED_NAMES.indexOf(newName) === -1) { + var names = this.runtime.targets.filter(function (runtimeTarget) { + return runtimeTarget.isSprite(); + }).map(function (runtimeTarget) { + return runtimeTarget.sprite.name; + }); + + sprite.name = StringUtil.unusedName(newName, names); + } this.emitTargetsUpdate(); } else { throw new Error('No target with the provided id.'); From 8322e53bdc9b2653351fa8654e87d755a3b692ad Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Thu, 23 Mar 2017 22:10:25 -0700 Subject: [PATCH 1517/1971] Merge pull request #498 from cwillisf/use-scratch-storage Load projects & costumes through scratch-storage --- packages/scratch-vm/src/engine/runtime.js | 16 ++++++-- .../scratch-vm/src/sprites/rendered-target.js | 4 +- packages/scratch-vm/src/virtual-machine.js | 38 ++++++++++++++++--- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 130c4ae958..1019823358 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -311,6 +311,14 @@ Runtime.prototype.clearEdgeActivatedValues = function () { this._edgeActivatedHatValues = {}; }; +/** + * Attach the audio engine + * @param {!AudioEngine} audioEngine The audio engine to attach + */ +Runtime.prototype.attachAudioEngine = function (audioEngine) { + this.audioEngine = audioEngine; +}; + /** * Attach the renderer * @param {!RenderWebGL} renderer The renderer to attach @@ -320,11 +328,11 @@ Runtime.prototype.attachRenderer = function (renderer) { }; /** - * Attach the audio engine - * @param {!AudioEngine} audioEngine The audio engine to attach + * Attach the storage module + * @param {!ScratchStorage} storage The storage module to attach */ -Runtime.prototype.attachAudioEngine = function (audioEngine) { - this.audioEngine = audioEngine; +Runtime.prototype.attachStorage = function (storage) { + this.storage = storage; }; // ----------------------------------------------------------------------------- diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index e062df53f0..24d8f1a7dd 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -367,7 +367,7 @@ RenderedTarget.prototype.setCostume = function (index) { if (this.renderer) { var costume = this.sprite.costumes[this.currentCostume]; var drawableProperties = { - skin: costume.skin, + skinId: costume.skinId, costumeResolution: costume.bitmapResolution }; if ( @@ -458,7 +458,7 @@ RenderedTarget.prototype.updateAllDrawableProperties = function () { draggable: this.draggable, scale: renderedDirectionScale.scale, visible: this.visible, - skin: costume.skin, + skinId: costume.skinId, costumeResolution: bitmapResolution, rotationCenter: [ costume.rotationCenterX / bitmapResolution, diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 576e3a78ac..80eae4ee0b 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -1,12 +1,16 @@ var EventEmitter = require('events'); var util = require('util'); +var log = require('./util/log'); var Runtime = require('./engine/runtime'); +var ScratchStorage = require('scratch-storage'); var sb2import = require('./import/sb2import'); var StringUtil = require('./util/string-util'); var RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_']; +var AssetType = ScratchStorage.AssetType; + /** * Handles connections between blocks, stage, and extensions. * @constructor @@ -156,9 +160,25 @@ VirtualMachine.prototype.loadProject = function (json) { this.runtime.setEditingTarget(this.editingTarget); }; +/** + * Load a project from the Scratch web site, by ID. + * @param {string} id - the ID of the project to download, as a string. + */ +VirtualMachine.prototype.downloadProjectId = function (id) { + if (!this.runtime.storage) { + log.error('No storage module present; cannot load project: ', id); + return; + } + var vm = this; + var promise = this.runtime.storage.load(AssetType.Project, id); + promise.then(function (projectAsset) { + vm.loadProject(projectAsset.decodeText()); + }); +}; + /** * Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format. - * @param {?string} json JSON string representing the sprite. + * @param {string} json JSON string representing the sprite. */ VirtualMachine.prototype.addSprite2 = function (json) { // Select new sprite. @@ -253,6 +273,14 @@ VirtualMachine.prototype.deleteSprite = function (targetId) { } }; +/** + * Set the audio engine for the VM/runtime + * @param {!AudioEngine} audioEngine The audio engine to attach + */ +VirtualMachine.prototype.attachAudioEngine = function (audioEngine) { + this.runtime.attachAudioEngine(audioEngine); +}; + /** * Set the renderer for the VM/runtime * @param {!RenderWebGL} renderer The renderer to attach @@ -262,11 +290,11 @@ VirtualMachine.prototype.attachRenderer = function (renderer) { }; /** - * Set the audio engine for the VM/runtime - * @param {!AudioEngine} audioEngine The audio engine to attach + * Set the storage module for the VM/runtime + * @param {!ScratchStorage} storage The storage module to attach */ -VirtualMachine.prototype.attachAudioEngine = function (audioEngine) { - this.runtime.attachAudioEngine(audioEngine); +VirtualMachine.prototype.attachStorage = function (storage) { + this.runtime.attachStorage(storage); }; /** From f9549dc8df7d2a3b032bbdafbeb2dc046b961c09 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 31 Mar 2017 11:10:14 -0400 Subject: [PATCH 1518/1971] Merge pull request #512 from ericrosenbaum/feature/use-scratch-storage Use Scratch Storage to load sounds and Audio Engine to decode and register them --- packages/scratch-vm/src/sprites/rendered-target.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 24d8f1a7dd..98172f67c4 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -75,9 +75,6 @@ RenderedTarget.prototype.initDrawable = function () { */ this.audioPlayer = null; if (this.runtime && this.runtime.audioEngine) { - if (this.isOriginal) { - this.runtime.audioEngine.loadSounds(this.sprite.sounds); - } this.audioPlayer = this.runtime.audioEngine.createPlayer(); } }; From 3cd94aedb130e668eb8bd57d68633ddf0d70e33b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 31 Mar 2017 12:52:06 -0400 Subject: [PATCH 1519/1971] Merge pull request #514 from paulkaplan/fix-costume-backdrop-loading Use costume loading from importer in public costume and backdrop loader --- packages/scratch-vm/src/virtual-machine.js | 36 +++++++++++++++------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 80eae4ee0b..296cd45cf7 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -7,6 +7,8 @@ var ScratchStorage = require('scratch-storage'); var sb2import = require('./import/sb2import'); var StringUtil = require('./util/string-util'); +var loadCostume = require('./import/load-costume.js'); + var RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_']; var AssetType = ScratchStorage.AssetType; @@ -191,25 +193,37 @@ VirtualMachine.prototype.addSprite2 = function (json) { /** * Add a costume to the current editing target. + * @param {string} md5ext - the MD5 and extension of the costume to be loaded. * @param {!object} costumeObject Object representing the costume. + * @property {int} skinId - the ID of the costume's render skin, once installed. + * @property {number} rotationCenterX - the X component of the costume's origin. + * @property {number} rotationCenterY - the Y component of the costume's origin. + * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume. */ -VirtualMachine.prototype.addCostume = function (costumeObject) { - this.editingTarget.sprite.costumes.push(costumeObject); - // Switch to the costume. - this.editingTarget.setCostume( - this.editingTarget.sprite.costumes.length - 1 - ); +VirtualMachine.prototype.addCostume = function (md5ext, costumeObject) { + loadCostume(md5ext, costumeObject, this.runtime).then(function () { + this.editingTarget.sprite.costumes.push(costumeObject); + this.editingTarget.setCostume( + this.editingTarget.sprite.costumes.length - 1 + ); + }.bind(this)); }; /** * Add a backdrop to the stage. + * @param {string} md5ext - the MD5 and extension of the backdrop to be loaded. * @param {!object} backdropObject Object representing the backdrop. + * @property {int} skinId - the ID of the backdrop's render skin, once installed. + * @property {number} rotationCenterX - the X component of the backdrop's origin. + * @property {number} rotationCenterY - the Y component of the backdrop's origin. + * @property {number} [bitmapResolution] - the resolution scale for a bitmap backdrop. */ -VirtualMachine.prototype.addBackdrop = function (backdropObject) { - var stage = this.runtime.getTargetForStage(); - stage.sprite.costumes.push(backdropObject); - // Switch to the backdrop. - stage.setCostume(stage.sprite.costumes.length - 1); +VirtualMachine.prototype.addBackdrop = function (md5ext, backdropObject) { + loadCostume(md5ext, backdropObject, this.runtime).then(function () { + var stage = this.runtime.getTargetForStage(); + stage.sprite.costumes.push(backdropObject); + stage.setCostume(stage.sprite.costumes.length - 1); + }.bind(this)); }; /** From 6bf06ea01c67d93f0824e279e1d9a6c20dc144de Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Mon, 3 Apr 2017 13:00:05 -0400 Subject: [PATCH 1520/1971] Merge pull request #462 from griffpatch/optimise/reduceGetBlockCalls Optimise reducegetblockcalls --- packages/scratch-vm/src/engine/runtime.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 1019823358..65f3c5fc74 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -455,7 +455,9 @@ Runtime.prototype.startHats = function (requestedHatOpcode, // Consider all scripts, looking for hats with opcode `requestedHatOpcode`. this.allScriptsDo(function (topBlockId, target) { - var potentialHatOpcode = target.blocks.getBlock(topBlockId).opcode; + var blocks = target.blocks; + var block = blocks.getBlock(topBlockId); + var potentialHatOpcode = block.opcode; if (potentialHatOpcode !== requestedHatOpcode) { // Not the right hat. return; @@ -466,15 +468,16 @@ Runtime.prototype.startHats = function (requestedHatOpcode, // This needs to happen before the block is evaluated // (i.e., before the predicate can be run) because "broadcast and wait" // needs to have a precise collection of started threads. - var hatFields = target.blocks.getFields(topBlockId); + var hatFields = blocks.getFields(block); // If no fields are present, check inputs (horizontal blocks) if (Object.keys(hatFields).length === 0) { - var hatInputs = target.blocks.getInputs(topBlockId); + var hatInputs = blocks.getInputs(block); for (var input in hatInputs) { if (!hatInputs.hasOwnProperty(input)) continue; var id = hatInputs[input].block; - var fields = target.blocks.getFields(id); + var inpBlock = blocks.getBlock(id); + var fields = blocks.getFields(inpBlock); hatFields = Object.assign(fields, hatFields); } } From ac4cff988f86bbb1145330508037bb282cfd9a12 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 4 Apr 2017 09:05:45 -0400 Subject: [PATCH 1521/1971] Merge pull request #516 from paulkaplan/add-sound-public-api Expose VM#addSound publicly --- packages/scratch-vm/src/virtual-machine.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 296cd45cf7..b85705e379 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -8,6 +8,7 @@ var sb2import = require('./import/sb2import'); var StringUtil = require('./util/string-util'); var loadCostume = require('./import/load-costume.js'); +var loadSound = require('./import/load-sound.js'); var RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_']; @@ -209,6 +210,18 @@ VirtualMachine.prototype.addCostume = function (md5ext, costumeObject) { }.bind(this)); }; +/** + * Add a sound to the current editing target. + * @param {!object} soundObject Object representing the costume. + * @returns {?Promise} - a promise that resolves when the sound has been decoded and added + */ +VirtualMachine.prototype.addSound = function (soundObject) { + return loadSound(soundObject, this.runtime).then(function () { + this.editingTarget.sprite.sounds.push(soundObject); + this.emitTargetsUpdate(); + }.bind(this)); +}; + /** * Add a backdrop to the stage. * @param {string} md5ext - the MD5 and extension of the backdrop to be loaded. From 0f4ef5114a039315d73e82a907374afee265731f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 6 Apr 2017 11:45:50 -0400 Subject: [PATCH 1522/1971] Merge pull request #526 from paulkaplan/report-sound-costume-data Report sound costume data --- packages/scratch-vm/src/sprites/rendered-target.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 98172f67c4..a032b1ece6 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -440,6 +440,14 @@ RenderedTarget.prototype.getCostumes = function () { return this.sprite.costumes; }; +/** + * Get full sound list + * @return {object[]} list of sounds + */ +RenderedTarget.prototype.getSounds = function () { + return this.sprite.sounds; +}; + /** * Update all drawable properties for this rendered target. * Use when a batch has changed, e.g., when the drawable is first created. @@ -767,6 +775,8 @@ RenderedTarget.prototype.toJSON = function () { direction: this.direction, draggable: this.draggable, costume: this.getCurrentCostume(), + costumes: this.getCostumes(), + sounds: this.getSounds(), costumeCount: this.getCostumes().length, visible: this.visible, rotationStyle: this.rotationStyle From ecd5562e5f67b58611bbff7b6293fa80e92b924b Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Thu, 6 Apr 2017 14:55:08 -0700 Subject: [PATCH 1523/1971] Merge pull request #522 from presight/develop Fix so that size is set in float to ensure backward compatibility with Scratch 2.0 --- packages/scratch-vm/src/sprites/rendered-target.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index a032b1ece6..29154af344 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -298,14 +298,14 @@ RenderedTarget.prototype.setSize = function (size) { // Clamp to scales relative to costume and stage size. // See original ScratchSprite.as:setSize. var costumeSize = this.renderer.getSkinSize(this.drawableID); - var origW = Math.round(costumeSize[0]); - var origH = Math.round(costumeSize[1]); + var origW = costumeSize[0]; + var origH = costumeSize[1]; var minScale = Math.min(1, Math.max(5 / origW, 5 / origH)); var maxScale = Math.min( (1.5 * this.runtime.constructor.STAGE_WIDTH) / origW, (1.5 * this.runtime.constructor.STAGE_HEIGHT) / origH ); - this.size = Math.round(MathUtil.clamp(size / 100, minScale, maxScale) * 100); + this.size = MathUtil.clamp(size / 100, minScale, maxScale) * 100; var renderedDirectionScale = this._getRenderedDirectionAndScale(); this.renderer.updateDrawableProperties(this.drawableID, { direction: renderedDirectionScale.direction, @@ -772,6 +772,7 @@ RenderedTarget.prototype.toJSON = function () { isStage: this.isStage, x: this.x, y: this.y, + size: this.size, direction: this.direction, draggable: this.draggable, costume: this.getCurrentCostume(), From 013d897edee4d56ef4e9992318579697010110bd Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 19 Apr 2017 11:40:30 -0400 Subject: [PATCH 1524/1971] Merge pull request #535 from paulkaplan/isolate-import Separate downloading from installation of sprite info --- packages/scratch-vm/src/virtual-machine.js | 38 ++++++++++++++-------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index b85705e379..ca0edc9fde 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -150,17 +150,26 @@ VirtualMachine.prototype.postIOData = function (device, data) { /** * Load a project from a Scratch 2.0 JSON representation. * @param {?string} json JSON string representing the project. + * @return {!Promise} Promise that resolves after targets are installed. */ VirtualMachine.prototype.loadProject = function (json) { - this.clear(); // @todo: Handle other formats, e.g., Scratch 1.4, Scratch 3.0. - sb2import(json, this.runtime); - // Select the first target for editing, e.g., the first sprite. - this.editingTarget = this.runtime.targets[1]; - // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); + return sb2import(json, this.runtime).then(function (targets) { + this.clear(); + for (var n = 0; n < targets.length; n++) { + if (targets[n] !== null) { + this.runtime.targets.push(targets[n]); + targets[n].updateAllDrawableProperties(); + } + } + // Select the first target for editing, e.g., the first sprite. + this.editingTarget = this.runtime.targets[1]; + + // Update the VM user's knowledge of targets and blocks on the workspace. + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); + }.bind(this)); }; /** @@ -185,11 +194,14 @@ VirtualMachine.prototype.downloadProjectId = function (id) { */ VirtualMachine.prototype.addSprite2 = function (json) { // Select new sprite. - this.editingTarget = sb2import(json, this.runtime, true); - // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); + sb2import(json, this.runtime, true).then(function (targets) { + this.runtime.targets.push(targets[0]); + this.editingTarget = targets[0]; + // Update the VM user's knowledge of targets and blocks on the workspace. + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); + }.bind(this)); }; /** From 10fb22728536a16b0ec84c4097ad22016e0fc72e Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 20 Apr 2017 17:06:29 -0400 Subject: [PATCH 1525/1971] Merge pull request #534 from rschamp/es6 Use ES6, convert to ES6-style classes --- packages/scratch-vm/src/engine/runtime.js | 1455 +++++++++-------- .../scratch-vm/src/sprites/rendered-target.js | 1409 ++++++++-------- packages/scratch-vm/src/virtual-machine.js | 781 +++++---- 3 files changed, 1831 insertions(+), 1814 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 65f3c5fc74..f57a0e9f96 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1,15 +1,14 @@ -var EventEmitter = require('events'); -var Sequencer = require('./sequencer'); -var Blocks = require('./blocks'); -var Thread = require('./thread'); -var util = require('util'); +const EventEmitter = require('events'); +const Sequencer = require('./sequencer'); +const Blocks = require('./blocks'); +const Thread = require('./thread'); // Virtual I/O devices. -var Clock = require('../io/clock'); -var Keyboard = require('../io/keyboard'); -var Mouse = require('../io/mouse'); +const Clock = require('../io/clock'); +const Keyboard = require('../io/keyboard'); +const Mouse = require('../io/mouse'); -var defaultBlockPackages = { +const defaultBlockPackages = { scratch3_control: require('../blocks/scratch3_control'), scratch3_event: require('../blocks/scratch3_event'), scratch3_looks: require('../blocks/scratch3_looks'), @@ -26,845 +25,867 @@ var defaultBlockPackages = { * Manages targets, scripts, and the sequencer. * @constructor */ -var Runtime = function () { - // Bind event emitter - EventEmitter.call(this); +class Runtime extends EventEmitter { + constructor () { + super(); + + /** + * Target management and storage. + * @type {Array.} + */ + this.targets = []; + + /** + * A list of threads that are currently running in the VM. + * Threads are added when execution starts and pruned when execution ends. + * @type {Array.} + */ + this.threads = []; + + /** @type {!Sequencer} */ + this.sequencer = new Sequencer(this); + + /** + * Storage container for flyout blocks. + * These will execute on `_editingTarget.` + * @type {!Blocks} + */ + this.flyoutBlocks = new Blocks(); + + /** + * Currently known editing target for the VM. + * @type {?Target} + */ + this._editingTarget = null; + + /** + * Map to look up a block primitive's implementation function by its opcode. + * This is a two-step lookup: package name first, then primitive name. + * @type {Object.} + */ + this._primitives = {}; + + /** + * Map to look up hat blocks' metadata. + * Keys are opcode for hat, values are metadata objects. + * @type {Object.} + */ + this._hats = {}; + + /** + * Currently known values for edge-activated hats. + * Keys are block ID for the hat; values are the currently known values. + * @type {Object.} + */ + this._edgeActivatedHatValues = {}; + + /** + * A list of script block IDs that were glowing during the previous frame. + * @type {!Array.} + */ + this._scriptGlowsPreviousFrame = []; + + /** + * Number of threads running during the previous frame + * @type {number} + */ + this._threadCount = 0; + + /** + * Currently known number of clones, used to enforce clone limit. + * @type {number} + */ + this._cloneCounter = 0; + + /** + * Whether the project is in "turbo mode." + * @type {Boolean} + */ + this.turboMode = false; + + /** + * Whether the project is in "compatibility mode" (30 TPS). + * @type {Boolean} + */ + this.compatibilityMode = false; + + /** + * A reference to the current runtime stepping interval, set + * by a `setInterval`. + * @type {!number} + */ + this._steppingInterval = null; + + /** + * Current length of a step. + * Changes as mode switches, and used by the sequencer to calculate + * WORK_TIME. + * @type {!number} + */ + this.currentStepTime = null; + + /** + * Whether any primitive has requested a redraw. + * Affects whether `Sequencer.stepThreads` will yield + * after stepping each thread. + * Reset on every frame. + * @type {boolean} + */ + this.redrawRequested = false; + + // Register all given block packages. + this._registerBlockPackages(); + + // Register and initialize "IO devices", containers for processing + // I/O related data. + /** @type {Object.} */ + this.ioDevices = { + clock: new Clock(), + keyboard: new Keyboard(this), + mouse: new Mouse(this) + }; + } /** - * Target management and storage. - * @type {Array.} + * Width of the stage, in pixels. + * @const {number} */ - this.targets = []; + static get STAGE_WIDTH () { + return 480; + } /** - * A list of threads that are currently running in the VM. - * Threads are added when execution starts and pruned when execution ends. - * @type {Array.} + * Height of the stage, in pixels. + * @const {number} */ - this.threads = []; - - /** @type {!Sequencer} */ - this.sequencer = new Sequencer(this); + static get STAGE_HEIGHT () { + return 360; + } /** - * Storage container for flyout blocks. - * These will execute on `_editingTarget.` - * @type {!Blocks} + * Event name for glowing a script. + * @const {string} */ - this.flyoutBlocks = new Blocks(); + static get SCRIPT_GLOW_ON () { + return 'SCRIPT_GLOW_ON'; + } /** - * Currently known editing target for the VM. - * @type {?Target} + * Event name for unglowing a script. + * @const {string} */ - this._editingTarget = null; + static get SCRIPT_GLOW_OFF () { + return 'SCRIPT_GLOW_OFF'; + } /** - * Map to look up a block primitive's implementation function by its opcode. - * This is a two-step lookup: package name first, then primitive name. - * @type {Object.} + * Event name for glowing a block. + * @const {string} */ - this._primitives = {}; + static get BLOCK_GLOW_ON () { + return 'BLOCK_GLOW_ON'; + } /** - * Map to look up hat blocks' metadata. - * Keys are opcode for hat, values are metadata objects. - * @type {Object.} + * Event name for unglowing a block. + * @const {string} */ - this._hats = {}; + static get BLOCK_GLOW_OFF () { + return 'BLOCK_GLOW_OFF'; + } /** - * Currently known values for edge-activated hats. - * Keys are block ID for the hat; values are the currently known values. - * @type {Object.} + * Event name for glowing the green flag + * @const {string} */ - this._edgeActivatedHatValues = {}; + static get PROJECT_RUN_START () { + return 'PROJECT_RUN_START'; + } /** - * A list of script block IDs that were glowing during the previous frame. - * @type {!Array.} + * Event name for unglowing the green flag + * @const {string} */ - this._scriptGlowsPreviousFrame = []; + static get PROJECT_RUN_STOP () { + return 'PROJECT_RUN_STOP'; + } /** - * Number of threads running during the previous frame - * @type {number} + * Event name for visual value report. + * @const {string} */ - this._threadCount = 0; + static get VISUAL_REPORT () { + return 'VISUAL_REPORT'; + } /** - * Currently known number of clones, used to enforce clone limit. - * @type {number} + * Event name for sprite info report. + * @const {string} */ - this._cloneCounter = 0; + static get SPRITE_INFO_REPORT () { + return 'SPRITE_INFO_REPORT'; + } /** - * Whether the project is in "turbo mode." - * @type {Boolean} + * How rapidly we try to step threads by default, in ms. */ - this.turboMode = false; + static get THREAD_STEP_INTERVAL () { + return 1000 / 60; + } /** - * Whether the project is in "compatibility mode" (30 TPS). - * @type {Boolean} + * In compatibility mode, how rapidly we try to step threads, in ms. */ - this.compatibilityMode = false; + static get THREAD_STEP_INTERVAL_COMPATIBILITY () { + return 1000 / 30; + } /** - * A reference to the current runtime stepping interval, set - * by a `setInterval`. - * @type {!number} + * How many clones can be created at a time. + * @const {number} */ - this._steppingInterval = null; + static get MAX_CLONES () { + return 300; + } - /** - * Current length of a step. - * Changes as mode switches, and used by the sequencer to calculate - * WORK_TIME. - * @type {!number} - */ - this.currentStepTime = null; + // ----------------------------------------------------------------------------- + // ----------------------------------------------------------------------------- /** - * Whether any primitive has requested a redraw. - * Affects whether `Sequencer.stepThreads` will yield - * after stepping each thread. - * Reset on every frame. - * @type {boolean} + * Register default block packages with this runtime. + * @todo Prefix opcodes with package name. + * @private */ - this.redrawRequested = false; - - // Register all given block packages. - this._registerBlockPackages(); - - // Register and initialize "IO devices", containers for processing - // I/O related data. - /** @type {Object.} */ - this.ioDevices = { - clock: new Clock(), - keyboard: new Keyboard(this), - mouse: new Mouse(this) - }; -}; - -/** - * Inherit from EventEmitter - */ -util.inherits(Runtime, EventEmitter); - -/** - * Width of the stage, in pixels. - * @const {number} - */ -Runtime.STAGE_WIDTH = 480; - -/** - * Height of the stage, in pixels. - * @const {number} - */ -Runtime.STAGE_HEIGHT = 360; - -/** - * Event name for glowing a script. - * @const {string} - */ -Runtime.SCRIPT_GLOW_ON = 'SCRIPT_GLOW_ON'; - -/** - * Event name for unglowing a script. - * @const {string} - */ -Runtime.SCRIPT_GLOW_OFF = 'SCRIPT_GLOW_OFF'; - -/** - * Event name for glowing a block. - * @const {string} - */ -Runtime.BLOCK_GLOW_ON = 'BLOCK_GLOW_ON'; - -/** - * Event name for unglowing a block. - * @const {string} - */ -Runtime.BLOCK_GLOW_OFF = 'BLOCK_GLOW_OFF'; - -/** - * Event name for glowing the green flag - * @const {string} - */ -Runtime.PROJECT_RUN_START = 'PROJECT_RUN_START'; - -/** - * Event name for unglowing the green flag - * @const {string} - */ -Runtime.PROJECT_RUN_STOP = 'PROJECT_RUN_STOP'; - -/** - * Event name for visual value report. - * @const {string} - */ -Runtime.VISUAL_REPORT = 'VISUAL_REPORT'; - -/** - * Event name for sprite info report. - * @const {string} - */ -Runtime.SPRITE_INFO_REPORT = 'SPRITE_INFO_REPORT'; - -/** - * How rapidly we try to step threads by default, in ms. - */ -Runtime.THREAD_STEP_INTERVAL = 1000 / 60; - -/** - * In compatibility mode, how rapidly we try to step threads, in ms. - */ -Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY = 1000 / 30; - -/** - * How many clones can be created at a time. - * @const {number} - */ -Runtime.MAX_CLONES = 300; - -// ----------------------------------------------------------------------------- -// ----------------------------------------------------------------------------- - -/** - * Register default block packages with this runtime. - * @todo Prefix opcodes with package name. - * @private - */ -Runtime.prototype._registerBlockPackages = function () { - for (var packageName in defaultBlockPackages) { - if (defaultBlockPackages.hasOwnProperty(packageName)) { - // @todo pass a different runtime depending on package privilege? - var packageObject = new (defaultBlockPackages[packageName])(this); - // Collect primitives from package. - if (packageObject.getPrimitives) { - var packagePrimitives = packageObject.getPrimitives(); - for (var op in packagePrimitives) { - if (packagePrimitives.hasOwnProperty(op)) { - this._primitives[op] = - packagePrimitives[op].bind(packageObject); + _registerBlockPackages () { + for (const packageName in defaultBlockPackages) { + if (defaultBlockPackages.hasOwnProperty(packageName)) { + // @todo pass a different runtime depending on package privilege? + const packageObject = new (defaultBlockPackages[packageName])(this); + // Collect primitives from package. + if (packageObject.getPrimitives) { + const packagePrimitives = packageObject.getPrimitives(); + for (const op in packagePrimitives) { + if (packagePrimitives.hasOwnProperty(op)) { + this._primitives[op] = + packagePrimitives[op].bind(packageObject); + } } } - } - // Collect hat metadata from package. - if (packageObject.getHats) { - var packageHats = packageObject.getHats(); - for (var hatName in packageHats) { - if (packageHats.hasOwnProperty(hatName)) { - this._hats[hatName] = packageHats[hatName]; + // Collect hat metadata from package. + if (packageObject.getHats) { + const packageHats = packageObject.getHats(); + for (const hatName in packageHats) { + if (packageHats.hasOwnProperty(hatName)) { + this._hats[hatName] = packageHats[hatName]; + } } } } } } -}; -/** - * Retrieve the function associated with the given opcode. - * @param {!string} opcode The opcode to look up. - * @return {Function} The function which implements the opcode. - */ -Runtime.prototype.getOpcodeFunction = function (opcode) { - return this._primitives[opcode]; -}; + /** + * Retrieve the function associated with the given opcode. + * @param {!string} opcode The opcode to look up. + * @return {Function} The function which implements the opcode. + */ + getOpcodeFunction (opcode) { + return this._primitives[opcode]; + } -/** - * Return whether an opcode represents a hat block. - * @param {!string} opcode The opcode to look up. - * @return {boolean} True if the op is known to be a hat. - */ -Runtime.prototype.getIsHat = function (opcode) { - return this._hats.hasOwnProperty(opcode); -}; + /** + * Return whether an opcode represents a hat block. + * @param {!string} opcode The opcode to look up. + * @return {boolean} True if the op is known to be a hat. + */ + getIsHat (opcode) { + return this._hats.hasOwnProperty(opcode); + } -/** - * Return whether an opcode represents an edge-activated hat block. - * @param {!string} opcode The opcode to look up. - * @return {boolean} True if the op is known to be a edge-activated hat. - */ -Runtime.prototype.getIsEdgeActivatedHat = function (opcode) { - return this._hats.hasOwnProperty(opcode) && - this._hats[opcode].edgeActivated; -}; + /** + * Return whether an opcode represents an edge-activated hat block. + * @param {!string} opcode The opcode to look up. + * @return {boolean} True if the op is known to be a edge-activated hat. + */ + getIsEdgeActivatedHat (opcode) { + return this._hats.hasOwnProperty(opcode) && + this._hats[opcode].edgeActivated; + } -/** - * Update an edge-activated hat block value. - * @param {!string} blockId ID of hat to store value for. - * @param {*} newValue Value to store for edge-activated hat. - * @return {*} The old value for the edge-activated hat. - */ -Runtime.prototype.updateEdgeActivatedValue = function (blockId, newValue) { - var oldValue = this._edgeActivatedHatValues[blockId]; - this._edgeActivatedHatValues[blockId] = newValue; - return oldValue; -}; + /** + * Update an edge-activated hat block value. + * @param {!string} blockId ID of hat to store value for. + * @param {*} newValue Value to store for edge-activated hat. + * @return {*} The old value for the edge-activated hat. + */ + updateEdgeActivatedValue (blockId, newValue) { + const oldValue = this._edgeActivatedHatValues[blockId]; + this._edgeActivatedHatValues[blockId] = newValue; + return oldValue; + } -/** - * Clear all edge-activaed hat values. - */ -Runtime.prototype.clearEdgeActivatedValues = function () { - this._edgeActivatedHatValues = {}; -}; + /** + * Clear all edge-activaed hat values. + */ + clearEdgeActivatedValues () { + this._edgeActivatedHatValues = {}; + } -/** - * Attach the audio engine - * @param {!AudioEngine} audioEngine The audio engine to attach - */ -Runtime.prototype.attachAudioEngine = function (audioEngine) { - this.audioEngine = audioEngine; -}; + /** + * Attach the audio engine + * @param {!AudioEngine} audioEngine The audio engine to attach + */ + attachAudioEngine (audioEngine) { + this.audioEngine = audioEngine; + } -/** - * Attach the renderer - * @param {!RenderWebGL} renderer The renderer to attach - */ -Runtime.prototype.attachRenderer = function (renderer) { - this.renderer = renderer; -}; + /** + * Attach the renderer + * @param {!RenderWebGL} renderer The renderer to attach + */ + attachRenderer (renderer) { + this.renderer = renderer; + } -/** - * Attach the storage module - * @param {!ScratchStorage} storage The storage module to attach - */ -Runtime.prototype.attachStorage = function (storage) { - this.storage = storage; -}; + /** + * Attach the storage module + * @param {!ScratchStorage} storage The storage module to attach + */ + attachStorage (storage) { + this.storage = storage; + } -// ----------------------------------------------------------------------------- -// ----------------------------------------------------------------------------- + // ----------------------------------------------------------------------------- + // ----------------------------------------------------------------------------- -/** - * Create a thread and push it to the list of threads. - * @param {!string} id ID of block that starts the stack. - * @param {!Target} target Target to run thread on. - * @return {!Thread} The newly created thread. - */ -Runtime.prototype._pushThread = function (id, target) { - var thread = new Thread(id); - thread.target = target; - thread.pushStack(id); - this.threads.push(thread); - return thread; -}; + /** + * Create a thread and push it to the list of threads. + * @param {!string} id ID of block that starts the stack. + * @param {!Target} target Target to run thread on. + * @return {!Thread} The newly created thread. + */ + _pushThread (id, target) { + const thread = new Thread(id); + thread.target = target; + thread.pushStack(id); + this.threads.push(thread); + return thread; + } -/** - * Remove a thread from the list of threads. - * @param {?Thread} thread Thread object to remove from actives - */ -Runtime.prototype._removeThread = function (thread) { - // Inform sequencer to stop executing that thread. - this.sequencer.retireThread(thread); - // Remove from the list. - var i = this.threads.indexOf(thread); - if (i > -1) { - this.threads.splice(i, 1); + /** + * Remove a thread from the list of threads. + * @param {?Thread} thread Thread object to remove from actives + */ + _removeThread (thread) { + // Inform sequencer to stop executing that thread. + this.sequencer.retireThread(thread); + // Remove from the list. + const i = this.threads.indexOf(thread); + if (i > -1) { + this.threads.splice(i, 1); + } } -}; -/** - * Restart a thread in place, maintaining its position in the list of threads. - * This is used by `startHats` to and is necessary to ensure 2.0-like execution order. - * Test project: https://scratch.mit.edu/projects/130183108/ - * @param {!Thread} thread Thread object to restart. - */ -Runtime.prototype._restartThread = function (thread) { - var newThread = new Thread(thread.topBlock); - newThread.target = thread.target; - newThread.pushStack(thread.topBlock); - var i = this.threads.indexOf(thread); - if (i > -1) { - this.threads[i] = newThread; - } else { - this.threads.push(thread); + /** + * Restart a thread in place, maintaining its position in the list of threads. + * This is used by `startHats` to and is necessary to ensure 2.0-like execution order. + * Test project: https://scratch.mit.edu/projects/130183108/ + * @param {!Thread} thread Thread object to restart. + */ + _restartThread (thread) { + const newThread = new Thread(thread.topBlock); + newThread.target = thread.target; + newThread.pushStack(thread.topBlock); + const i = this.threads.indexOf(thread); + if (i > -1) { + this.threads[i] = newThread; + } else { + this.threads.push(thread); + } } -}; -/** - * Return whether a thread is currently active/running. - * @param {?Thread} thread Thread object to check. - * @return {boolean} True if the thread is active/running. - */ -Runtime.prototype.isActiveThread = function (thread) { - return this.threads.indexOf(thread) > -1; -}; + /** + * Return whether a thread is currently active/running. + * @param {?Thread} thread Thread object to check. + * @return {boolean} True if the thread is active/running. + */ + isActiveThread (thread) { + return this.threads.indexOf(thread) > -1; + } -/** - * Toggle a script. - * @param {!string} topBlockId ID of block that starts the script. - */ -Runtime.prototype.toggleScript = function (topBlockId) { - // Remove any existing thread. - for (var i = 0; i < this.threads.length; i++) { - if (this.threads[i].topBlock === topBlockId) { - this._removeThread(this.threads[i]); - return; + /** + * Toggle a script. + * @param {!string} topBlockId ID of block that starts the script. + */ + toggleScript (topBlockId) { + // Remove any existing thread. + for (let i = 0; i < this.threads.length; i++) { + if (this.threads[i].topBlock === topBlockId) { + this._removeThread(this.threads[i]); + return; + } } + // Otherwise add it. + this._pushThread(topBlockId, this._editingTarget); } - // Otherwise add it. - this._pushThread(topBlockId, this._editingTarget); -}; -/** - * Run a function `f` for all scripts in a workspace. - * `f` will be called with two parameters: - * - the top block ID of the script. - * - the target that owns the script. - * @param {!Function} f Function to call for each script. - * @param {Target=} optTarget Optionally, a target to restrict to. - */ -Runtime.prototype.allScriptsDo = function (f, optTarget) { - var targets = this.targets; - if (optTarget) { - targets = [optTarget]; - } - for (var t = targets.length - 1; t >= 0; t--) { - var target = targets[t]; - var scripts = target.blocks.getScripts(); - for (var j = 0; j < scripts.length; j++) { - var topBlockId = scripts[j]; - f(topBlockId, target); + /** + * Run a function `f` for all scripts in a workspace. + * `f` will be called with two parameters: + * - the top block ID of the script. + * - the target that owns the script. + * @param {!Function} f Function to call for each script. + * @param {Target=} optTarget Optionally, a target to restrict to. + */ + allScriptsDo (f, optTarget) { + let targets = this.targets; + if (optTarget) { + targets = [optTarget]; + } + for (let t = targets.length - 1; t >= 0; t--) { + const target = targets[t]; + const scripts = target.blocks.getScripts(); + for (let j = 0; j < scripts.length; j++) { + const topBlockId = scripts[j]; + f(topBlockId, target); + } } } -}; -/** - * Start all relevant hats. - * @param {!string} requestedHatOpcode Opcode of hats to start. - * @param {object=} optMatchFields Optionally, fields to match on the hat. - * @param {Target=} optTarget Optionally, a target to restrict to. - * @return {Array.} List of threads started by this function. - */ -Runtime.prototype.startHats = function (requestedHatOpcode, - optMatchFields, optTarget) { - if (!this._hats.hasOwnProperty(requestedHatOpcode)) { - // No known hat with this opcode. - return; - } - var instance = this; - var newThreads = []; - - for (var opts in optMatchFields) { - if (!optMatchFields.hasOwnProperty(opts)) continue; - optMatchFields[opts] = optMatchFields[opts].toUpperCase(); - } - - // Consider all scripts, looking for hats with opcode `requestedHatOpcode`. - this.allScriptsDo(function (topBlockId, target) { - var blocks = target.blocks; - var block = blocks.getBlock(topBlockId); - var potentialHatOpcode = block.opcode; - if (potentialHatOpcode !== requestedHatOpcode) { - // Not the right hat. + /** + * Start all relevant hats. + * @param {!string} requestedHatOpcode Opcode of hats to start. + * @param {object=} optMatchFields Optionally, fields to match on the hat. + * @param {Target=} optTarget Optionally, a target to restrict to. + * @return {Array.} List of threads started by this function. + */ + startHats (requestedHatOpcode, + optMatchFields, optTarget) { + if (!this._hats.hasOwnProperty(requestedHatOpcode)) { + // No known hat with this opcode. return; } + const instance = this; + const newThreads = []; - // Match any requested fields. - // For example: ensures that broadcasts match. - // This needs to happen before the block is evaluated - // (i.e., before the predicate can be run) because "broadcast and wait" - // needs to have a precise collection of started threads. - var hatFields = blocks.getFields(block); - - // If no fields are present, check inputs (horizontal blocks) - if (Object.keys(hatFields).length === 0) { - var hatInputs = blocks.getInputs(block); - for (var input in hatInputs) { - if (!hatInputs.hasOwnProperty(input)) continue; - var id = hatInputs[input].block; - var inpBlock = blocks.getBlock(id); - var fields = blocks.getFields(inpBlock); - hatFields = Object.assign(fields, hatFields); - } + for (const opts in optMatchFields) { + if (!optMatchFields.hasOwnProperty(opts)) continue; + optMatchFields[opts] = optMatchFields[opts].toUpperCase(); } - if (optMatchFields) { - for (var matchField in optMatchFields) { - if (hatFields[matchField].value.toUpperCase() !== - optMatchFields[matchField]) { - // Field mismatch. - return; + // Consider all scripts, looking for hats with opcode `requestedHatOpcode`. + this.allScriptsDo((topBlockId, target) => { + const blocks = target.blocks; + const block = blocks.getBlock(topBlockId); + const potentialHatOpcode = block.opcode; + if (potentialHatOpcode !== requestedHatOpcode) { + // Not the right hat. + return; + } + + // Match any requested fields. + // For example: ensures that broadcasts match. + // This needs to happen before the block is evaluated + // (i.e., before the predicate can be run) because "broadcast and wait" + // needs to have a precise collection of started threads. + let hatFields = blocks.getFields(block); + + // If no fields are present, check inputs (horizontal blocks) + if (Object.keys(hatFields).length === 0) { + const hatInputs = blocks.getInputs(block); + for (const input in hatInputs) { + if (!hatInputs.hasOwnProperty(input)) continue; + const id = hatInputs[input].block; + const inpBlock = blocks.getBlock(id); + const fields = blocks.getFields(inpBlock); + hatFields = Object.assign(fields, hatFields); } } - } - // Look up metadata for the relevant hat. - var hatMeta = instance._hats[requestedHatOpcode]; - if (hatMeta.restartExistingThreads) { - // If `restartExistingThreads` is true, we should stop - // any existing threads starting with the top block. - for (var i = 0; i < instance.threads.length; i++) { - if (instance.threads[i].topBlock === topBlockId && - instance.threads[i].target === target) { - instance._restartThread(instance.threads[i]); - return; + if (optMatchFields) { + for (const matchField in optMatchFields) { + if (hatFields[matchField].value.toUpperCase() !== + optMatchFields[matchField]) { + // Field mismatch. + return; + } } } - } else { - // If `restartExistingThreads` is false, we should - // give up if any threads with the top block are running. - for (var j = 0; j < instance.threads.length; j++) { - if (instance.threads[j].topBlock === topBlockId && - instance.threads[j].target === target) { - // Some thread is already running. - return; + + // Look up metadata for the relevant hat. + const hatMeta = instance._hats[requestedHatOpcode]; + if (hatMeta.restartExistingThreads) { + // If `restartExistingThreads` is true, we should stop + // any existing threads starting with the top block. + for (let i = 0; i < instance.threads.length; i++) { + if (instance.threads[i].topBlock === topBlockId && + instance.threads[i].target === target) { + instance._restartThread(instance.threads[i]); + return; + } + } + } else { + // If `restartExistingThreads` is false, we should + // give up if any threads with the top block are running. + for (let j = 0; j < instance.threads.length; j++) { + if (instance.threads[j].topBlock === topBlockId && + instance.threads[j].target === target) { + // Some thread is already running. + return; + } } } - } - // Start the thread with this top block. - newThreads.push(instance._pushThread(topBlockId, target)); - }, optTarget); - return newThreads; -}; + // Start the thread with this top block. + newThreads.push(instance._pushThread(topBlockId, target)); + }, optTarget); + return newThreads; + } -/** - * Dispose all targets. Return to clean state. - */ -Runtime.prototype.dispose = function () { - this.stopAll(); - this.targets.map(this.disposeTarget, this); -}; + /** + * Dispose all targets. Return to clean state. + */ + dispose () { + this.stopAll(); + this.targets.map(this.disposeTarget, this); + } -/** - * Dispose of a target. - * @param {!Target} disposingTarget Target to dispose of. - */ -Runtime.prototype.disposeTarget = function (disposingTarget) { - this.targets = this.targets.filter(function (target) { - if (disposingTarget !== target) return true; - // Allow target to do dispose actions. - target.dispose(); - // Remove from list of targets. - return false; - }); -}; + /** + * Dispose of a target. + * @param {!Target} disposingTarget Target to dispose of. + */ + disposeTarget (disposingTarget) { + this.targets = this.targets.filter(target => { + if (disposingTarget !== target) return true; + // Allow target to do dispose actions. + target.dispose(); + // Remove from list of targets. + return false; + }); + } -/** - * Stop any threads acting on the target. - * @param {!Target} target Target to stop threads for. - * @param {Thread=} optThreadException Optional thread to skip. - */ -Runtime.prototype.stopForTarget = function (target, optThreadException) { - // Stop any threads on the target. - for (var i = 0; i < this.threads.length; i++) { - if (this.threads[i] === optThreadException) { - continue; - } - if (this.threads[i].target === target) { - this._removeThread(this.threads[i]); + /** + * Stop any threads acting on the target. + * @param {!Target} target Target to stop threads for. + * @param {Thread=} optThreadException Optional thread to skip. + */ + stopForTarget (target, optThreadException) { + // Stop any threads on the target. + for (let i = 0; i < this.threads.length; i++) { + if (this.threads[i] === optThreadException) { + continue; + } + if (this.threads[i].target === target) { + this._removeThread(this.threads[i]); + } } } -}; -/** - * Start all threads that start with the green flag. - */ -Runtime.prototype.greenFlag = function () { - this.stopAll(); - this.ioDevices.clock.resetProjectTimer(); - this.clearEdgeActivatedValues(); - // Inform all targets of the green flag. - for (var i = 0; i < this.targets.length; i++) { - this.targets[i].onGreenFlag(); - } - this.startHats('event_whenflagclicked'); -}; - -/** - * Stop "everything." - */ -Runtime.prototype.stopAll = function () { - // Dispose all clones. - var newTargets = []; - for (var i = 0; i < this.targets.length; i++) { - this.targets[i].onStopAll(); - if (this.targets[i].hasOwnProperty('isOriginal') && - !this.targets[i].isOriginal) { - this.targets[i].dispose(); - } else { - newTargets.push(this.targets[i]); + /** + * Start all threads that start with the green flag. + */ + greenFlag () { + this.stopAll(); + this.ioDevices.clock.resetProjectTimer(); + this.clearEdgeActivatedValues(); + // Inform all targets of the green flag. + for (let i = 0; i < this.targets.length; i++) { + this.targets[i].onGreenFlag(); } + this.startHats('event_whenflagclicked'); } - this.targets = newTargets; - // Dispose all threads. - var threadsCopy = this.threads.slice(); - while (threadsCopy.length > 0) { - var poppedThread = threadsCopy.pop(); - this._removeThread(poppedThread); - } -}; -/** - * Repeatedly run `sequencer.stepThreads` and filter out - * inactive threads after each iteration. - */ -Runtime.prototype._step = function () { - // Find all edge-activated hats, and add them to threads to be evaluated. - for (var hatType in this._hats) { - if (!this._hats.hasOwnProperty(hatType)) continue; - var hat = this._hats[hatType]; - if (hat.edgeActivated) { - this.startHats(hatType); + /** + * Stop "everything." + */ + stopAll () { + // Dispose all clones. + const newTargets = []; + for (let i = 0; i < this.targets.length; i++) { + this.targets[i].onStopAll(); + if (this.targets[i].hasOwnProperty('isOriginal') && + !this.targets[i].isOriginal) { + this.targets[i].dispose(); + } else { + newTargets.push(this.targets[i]); + } + } + this.targets = newTargets; + // Dispose all threads. + const threadsCopy = this.threads.slice(); + while (threadsCopy.length > 0) { + const poppedThread = threadsCopy.pop(); + this._removeThread(poppedThread); } } - this.redrawRequested = false; - var doneThreads = this.sequencer.stepThreads(); - this._updateGlows(doneThreads); - this._setThreadCount(this.threads.length + doneThreads.length); - if (this.renderer) { - // @todo: Only render when this.redrawRequested or clones rendered. - this.renderer.draw(); + + /** + * Repeatedly run `sequencer.stepThreads` and filter out + * inactive threads after each iteration. + */ + _step () { + // Find all edge-activated hats, and add them to threads to be evaluated. + for (const hatType in this._hats) { + if (!this._hats.hasOwnProperty(hatType)) continue; + const hat = this._hats[hatType]; + if (hat.edgeActivated) { + this.startHats(hatType); + } + } + this.redrawRequested = false; + const doneThreads = this.sequencer.stepThreads(); + this._updateGlows(doneThreads); + this._setThreadCount(this.threads.length + doneThreads.length); + if (this.renderer) { + // @todo: Only render when this.redrawRequested or clones rendered. + this.renderer.draw(); + } } -}; -/** - * Set the current editing target known by the runtime. - * @param {!Target} editingTarget New editing target. - */ -Runtime.prototype.setEditingTarget = function (editingTarget) { - this._editingTarget = editingTarget; - // Script glows must be cleared. - this._scriptGlowsPreviousFrame = []; - this._updateGlows(); - this.spriteInfoReport(editingTarget); -}; + /** + * Set the current editing target known by the runtime. + * @param {!Target} editingTarget New editing target. + */ + setEditingTarget (editingTarget) { + this._editingTarget = editingTarget; + // Script glows must be cleared. + this._scriptGlowsPreviousFrame = []; + this._updateGlows(); + this.spriteInfoReport(editingTarget); + } -/** - * Set whether we are in 30 TPS compatibility mode. - * @param {boolean} compatibilityModeOn True iff in compatibility mode. - */ -Runtime.prototype.setCompatibilityMode = function (compatibilityModeOn) { - this.compatibilityMode = compatibilityModeOn; - if (this._steppingInterval) { - clearInterval(this._steppingInterval); - this.start(); + /** + * Set whether we are in 30 TPS compatibility mode. + * @param {boolean} compatibilityModeOn True iff in compatibility mode. + */ + setCompatibilityMode (compatibilityModeOn) { + this.compatibilityMode = compatibilityModeOn; + if (this._steppingInterval) { + clearInterval(this._steppingInterval); + this.start(); + } } -}; -/** - * Emit glows/glow clears for scripts after a single tick. - * Looks at `this.threads` and notices which have turned on/off new glows. - * @param {Array.=} optExtraThreads Optional list of inactive threads. - */ -Runtime.prototype._updateGlows = function (optExtraThreads) { - var searchThreads = []; - searchThreads.push.apply(searchThreads, this.threads); - if (optExtraThreads) { - searchThreads.push.apply(searchThreads, optExtraThreads); - } - // Set of scripts that request a glow this frame. - var requestedGlowsThisFrame = []; - // Final set of scripts glowing during this frame. - var finalScriptGlows = []; - // Find all scripts that should be glowing. - for (var i = 0; i < searchThreads.length; i++) { - var thread = searchThreads[i]; - var target = thread.target; - if (target === this._editingTarget) { - var blockForThread = thread.blockGlowInFrame; - if (thread.requestScriptGlowInFrame) { - var script = target.blocks.getTopLevelScript(blockForThread); - if (!script) { - // Attempt to find in flyout blocks. - script = this.flyoutBlocks.getTopLevelScript( - blockForThread - ); - } - if (script) { - requestedGlowsThisFrame.push(script); + /** + * Emit glows/glow clears for scripts after a single tick. + * Looks at `this.threads` and notices which have turned on/off new glows. + * @param {Array.=} optExtraThreads Optional list of inactive threads. + */ + _updateGlows (optExtraThreads) { + const searchThreads = []; + searchThreads.push.apply(searchThreads, this.threads); + if (optExtraThreads) { + searchThreads.push.apply(searchThreads, optExtraThreads); + } + // Set of scripts that request a glow this frame. + const requestedGlowsThisFrame = []; + // Final set of scripts glowing during this frame. + const finalScriptGlows = []; + // Find all scripts that should be glowing. + for (let i = 0; i < searchThreads.length; i++) { + const thread = searchThreads[i]; + const target = thread.target; + if (target === this._editingTarget) { + const blockForThread = thread.blockGlowInFrame; + if (thread.requestScriptGlowInFrame) { + let script = target.blocks.getTopLevelScript(blockForThread); + if (!script) { + // Attempt to find in flyout blocks. + script = this.flyoutBlocks.getTopLevelScript( + blockForThread + ); + } + if (script) { + requestedGlowsThisFrame.push(script); + } } } } - } - // Compare to previous frame. - for (var j = 0; j < this._scriptGlowsPreviousFrame.length; j++) { - var previousFrameGlow = this._scriptGlowsPreviousFrame[j]; - if (requestedGlowsThisFrame.indexOf(previousFrameGlow) < 0) { - // Glow turned off. - this.glowScript(previousFrameGlow, false); - } else { - // Still glowing. - finalScriptGlows.push(previousFrameGlow); + // Compare to previous frame. + for (let j = 0; j < this._scriptGlowsPreviousFrame.length; j++) { + const previousFrameGlow = this._scriptGlowsPreviousFrame[j]; + if (requestedGlowsThisFrame.indexOf(previousFrameGlow) < 0) { + // Glow turned off. + this.glowScript(previousFrameGlow, false); + } else { + // Still glowing. + finalScriptGlows.push(previousFrameGlow); + } } - } - for (var k = 0; k < requestedGlowsThisFrame.length; k++) { - var currentFrameGlow = requestedGlowsThisFrame[k]; - if (this._scriptGlowsPreviousFrame.indexOf(currentFrameGlow) < 0) { - // Glow turned on. - this.glowScript(currentFrameGlow, true); - finalScriptGlows.push(currentFrameGlow); + for (let k = 0; k < requestedGlowsThisFrame.length; k++) { + const currentFrameGlow = requestedGlowsThisFrame[k]; + if (this._scriptGlowsPreviousFrame.indexOf(currentFrameGlow) < 0) { + // Glow turned on. + this.glowScript(currentFrameGlow, true); + finalScriptGlows.push(currentFrameGlow); + } } + this._scriptGlowsPreviousFrame = finalScriptGlows; } - this._scriptGlowsPreviousFrame = finalScriptGlows; -}; -/** - * Emit run start/stop after each tick. Emits when `this.threads.length` goes - * between non-zero and zero - * - * @param {number} threadCount The new threadCount - */ -Runtime.prototype._setThreadCount = function (threadCount) { - if (this._threadCount === 0 && threadCount > 0) { - this.emit(Runtime.PROJECT_RUN_START); - } - if (this._threadCount > 0 && threadCount === 0) { - this.emit(Runtime.PROJECT_RUN_STOP); + /** + * Emit run start/stop after each tick. Emits when `this.threads.length` goes + * between non-zero and zero + * + * @param {number} threadCount The new threadCount + */ + _setThreadCount (threadCount) { + if (this._threadCount === 0 && threadCount > 0) { + this.emit(Runtime.PROJECT_RUN_START); + } + if (this._threadCount > 0 && threadCount === 0) { + this.emit(Runtime.PROJECT_RUN_STOP); + } + this._threadCount = threadCount; } - this._threadCount = threadCount; -}; -/** - * "Quiet" a script's glow: stop the VM from generating glow/unglow events - * about that script. Use when a script has just been deleted, but we may - * still be tracking glow data about it. - * @param {!string} scriptBlockId Id of top-level block in script to quiet. - */ -Runtime.prototype.quietGlow = function (scriptBlockId) { - var index = this._scriptGlowsPreviousFrame.indexOf(scriptBlockId); - if (index > -1) { - this._scriptGlowsPreviousFrame.splice(index, 1); + /** + * "Quiet" a script's glow: stop the VM from generating glow/unglow events + * about that script. Use when a script has just been deleted, but we may + * still be tracking glow data about it. + * @param {!string} scriptBlockId Id of top-level block in script to quiet. + */ + quietGlow (scriptBlockId) { + const index = this._scriptGlowsPreviousFrame.indexOf(scriptBlockId); + if (index > -1) { + this._scriptGlowsPreviousFrame.splice(index, 1); + } } -}; -/** - * Emit feedback for block glowing (used in the sequencer). - * @param {?string} blockId ID for the block to update glow - * @param {boolean} isGlowing True to turn on glow; false to turn off. - */ -Runtime.prototype.glowBlock = function (blockId, isGlowing) { - if (isGlowing) { - this.emit(Runtime.BLOCK_GLOW_ON, {id: blockId}); - } else { - this.emit(Runtime.BLOCK_GLOW_OFF, {id: blockId}); + /** + * Emit feedback for block glowing (used in the sequencer). + * @param {?string} blockId ID for the block to update glow + * @param {boolean} isGlowing True to turn on glow; false to turn off. + */ + glowBlock (blockId, isGlowing) { + if (isGlowing) { + this.emit(Runtime.BLOCK_GLOW_ON, {id: blockId}); + } else { + this.emit(Runtime.BLOCK_GLOW_OFF, {id: blockId}); + } } -}; -/** - * Emit feedback for script glowing. - * @param {?string} topBlockId ID for the top block to update glow - * @param {boolean} isGlowing True to turn on glow; false to turn off. - */ -Runtime.prototype.glowScript = function (topBlockId, isGlowing) { - if (isGlowing) { - this.emit(Runtime.SCRIPT_GLOW_ON, {id: topBlockId}); - } else { - this.emit(Runtime.SCRIPT_GLOW_OFF, {id: topBlockId}); + /** + * Emit feedback for script glowing. + * @param {?string} topBlockId ID for the top block to update glow + * @param {boolean} isGlowing True to turn on glow; false to turn off. + */ + glowScript (topBlockId, isGlowing) { + if (isGlowing) { + this.emit(Runtime.SCRIPT_GLOW_ON, {id: topBlockId}); + } else { + this.emit(Runtime.SCRIPT_GLOW_OFF, {id: topBlockId}); + } } -}; -/** - * Emit value for reporter to show in the blocks. - * @param {string} blockId ID for the block. - * @param {string} value Value to show associated with the block. - */ -Runtime.prototype.visualReport = function (blockId, value) { - this.emit(Runtime.VISUAL_REPORT, {id: blockId, value: String(value)}); -}; + /** + * Emit value for reporter to show in the blocks. + * @param {string} blockId ID for the block. + * @param {string} value Value to show associated with the block. + */ + visualReport (blockId, value) { + this.emit(Runtime.VISUAL_REPORT, {id: blockId, value: String(value)}); + } -/** - * Emit a sprite info report if the provided target is the original sprite - * @param {!Target} target Target to report sprite info for. - */ -Runtime.prototype.spriteInfoReport = function (target) { - if (!target.isOriginal) return; + /** + * Emit a sprite info report if the provided target is the original sprite + * @param {!Target} target Target to report sprite info for. + */ + spriteInfoReport (target) { + if (!target.isOriginal) return; - this.emit(Runtime.SPRITE_INFO_REPORT, target.toJSON()); -}; + this.emit(Runtime.SPRITE_INFO_REPORT, target.toJSON()); + } -/** - * Get a target by its id. - * @param {string} targetId Id of target to find. - * @return {?Target} The target, if found. - */ -Runtime.prototype.getTargetById = function (targetId) { - for (var i = 0; i < this.targets.length; i++) { - var target = this.targets[i]; - if (target.id === targetId) { - return target; + /** + * Get a target by its id. + * @param {string} targetId Id of target to find. + * @return {?Target} The target, if found. + */ + getTargetById (targetId) { + for (let i = 0; i < this.targets.length; i++) { + const target = this.targets[i]; + if (target.id === targetId) { + return target; + } } } -}; -/** - * Get the first original (non-clone-block-created) sprite given a name. - * @param {string} spriteName Name of sprite to look for. - * @return {?Target} Target representing a sprite of the given name. - */ -Runtime.prototype.getSpriteTargetByName = function (spriteName) { - for (var i = 0; i < this.targets.length; i++) { - var target = this.targets[i]; - if (target.sprite && target.sprite.name === spriteName) { - return target; + /** + * Get the first original (non-clone-block-created) sprite given a name. + * @param {string} spriteName Name of sprite to look for. + * @return {?Target} Target representing a sprite of the given name. + */ + getSpriteTargetByName (spriteName) { + for (let i = 0; i < this.targets.length; i++) { + const target = this.targets[i]; + if (target.sprite && target.sprite.name === spriteName) { + return target; + } } } -}; -/** - * Get a target by its drawable id. - * @param {number} drawableID drawable id of target to find - * @return {?Target} The target, if found - */ -Runtime.prototype.getTargetByDrawableId = function (drawableID) { - for (var i = 0; i < this.targets.length; i++) { - var target = this.targets[i]; - if (target.drawableID === drawableID) return target; + /** + * Get a target by its drawable id. + * @param {number} drawableID drawable id of target to find + * @return {?Target} The target, if found + */ + getTargetByDrawableId (drawableID) { + for (let i = 0; i < this.targets.length; i++) { + const target = this.targets[i]; + if (target.drawableID === drawableID) return target; + } } -}; -/** - * Update the clone counter to track how many clones are created. - * @param {number} changeAmount How many clones have been created/destroyed. - */ -Runtime.prototype.changeCloneCounter = function (changeAmount) { - this._cloneCounter += changeAmount; -}; + /** + * Update the clone counter to track how many clones are created. + * @param {number} changeAmount How many clones have been created/destroyed. + */ + changeCloneCounter (changeAmount) { + this._cloneCounter += changeAmount; + } -/** - * Return whether there are clones available. - * @return {boolean} True until the number of clones hits Runtime.MAX_CLONES. - */ -Runtime.prototype.clonesAvailable = function () { - return this._cloneCounter < Runtime.MAX_CLONES; -}; + /** + * Return whether there are clones available. + * @return {boolean} True until the number of clones hits Runtime.MAX_CLONES. + */ + clonesAvailable () { + return this._cloneCounter < Runtime.MAX_CLONES; + } -/** - * Get a target representing the Scratch stage, if one exists. - * @return {?Target} The target, if found. - */ -Runtime.prototype.getTargetForStage = function () { - for (var i = 0; i < this.targets.length; i++) { - var target = this.targets[i]; - if (target.isStage) { - return target; + /** + * Get a target representing the Scratch stage, if one exists. + * @return {?Target} The target, if found. + */ + getTargetForStage () { + for (let i = 0; i < this.targets.length; i++) { + const target = this.targets[i]; + if (target.isStage) { + return target; + } } } -}; -/** - * Tell the runtime to request a redraw. - * Use after a clone/sprite has completed some visible operation on the stage. - */ -Runtime.prototype.requestRedraw = function () { - this.redrawRequested = true; -}; + /** + * Tell the runtime to request a redraw. + * Use after a clone/sprite has completed some visible operation on the stage. + */ + requestRedraw () { + this.redrawRequested = true; + } -/** - * Set up timers to repeatedly step in a browser. - */ -Runtime.prototype.start = function () { - var interval = Runtime.THREAD_STEP_INTERVAL; - if (this.compatibilityMode) { - interval = Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY; - } - this.currentStepTime = interval; - this._steppingInterval = setInterval(function () { - this._step(); - }.bind(this), interval); -}; + /** + * Set up timers to repeatedly step in a browser. + */ + start () { + let interval = Runtime.THREAD_STEP_INTERVAL; + if (this.compatibilityMode) { + interval = Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY; + } + this.currentStepTime = interval; + this._steppingInterval = setInterval(() => { + this._step(); + }, interval); + } +} module.exports = Runtime; diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 29154af344..e6323ec73e 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -1,8 +1,6 @@ -var util = require('util'); - -var log = require('../util/log'); -var MathUtil = require('../util/math-util'); -var Target = require('../engine/target'); +const log = require('../util/log'); +const MathUtil = require('../util/math-util'); +const Target = require('../engine/target'); /** * Rendered target: instance of a sprite (clone), or the stage. @@ -10,791 +8,796 @@ var Target = require('../engine/target'); * @param {Runtime} runtime Reference to the runtime. * @constructor */ -var RenderedTarget = function (sprite, runtime) { - Target.call(this, sprite.blocks); - this.runtime = runtime; +class RenderedTarget extends Target { + constructor (sprite, runtime) { + super(sprite.blocks); + this.runtime = runtime; + /** + * Reference to the sprite that this is a render of. + * @type {!Sprite} + */ + this.sprite = sprite; + /** + * Reference to the global renderer for this VM, if one exists. + * @type {?RenderWebGLWorker} + */ + this.renderer = null; + if (this.runtime) { + this.renderer = this.runtime.renderer; + } + /** + * ID of the drawable for this rendered target, + * returned by the renderer, if rendered. + * @type {?Number} + */ + this.drawableID = null; + + /** + * Drag state of this rendered target. If true, x/y position can't be + * changed by blocks. + * @type {boolean} + */ + this.dragging = false; + + /** + * Map of current graphic effect values. + * @type {!Object.} + */ + this.effects = { + color: 0, + fisheye: 0, + whirl: 0, + pixelate: 0, + mosaic: 0, + brightness: 0, + ghost: 0 + }; + + /** + * Whether this represents an "original" non-clone rendered-target for a sprite, + * i.e., created by the editor and not clone blocks. + * @type {boolean} + */ + this.isOriginal = true; + + /** + * Whether this rendered target represents the Scratch stage. + * @type {boolean} + */ + this.isStage = false; + + /** + * Scratch X coordinate. Currently should range from -240 to 240. + * @type {Number} + */ + this.x = 0; + + /** + * Scratch Y coordinate. Currently should range from -180 to 180. + * @type {number} + */ + this.y = 0; + + /** + * Scratch direction. Currently should range from -179 to 180. + * @type {number} + */ + this.direction = 90; + + /** + * Whether the rendered target is draggable on the stage + * @type {boolean} + */ + this.draggable = false; + + /** + * Whether the rendered target is currently visible. + * @type {boolean} + */ + this.visible = true; + + /** + * Size of rendered target as a percent of costume size. + * @type {number} + */ + this.size = 100; + + /** + * Currently selected costume index. + * @type {number} + */ + this.currentCostume = 0; + + /** + * Current rotation style. + * @type {!string} + */ + this.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND; + } + /** - * Reference to the sprite that this is a render of. - * @type {!Sprite} + * Create a drawable with the this.renderer. */ - this.sprite = sprite; + initDrawable () { + if (this.renderer) { + this.drawableID = this.renderer.createDrawable(); + } + // If we're a clone, start the hats. + if (!this.isOriginal) { + this.runtime.startHats( + 'control_start_as_clone', null, this + ); + } + + /** + * Audio player + */ + this.audioPlayer = null; + if (this.runtime && this.runtime.audioEngine) { + this.audioPlayer = this.runtime.audioEngine.createPlayer(); + } + } + /** - * Reference to the global renderer for this VM, if one exists. - * @type {?RenderWebGLWorker} + * Event which fires when a target moves. + * @type {string} */ - this.renderer = null; - if (this.runtime) { - this.renderer = this.runtime.renderer; + static get EVENT_TARGET_MOVED () { + return 'TARGET_MOVED'; } + /** - * ID of the drawable for this rendered target, - * returned by the renderer, if rendered. - * @type {?Number} + * Rotation style for "all around"/spinning. + * @type {string} */ - this.drawableID = null; + static get ROTATION_STYLE_ALL_AROUND () { + return 'all around'; + } /** - * Drag state of this rendered target. If true, x/y position can't be - * changed by blocks. - * @type {boolean} + * Rotation style for "left-right"/flipping. + * @type {string} */ - this.dragging = false; + static get ROTATION_STYLE_LEFT_RIGHT () { + return 'left-right'; + } /** - * Map of current graphic effect values. - * @type {!Object.} + * Rotation style for "no rotation." + * @type {string} */ - this.effects = { - color: 0, - fisheye: 0, - whirl: 0, - pixelate: 0, - mosaic: 0, - brightness: 0, - ghost: 0 - }; -}; -util.inherits(RenderedTarget, Target); - -/** - * Create a drawable with the this.renderer. - */ -RenderedTarget.prototype.initDrawable = function () { - if (this.renderer) { - this.drawableID = this.renderer.createDrawable(); - } - // If we're a clone, start the hats. - if (!this.isOriginal) { - this.runtime.startHats( - 'control_start_as_clone', null, this - ); + static get ROTATION_STYLE_NONE () { + return "don't rotate"; } /** - * Audio player - */ - this.audioPlayer = null; - if (this.runtime && this.runtime.audioEngine) { - this.audioPlayer = this.runtime.audioEngine.createPlayer(); + * Set the X and Y coordinates. + * @param {!number} x New X coordinate, in Scratch coordinates. + * @param {!number} y New Y coordinate, in Scratch coordinates. + * @param {?boolean} force Force setting X/Y, in case of dragging + */ + setXY (x, y, force) { + if (this.isStage) return; + if (this.dragging && !force) return; + const oldX = this.x; + const oldY = this.y; + if (this.renderer) { + const position = this.renderer.getFencedPositionOfDrawable(this.drawableID, [x, y]); + this.x = position[0]; + this.y = position[1]; + + this.renderer.updateDrawableProperties(this.drawableID, { + position: position + }); + if (this.visible) { + this.runtime.requestRedraw(); + } + } else { + this.x = x; + this.y = y; + } + this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY); + this.runtime.spriteInfoReport(this); } -}; - -/** - * Whether this represents an "original" non-clone rendered-target for a sprite, - * i.e., created by the editor and not clone blocks. - * @type {boolean} - */ -RenderedTarget.prototype.isOriginal = true; - -/** - * Whether this rendered target represents the Scratch stage. - * @type {boolean} - */ -RenderedTarget.prototype.isStage = false; -/** - * Scratch X coordinate. Currently should range from -240 to 240. - * @type {Number} - */ -RenderedTarget.prototype.x = 0; - -/** - * Scratch Y coordinate. Currently should range from -180 to 180. - * @type {number} - */ -RenderedTarget.prototype.y = 0; - -/** - * Scratch direction. Currently should range from -179 to 180. - * @type {number} - */ -RenderedTarget.prototype.direction = 90; - -/** - * Whether the rendered target is draggable on the stage - * @type {boolean} - */ -RenderedTarget.prototype.draggable = false; - -/** - * Whether the rendered target is currently visible. - * @type {boolean} - */ -RenderedTarget.prototype.visible = true; - -/** - * Size of rendered target as a percent of costume size. - * @type {number} - */ -RenderedTarget.prototype.size = 100; - -/** - * Currently selected costume index. - * @type {number} - */ -RenderedTarget.prototype.currentCostume = 0; - -/** - * Event which fires when a target moves. - * @type {string} - */ -RenderedTarget.EVENT_TARGET_MOVED = 'TARGET_MOVED'; - -/** - * Rotation style for "all around"/spinning. - * @enum - */ -RenderedTarget.ROTATION_STYLE_ALL_AROUND = 'all around'; - -/** - * Rotation style for "left-right"/flipping. - * @enum - */ -RenderedTarget.ROTATION_STYLE_LEFT_RIGHT = 'left-right'; - -/** - * Rotation style for "no rotation." - * @enum - */ -RenderedTarget.ROTATION_STYLE_NONE = 'don\'t rotate'; - -/** - * Current rotation style. - * @type {!string} - */ -RenderedTarget.prototype.rotationStyle = ( - RenderedTarget.ROTATION_STYLE_ALL_AROUND -); - -/** - * Set the X and Y coordinates. - * @param {!number} x New X coordinate, in Scratch coordinates. - * @param {!number} y New Y coordinate, in Scratch coordinates. - * @param {?boolean} force Force setting X/Y, in case of dragging - */ -RenderedTarget.prototype.setXY = function (x, y, force) { - if (this.isStage) return; - if (this.dragging && !force) return; - var oldX = this.x; - var oldY = this.y; - if (this.renderer) { - var position = this.renderer.getFencedPositionOfDrawable(this.drawableID, [x, y]); - this.x = position[0]; - this.y = position[1]; - - this.renderer.updateDrawableProperties(this.drawableID, { - position: position - }); - if (this.visible) { - this.runtime.requestRedraw(); - } - } else { - this.x = x; - this.y = y; - } - this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY); - this.runtime.spriteInfoReport(this); -}; - -/** - * Get the rendered direction and scale, after applying rotation style. - * @return {object} Direction and scale to render. - */ -RenderedTarget.prototype._getRenderedDirectionAndScale = function () { - // Default: no changes to `this.direction` or `this.scale`. - var finalDirection = this.direction; - var finalScale = [this.size, this.size]; - if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_NONE) { - // Force rendered direction to be 90. - finalDirection = 90; - } else if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_LEFT_RIGHT) { - // Force rendered direction to be 90, and flip drawable if needed. - finalDirection = 90; - var scaleFlip = (this.direction < 0) ? -1 : 1; - finalScale = [scaleFlip * this.size, this.size]; - } - return {direction: finalDirection, scale: finalScale}; -}; - -/** - * Set the direction. - * @param {!number} direction New direction. - */ -RenderedTarget.prototype.setDirection = function (direction) { - if (this.isStage) { - return; - } - // Keep direction between -179 and +180. - this.direction = MathUtil.wrapClamp(direction, -179, 180); - if (this.renderer) { - var renderedDirectionScale = this._getRenderedDirectionAndScale(); - this.renderer.updateDrawableProperties(this.drawableID, { - direction: renderedDirectionScale.direction, - scale: renderedDirectionScale.scale - }); - if (this.visible) { - this.runtime.requestRedraw(); + /** + * Get the rendered direction and scale, after applying rotation style. + * @return {object} Direction and scale to render. + */ + _getRenderedDirectionAndScale () { + // Default: no changes to `this.direction` or `this.scale`. + let finalDirection = this.direction; + let finalScale = [this.size, this.size]; + if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_NONE) { + // Force rendered direction to be 90. + finalDirection = 90; + } else if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_LEFT_RIGHT) { + // Force rendered direction to be 90, and flip drawable if needed. + finalDirection = 90; + const scaleFlip = (this.direction < 0) ? -1 : 1; + finalScale = [scaleFlip * this.size, this.size]; } + return {direction: finalDirection, scale: finalScale}; } - this.runtime.spriteInfoReport(this); -}; -/** - * Set draggability; i.e., whether it's able to be dragged in the player - * @param {!boolean} draggable True if should be draggable. - */ -RenderedTarget.prototype.setDraggable = function (draggable) { - if (this.isStage) return; - this.draggable = !!draggable; - this.runtime.spriteInfoReport(this); -}; - -/** - * Set a say bubble. - * @param {?string} type Type of say bubble: "say", "think", or null. - * @param {?string} message Message to put in say bubble. - */ -RenderedTarget.prototype.setSay = function (type, message) { - if (this.isStage) { - return; - } - // @todo: Render to stage. - if (!type || !message) { - log.info('Clearing say bubble'); - return; + /** + * Set the direction. + * @param {!number} direction New direction. + */ + setDirection (direction) { + if (this.isStage) { + return; + } + // Keep direction between -179 and +180. + this.direction = MathUtil.wrapClamp(direction, -179, 180); + if (this.renderer) { + const renderedDirectionScale = this._getRenderedDirectionAndScale(); + this.renderer.updateDrawableProperties(this.drawableID, { + direction: renderedDirectionScale.direction, + scale: renderedDirectionScale.scale + }); + if (this.visible) { + this.runtime.requestRedraw(); + } + } + this.runtime.spriteInfoReport(this); } - log.info('Setting say bubble:', type, message); -}; -/** - * Set visibility; i.e., whether it's shown or hidden. - * @param {!boolean} visible True if should be shown. - */ -RenderedTarget.prototype.setVisible = function (visible) { - if (this.isStage) { - return; + /** + * Set draggability; i.e., whether it's able to be dragged in the player + * @param {!boolean} draggable True if should be draggable. + */ + setDraggable (draggable) { + if (this.isStage) return; + this.draggable = !!draggable; + this.runtime.spriteInfoReport(this); } - this.visible = !!visible; - if (this.renderer) { - this.renderer.updateDrawableProperties(this.drawableID, { - visible: this.visible - }); - if (this.visible) { - this.runtime.requestRedraw(); + + /** + * Set a say bubble. + * @param {?string} type Type of say bubble: "say", "think", or null. + * @param {?string} message Message to put in say bubble. + */ + setSay (type, message) { + if (this.isStage) { + return; } + // @todo: Render to stage. + if (!type || !message) { + log.info('Clearing say bubble'); + return; + } + log.info('Setting say bubble:', type, message); } - this.runtime.spriteInfoReport(this); -}; -/** - * Set size, as a percentage of the costume size. - * @param {!number} size Size of rendered target, as % of costume size. - */ -RenderedTarget.prototype.setSize = function (size) { - if (this.isStage) { - return; - } - if (this.renderer) { - // Clamp to scales relative to costume and stage size. - // See original ScratchSprite.as:setSize. - var costumeSize = this.renderer.getSkinSize(this.drawableID); - var origW = costumeSize[0]; - var origH = costumeSize[1]; - var minScale = Math.min(1, Math.max(5 / origW, 5 / origH)); - var maxScale = Math.min( - (1.5 * this.runtime.constructor.STAGE_WIDTH) / origW, - (1.5 * this.runtime.constructor.STAGE_HEIGHT) / origH - ); - this.size = MathUtil.clamp(size / 100, minScale, maxScale) * 100; - var renderedDirectionScale = this._getRenderedDirectionAndScale(); - this.renderer.updateDrawableProperties(this.drawableID, { - direction: renderedDirectionScale.direction, - scale: renderedDirectionScale.scale - }); - if (this.visible) { - this.runtime.requestRedraw(); + /** + * Set visibility; i.e., whether it's shown or hidden. + * @param {!boolean} visible True if should be shown. + */ + setVisible (visible) { + if (this.isStage) { + return; } + this.visible = !!visible; + if (this.renderer) { + this.renderer.updateDrawableProperties(this.drawableID, { + visible: this.visible + }); + if (this.visible) { + this.runtime.requestRedraw(); + } + } + this.runtime.spriteInfoReport(this); } -}; -/** - * Set a particular graphic effect value. - * @param {!string} effectName Name of effect (see `RenderedTarget.prototype.effects`). - * @param {!number} value Numerical magnitude of effect. - */ -RenderedTarget.prototype.setEffect = function (effectName, value) { - if (!this.effects.hasOwnProperty(effectName)) return; - this.effects[effectName] = value; - if (this.renderer) { - var props = {}; - props[effectName] = this.effects[effectName]; - this.renderer.updateDrawableProperties(this.drawableID, props); - if (this.visible) { - this.runtime.requestRedraw(); + /** + * Set size, as a percentage of the costume size. + * @param {!number} size Size of rendered target, as % of costume size. + */ + setSize (size) { + if (this.isStage) { + return; + } + if (this.renderer) { + // Clamp to scales relative to costume and stage size. + // See original ScratchSprite.as:setSize. + const costumeSize = this.renderer.getSkinSize(this.drawableID); + const origW = costumeSize[0]; + const origH = costumeSize[1]; + const minScale = Math.min(1, Math.max(5 / origW, 5 / origH)); + const maxScale = Math.min( + (1.5 * this.runtime.constructor.STAGE_WIDTH) / origW, + (1.5 * this.runtime.constructor.STAGE_HEIGHT) / origH + ); + this.size = MathUtil.clamp(size / 100, minScale, maxScale) * 100; + const renderedDirectionScale = this._getRenderedDirectionAndScale(); + this.renderer.updateDrawableProperties(this.drawableID, { + direction: renderedDirectionScale.direction, + scale: renderedDirectionScale.scale + }); + if (this.visible) { + this.runtime.requestRedraw(); + } } } -}; -/** - * Clear all graphic effects on this rendered target. - */ -RenderedTarget.prototype.clearEffects = function () { - for (var effectName in this.effects) { - if (!this.effects.hasOwnProperty(effectName)) continue; - this.effects[effectName] = 0; - } - if (this.renderer) { - this.renderer.updateDrawableProperties(this.drawableID, this.effects); - if (this.visible) { - this.runtime.requestRedraw(); + /** + * Set a particular graphic effect value. + * @param {!string} effectName Name of effect (see `RenderedTarget.prototype.effects`). + * @param {!number} value Numerical magnitude of effect. + */ + setEffect (effectName, value) { + if (!this.effects.hasOwnProperty(effectName)) return; + this.effects[effectName] = value; + if (this.renderer) { + const props = {}; + props[effectName] = this.effects[effectName]; + this.renderer.updateDrawableProperties(this.drawableID, props); + if (this.visible) { + this.runtime.requestRedraw(); + } } } -}; -/** - * Set the current costume. - * @param {number} index New index of costume. - */ -RenderedTarget.prototype.setCostume = function (index) { - // Keep the costume index within possible values. - index = Math.round(index); - this.currentCostume = MathUtil.wrapClamp( - index, 0, this.sprite.costumes.length - 1 - ); - if (this.renderer) { - var costume = this.sprite.costumes[this.currentCostume]; - var drawableProperties = { - skinId: costume.skinId, - costumeResolution: costume.bitmapResolution - }; - if ( - typeof costume.rotationCenterX !== 'undefined' && - typeof costume.rotationCenterY !== 'undefined' - ) { - var scale = costume.bitmapResolution || 1; - drawableProperties.rotationCenter = [ - costume.rotationCenterX / scale, - costume.rotationCenterY / scale - ]; + /** + * Clear all graphic effects on this rendered target. + */ + clearEffects () { + for (const effectName in this.effects) { + if (!this.effects.hasOwnProperty(effectName)) continue; + this.effects[effectName] = 0; } - this.renderer.updateDrawableProperties(this.drawableID, drawableProperties); - if (this.visible) { - this.runtime.requestRedraw(); + if (this.renderer) { + this.renderer.updateDrawableProperties(this.drawableID, this.effects); + if (this.visible) { + this.runtime.requestRedraw(); + } } } - this.runtime.spriteInfoReport(this); -}; -/** - * Update the rotation style. - * @param {!string} rotationStyle New rotation style. - */ -RenderedTarget.prototype.setRotationStyle = function (rotationStyle) { - if (rotationStyle === RenderedTarget.ROTATION_STYLE_NONE) { - this.rotationStyle = RenderedTarget.ROTATION_STYLE_NONE; - } else if (rotationStyle === RenderedTarget.ROTATION_STYLE_ALL_AROUND) { - this.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND; - } else if (rotationStyle === RenderedTarget.ROTATION_STYLE_LEFT_RIGHT) { - this.rotationStyle = RenderedTarget.ROTATION_STYLE_LEFT_RIGHT; + /** + * Set the current costume. + * @param {number} index New index of costume. + */ + setCostume (index) { + // Keep the costume index within possible values. + index = Math.round(index); + this.currentCostume = MathUtil.wrapClamp( + index, 0, this.sprite.costumes.length - 1 + ); + if (this.renderer) { + const costume = this.sprite.costumes[this.currentCostume]; + const drawableProperties = { + skinId: costume.skinId, + costumeResolution: costume.bitmapResolution + }; + if ( + typeof costume.rotationCenterX !== 'undefined' && + typeof costume.rotationCenterY !== 'undefined' + ) { + const scale = costume.bitmapResolution || 1; + drawableProperties.rotationCenter = [ + costume.rotationCenterX / scale, + costume.rotationCenterY / scale + ]; + } + this.renderer.updateDrawableProperties(this.drawableID, drawableProperties); + if (this.visible) { + this.runtime.requestRedraw(); + } + } + this.runtime.spriteInfoReport(this); } - if (this.renderer) { - var renderedDirectionScale = this._getRenderedDirectionAndScale(); - this.renderer.updateDrawableProperties(this.drawableID, { - direction: renderedDirectionScale.direction, - scale: renderedDirectionScale.scale - }); - if (this.visible) { - this.runtime.requestRedraw(); + + /** + * Update the rotation style. + * @param {!string} rotationStyle New rotation style. + */ + setRotationStyle (rotationStyle) { + if (rotationStyle === RenderedTarget.ROTATION_STYLE_NONE) { + this.rotationStyle = RenderedTarget.ROTATION_STYLE_NONE; + } else if (rotationStyle === RenderedTarget.ROTATION_STYLE_ALL_AROUND) { + this.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND; + } else if (rotationStyle === RenderedTarget.ROTATION_STYLE_LEFT_RIGHT) { + this.rotationStyle = RenderedTarget.ROTATION_STYLE_LEFT_RIGHT; + } + if (this.renderer) { + const renderedDirectionScale = this._getRenderedDirectionAndScale(); + this.renderer.updateDrawableProperties(this.drawableID, { + direction: renderedDirectionScale.direction, + scale: renderedDirectionScale.scale + }); + if (this.visible) { + this.runtime.requestRedraw(); + } } + this.runtime.spriteInfoReport(this); } - this.runtime.spriteInfoReport(this); -}; -/** - * Get a costume index of this rendered target, by name of the costume. - * @param {?string} costumeName Name of a costume. - * @return {number} Index of the named costume, or -1 if not present. - */ -RenderedTarget.prototype.getCostumeIndexByName = function (costumeName) { - for (var i = 0; i < this.sprite.costumes.length; i++) { - if (this.sprite.costumes[i].name === costumeName) { - return i; + /** + * Get a costume index of this rendered target, by name of the costume. + * @param {?string} costumeName Name of a costume. + * @return {number} Index of the named costume, or -1 if not present. + */ + getCostumeIndexByName (costumeName) { + for (let i = 0; i < this.sprite.costumes.length; i++) { + if (this.sprite.costumes[i].name === costumeName) { + return i; + } } + return -1; } - return -1; -}; -/** - * Get a costume of this rendered target by id. - * @return {object} current costume - */ -RenderedTarget.prototype.getCurrentCostume = function () { - return this.sprite.costumes[this.currentCostume]; -}; + /** + * Get a costume of this rendered target by id. + * @return {object} current costume + */ + getCurrentCostume () { + return this.sprite.costumes[this.currentCostume]; + } -/** - * Get full costume list - * @return {object[]} list of costumes - */ -RenderedTarget.prototype.getCostumes = function () { - return this.sprite.costumes; -}; + /** + * Get full costume list + * @return {object[]} list of costumes + */ + getCostumes () { + return this.sprite.costumes; + } -/** - * Get full sound list - * @return {object[]} list of sounds - */ -RenderedTarget.prototype.getSounds = function () { - return this.sprite.sounds; -}; + /** + * Get full sound list + * @return {object[]} list of sounds + */ + getSounds () { + return this.sprite.sounds; + } -/** - * Update all drawable properties for this rendered target. - * Use when a batch has changed, e.g., when the drawable is first created. - */ -RenderedTarget.prototype.updateAllDrawableProperties = function () { - if (this.renderer) { - var renderedDirectionScale = this._getRenderedDirectionAndScale(); - var costume = this.sprite.costumes[this.currentCostume]; - var bitmapResolution = costume.bitmapResolution || 1; - var props = { - position: [this.x, this.y], - direction: renderedDirectionScale.direction, - draggable: this.draggable, - scale: renderedDirectionScale.scale, - visible: this.visible, - skinId: costume.skinId, - costumeResolution: bitmapResolution, - rotationCenter: [ - costume.rotationCenterX / bitmapResolution, - costume.rotationCenterY / bitmapResolution - ] - }; - for (var effectName in this.effects) { - if (!this.effects.hasOwnProperty(effectName)) continue; - props[effectName] = this.effects[effectName]; - } - this.renderer.updateDrawableProperties(this.drawableID, props); - if (this.visible) { - this.runtime.requestRedraw(); + /** + * Update all drawable properties for this rendered target. + * Use when a batch has changed, e.g., when the drawable is first created. + */ + updateAllDrawableProperties () { + if (this.renderer) { + const renderedDirectionScale = this._getRenderedDirectionAndScale(); + const costume = this.sprite.costumes[this.currentCostume]; + const bitmapResolution = costume.bitmapResolution || 1; + const props = { + position: [this.x, this.y], + direction: renderedDirectionScale.direction, + draggable: this.draggable, + scale: renderedDirectionScale.scale, + visible: this.visible, + skinId: costume.skinId, + costumeResolution: bitmapResolution, + rotationCenter: [ + costume.rotationCenterX / bitmapResolution, + costume.rotationCenterY / bitmapResolution + ] + }; + for (const effectName in this.effects) { + if (!this.effects.hasOwnProperty(effectName)) continue; + props[effectName] = this.effects[effectName]; + } + this.renderer.updateDrawableProperties(this.drawableID, props); + if (this.visible) { + this.runtime.requestRedraw(); + } } + this.runtime.spriteInfoReport(this); } - this.runtime.spriteInfoReport(this); -}; - -/** - * Return the human-readable name for this rendered target, e.g., the sprite's name. - * @override - * @returns {string} Human-readable name. - */ -RenderedTarget.prototype.getName = function () { - return this.sprite.name; -}; -/** - * Return whether this rendered target is a sprite (not a clone, not the stage). - * @return {boolean} True if not a clone and not the stage. - */ -RenderedTarget.prototype.isSprite = function () { - return !this.isStage && this.isOriginal; -}; - -/** - * Return the rendered target's tight bounding box. - * Includes top, left, bottom, right attributes in Scratch coordinates. - * @return {?object} Tight bounding box, or null. - */ -RenderedTarget.prototype.getBounds = function () { - if (this.renderer) { - return this.runtime.renderer.getBounds(this.drawableID); + /** + * Return the human-readable name for this rendered target, e.g., the sprite's name. + * @override + * @returns {string} Human-readable name. + */ + getName () { + return this.sprite.name; } - return null; -}; -/** - * Return whether touching a point. - * @param {number} x X coordinate of test point. - * @param {number} y Y coordinate of test point. - * @return {boolean} True iff the rendered target is touching the point. - */ -RenderedTarget.prototype.isTouchingPoint = function (x, y) { - if (this.renderer) { - // @todo: Update once pick is in Scratch coordinates. - // Limits test to this Drawable, so this will return true - // even if the clone is obscured by another Drawable. - var pickResult = this.runtime.renderer.pick( - x + (this.runtime.constructor.STAGE_WIDTH / 2), - -y + (this.runtime.constructor.STAGE_HEIGHT / 2), - null, null, - [this.drawableID] - ); - return pickResult === this.drawableID; + /** + * Return whether this rendered target is a sprite (not a clone, not the stage). + * @return {boolean} True if not a clone and not the stage. + */ + isSprite () { + return !this.isStage && this.isOriginal; } - return false; -}; -/** - * Return whether touching a stage edge. - * @return {boolean} True iff the rendered target is touching the stage edge. - */ -RenderedTarget.prototype.isTouchingEdge = function () { - if (this.renderer) { - var stageWidth = this.runtime.constructor.STAGE_WIDTH; - var stageHeight = this.runtime.constructor.STAGE_HEIGHT; - var bounds = this.getBounds(); - if (bounds.left < -stageWidth / 2 || - bounds.right > stageWidth / 2 || - bounds.top > stageHeight / 2 || - bounds.bottom < -stageHeight / 2) { - return true; + /** + * Return the rendered target's tight bounding box. + * Includes top, left, bottom, right attributes in Scratch coordinates. + * @return {?object} Tight bounding box, or null. + */ + getBounds () { + if (this.renderer) { + return this.runtime.renderer.getBounds(this.drawableID); } + return null; } - return false; -}; -/** - * Return whether touching any of a named sprite's clones. - * @param {string} spriteName Name of the sprite. - * @return {boolean} True iff touching a clone of the sprite. - */ -RenderedTarget.prototype.isTouchingSprite = function (spriteName) { - var firstClone = this.runtime.getSpriteTargetByName(spriteName); - if (!firstClone || !this.renderer) { + /** + * Return whether touching a point. + * @param {number} x X coordinate of test point. + * @param {number} y Y coordinate of test point. + * @return {boolean} True iff the rendered target is touching the point. + */ + isTouchingPoint (x, y) { + if (this.renderer) { + // @todo: Update once pick is in Scratch coordinates. + // Limits test to this Drawable, so this will return true + // even if the clone is obscured by another Drawable. + const pickResult = this.runtime.renderer.pick( + x + (this.runtime.constructor.STAGE_WIDTH / 2), + -y + (this.runtime.constructor.STAGE_HEIGHT / 2), + null, null, + [this.drawableID] + ); + return pickResult === this.drawableID; + } return false; } - var drawableCandidates = firstClone.sprite.clones.map(function (clone) { - return clone.drawableID; - }); - return this.renderer.isTouchingDrawables( - this.drawableID, drawableCandidates); -}; -/** - * Return whether touching a color. - * @param {Array.} rgb [r,g,b], values between 0-255. - * @return {Promise.} True iff the rendered target is touching the color. - */ -RenderedTarget.prototype.isTouchingColor = function (rgb) { - if (this.renderer) { - return this.renderer.isTouchingColor(this.drawableID, rgb); + /** + * Return whether touching a stage edge. + * @return {boolean} True iff the rendered target is touching the stage edge. + */ + isTouchingEdge () { + if (this.renderer) { + const stageWidth = this.runtime.constructor.STAGE_WIDTH; + const stageHeight = this.runtime.constructor.STAGE_HEIGHT; + const bounds = this.getBounds(); + if (bounds.left < -stageWidth / 2 || + bounds.right > stageWidth / 2 || + bounds.top > stageHeight / 2 || + bounds.bottom < -stageHeight / 2) { + return true; + } + } + return false; } - return false; -}; -/** - * Return whether rendered target's color is touching a color. - * @param {object} targetRgb {Array.} [r,g,b], values between 0-255. - * @param {object} maskRgb {Array.} [r,g,b], values between 0-255. - * @return {Promise.} True iff the color is touching the color. - */ -RenderedTarget.prototype.colorIsTouchingColor = function (targetRgb, maskRgb) { - if (this.renderer) { - return this.renderer.isTouchingColor( - this.drawableID, - targetRgb, - maskRgb - ); + /** + * Return whether touching any of a named sprite's clones. + * @param {string} spriteName Name of the sprite. + * @return {boolean} True iff touching a clone of the sprite. + */ + isTouchingSprite (spriteName) { + const firstClone = this.runtime.getSpriteTargetByName(spriteName); + if (!firstClone || !this.renderer) { + return false; + } + const drawableCandidates = firstClone.sprite.clones.map(clone => clone.drawableID); + return this.renderer.isTouchingDrawables( + this.drawableID, drawableCandidates); } - return false; -}; -/** - * Move to the front layer. - */ -RenderedTarget.prototype.goToFront = function () { - if (this.renderer) { - this.renderer.setDrawableOrder(this.drawableID, Infinity); + /** + * Return whether touching a color. + * @param {Array.} rgb [r,g,b], values between 0-255. + * @return {Promise.} True iff the rendered target is touching the color. + */ + isTouchingColor (rgb) { + if (this.renderer) { + return this.renderer.isTouchingColor(this.drawableID, rgb); + } + return false; } -}; -/** - * Move back a number of layers. - * @param {number} nLayers How many layers to go back. - */ -RenderedTarget.prototype.goBackLayers = function (nLayers) { - if (this.renderer) { - this.renderer.setDrawableOrder(this.drawableID, -nLayers, true, 1); + /** + * Return whether rendered target's color is touching a color. + * @param {object} targetRgb {Array.} [r,g,b], values between 0-255. + * @param {object} maskRgb {Array.} [r,g,b], values between 0-255. + * @return {Promise.} True iff the color is touching the color. + */ + colorIsTouchingColor (targetRgb, maskRgb) { + if (this.renderer) { + return this.renderer.isTouchingColor( + this.drawableID, + targetRgb, + maskRgb + ); + } + return false; } -}; -/** - * Move behind some other rendered target. - * @param {!RenderedTarget} other Other rendered target to move behind. - */ -RenderedTarget.prototype.goBehindOther = function (other) { - if (this.renderer) { - var otherLayer = this.renderer.setDrawableOrder( - other.drawableID, 0, true); - this.renderer.setDrawableOrder(this.drawableID, otherLayer); + /** + * Move to the front layer. + */ + goToFront () { + if (this.renderer) { + this.renderer.setDrawableOrder(this.drawableID, Infinity); + } } -}; -/** - * Keep a desired position within a fence. - * @param {number} newX New desired X position. - * @param {number} newY New desired Y position. - * @param {object=} optFence Optional fence with left, right, top bottom. - * @return {Array.} Fenced X and Y coordinates. - */ -RenderedTarget.prototype.keepInFence = function (newX, newY, optFence) { - var fence = optFence; - if (!fence) { - fence = { - left: -this.runtime.constructor.STAGE_WIDTH / 2, - right: this.runtime.constructor.STAGE_WIDTH / 2, - top: this.runtime.constructor.STAGE_HEIGHT / 2, - bottom: -this.runtime.constructor.STAGE_HEIGHT / 2 - }; - } - var bounds = this.getBounds(); - if (!bounds) return; - // Adjust the known bounds to the target position. - bounds.left += (newX - this.x); - bounds.right += (newX - this.x); - bounds.top += (newY - this.y); - bounds.bottom += (newY - this.y); - // Find how far we need to move the target position. - var dx = 0; - var dy = 0; - if (bounds.left < fence.left) { - dx += fence.left - bounds.left; - } - if (bounds.right > fence.right) { - dx += fence.right - bounds.right; - } - if (bounds.top > fence.top) { - dy += fence.top - bounds.top; - } - if (bounds.bottom < fence.bottom) { - dy += fence.bottom - bounds.bottom; + /** + * Move back a number of layers. + * @param {number} nLayers How many layers to go back. + */ + goBackLayers (nLayers) { + if (this.renderer) { + this.renderer.setDrawableOrder(this.drawableID, -nLayers, true, 1); + } } - return [newX + dx, newY + dy]; -}; - -/** - * Make a clone, copying any run-time properties. - * If we've hit the global clone limit, returns null. - * @return {RenderedTarget} New clone. - */ -RenderedTarget.prototype.makeClone = function () { - if (!this.runtime.clonesAvailable() || this.isStage) { - return null; // Hit max clone limit, or this is the stage. - } - this.runtime.changeCloneCounter(1); - var newClone = this.sprite.createClone(); - // Copy all properties. - newClone.x = this.x; - newClone.y = this.y; - newClone.direction = this.direction; - newClone.draggable = this.draggable; - newClone.visible = this.visible; - newClone.size = this.size; - newClone.currentCostume = this.currentCostume; - newClone.rotationStyle = this.rotationStyle; - newClone.effects = JSON.parse(JSON.stringify(this.effects)); - newClone.variables = JSON.parse(JSON.stringify(this.variables)); - newClone.lists = JSON.parse(JSON.stringify(this.lists)); - newClone.initDrawable(); - newClone.updateAllDrawableProperties(); - // Place behind the current target. - newClone.goBehindOther(this); - return newClone; -}; -/** - * Called when the project receives a "green flag." - * For a rendered target, this clears graphic effects. - */ -RenderedTarget.prototype.onGreenFlag = function () { - this.clearEffects(); -}; - -/** - * Called when the project receives a "stop all" - * Stop all sounds and clear graphic effects. - */ -RenderedTarget.prototype.onStopAll = function () { - this.clearEffects(); - if (this.audioPlayer) { - this.audioPlayer.stopAllSounds(); - this.audioPlayer.clearEffects(); + /** + * Move behind some other rendered target. + * @param {!RenderedTarget} other Other rendered target to move behind. + */ + goBehindOther (other) { + if (this.renderer) { + const otherLayer = this.renderer.setDrawableOrder( + other.drawableID, 0, true); + this.renderer.setDrawableOrder(this.drawableID, otherLayer); + } } -}; -/** - * Post/edit sprite info. - * @param {object} data An object with sprite info data to set. - */ -RenderedTarget.prototype.postSpriteInfo = function (data) { - var force = data.hasOwnProperty('force') ? data.force : null; - if (data.hasOwnProperty('x')) { - this.setXY(data.x, this.y, force); - } - if (data.hasOwnProperty('y')) { - this.setXY(this.x, data.y, force); + /** + * Keep a desired position within a fence. + * @param {number} newX New desired X position. + * @param {number} newY New desired Y position. + * @param {object=} optFence Optional fence with left, right, top bottom. + * @return {Array.} Fenced X and Y coordinates. + */ + keepInFence (newX, newY, optFence) { + let fence = optFence; + if (!fence) { + fence = { + left: -this.runtime.constructor.STAGE_WIDTH / 2, + right: this.runtime.constructor.STAGE_WIDTH / 2, + top: this.runtime.constructor.STAGE_HEIGHT / 2, + bottom: -this.runtime.constructor.STAGE_HEIGHT / 2 + }; + } + const bounds = this.getBounds(); + if (!bounds) return; + // Adjust the known bounds to the target position. + bounds.left += (newX - this.x); + bounds.right += (newX - this.x); + bounds.top += (newY - this.y); + bounds.bottom += (newY - this.y); + // Find how far we need to move the target position. + let dx = 0; + let dy = 0; + if (bounds.left < fence.left) { + dx += fence.left - bounds.left; + } + if (bounds.right > fence.right) { + dx += fence.right - bounds.right; + } + if (bounds.top > fence.top) { + dy += fence.top - bounds.top; + } + if (bounds.bottom < fence.bottom) { + dy += fence.bottom - bounds.bottom; + } + return [newX + dx, newY + dy]; } - if (data.hasOwnProperty('direction')) { - this.setDirection(data.direction); + + /** + * Make a clone, copying any run-time properties. + * If we've hit the global clone limit, returns null. + * @return {RenderedTarget} New clone. + */ + makeClone () { + if (!this.runtime.clonesAvailable() || this.isStage) { + return null; // Hit max clone limit, or this is the stage. + } + this.runtime.changeCloneCounter(1); + const newClone = this.sprite.createClone(); + // Copy all properties. + newClone.x = this.x; + newClone.y = this.y; + newClone.direction = this.direction; + newClone.draggable = this.draggable; + newClone.visible = this.visible; + newClone.size = this.size; + newClone.currentCostume = this.currentCostume; + newClone.rotationStyle = this.rotationStyle; + newClone.effects = JSON.parse(JSON.stringify(this.effects)); + newClone.variables = JSON.parse(JSON.stringify(this.variables)); + newClone.lists = JSON.parse(JSON.stringify(this.lists)); + newClone.initDrawable(); + newClone.updateAllDrawableProperties(); + // Place behind the current target. + newClone.goBehindOther(this); + return newClone; } - if (data.hasOwnProperty('draggable')) { - this.setDraggable(data.draggable); + + /** + * Called when the project receives a "green flag." + * For a rendered target, this clears graphic effects. + */ + onGreenFlag () { + this.clearEffects(); } - if (data.hasOwnProperty('rotationStyle')) { - this.setRotationStyle(data.rotationStyle); + + /** + * Called when the project receives a "stop all" + * Stop all sounds and clear graphic effects. + */ + onStopAll () { + this.clearEffects(); + if (this.audioPlayer) { + this.audioPlayer.stopAllSounds(); + this.audioPlayer.clearEffects(); + } } - if (data.hasOwnProperty('visible')) { - this.setVisible(data.visible); + + /** + * Post/edit sprite info. + * @param {object} data An object with sprite info data to set. + */ + postSpriteInfo (data) { + const force = data.hasOwnProperty('force') ? data.force : null; + if (data.hasOwnProperty('x')) { + this.setXY(data.x, this.y, force); + } + if (data.hasOwnProperty('y')) { + this.setXY(this.x, data.y, force); + } + if (data.hasOwnProperty('direction')) { + this.setDirection(data.direction); + } + if (data.hasOwnProperty('draggable')) { + this.setDraggable(data.draggable); + } + if (data.hasOwnProperty('rotationStyle')) { + this.setRotationStyle(data.rotationStyle); + } + if (data.hasOwnProperty('visible')) { + this.setVisible(data.visible); + } } -}; -/** - * Put the sprite into the drag state. While in effect, setXY must be forced - */ -RenderedTarget.prototype.startDrag = function () { - this.dragging = true; -}; + /** + * Put the sprite into the drag state. While in effect, setXY must be forced + */ + startDrag () { + this.dragging = true; + } -/** - * Remove the sprite from the drag state. - */ -RenderedTarget.prototype.stopDrag = function () { - this.dragging = false; -}; + /** + * Remove the sprite from the drag state. + */ + stopDrag () { + this.dragging = false; + } -/** - * Serialize sprite info, used when emitting events about the sprite - * @returns {object} sprite data as a simple object - */ -RenderedTarget.prototype.toJSON = function () { - return { - id: this.id, - name: this.getName(), - isStage: this.isStage, - x: this.x, - y: this.y, - size: this.size, - direction: this.direction, - draggable: this.draggable, - costume: this.getCurrentCostume(), - costumes: this.getCostumes(), - sounds: this.getSounds(), - costumeCount: this.getCostumes().length, - visible: this.visible, - rotationStyle: this.rotationStyle - }; -}; + /** + * Serialize sprite info, used when emitting events about the sprite + * @returns {object} sprite data as a simple object + */ + toJSON () { + return { + id: this.id, + name: this.getName(), + isStage: this.isStage, + x: this.x, + y: this.y, + size: this.size, + direction: this.direction, + draggable: this.draggable, + costume: this.getCurrentCostume(), + costumes: this.getCostumes(), + sounds: this.getSounds(), + costumeCount: this.getCostumes().length, + visible: this.visible, + rotationStyle: this.rotationStyle + }; + } -/** - * Dispose, destroying any run-time properties. - */ -RenderedTarget.prototype.dispose = function () { - this.runtime.changeCloneCounter(-1); - if (this.renderer && this.drawableID !== null) { - this.renderer.destroyDrawable(this.drawableID); - if (this.visible) { - this.runtime.requestRedraw(); + /** + * Dispose, destroying any run-time properties. + */ + dispose () { + this.runtime.changeCloneCounter(-1); + if (this.renderer && this.drawableID !== null) { + this.renderer.destroyDrawable(this.drawableID); + if (this.visible) { + this.runtime.requestRedraw(); + } } } -}; +} module.exports = RenderedTarget; diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index ca0edc9fde..8dfed97ae6 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -1,452 +1,445 @@ -var EventEmitter = require('events'); -var util = require('util'); +const EventEmitter = require('events'); -var log = require('./util/log'); -var Runtime = require('./engine/runtime'); -var ScratchStorage = require('scratch-storage'); -var sb2import = require('./import/sb2import'); -var StringUtil = require('./util/string-util'); +const log = require('./util/log'); +const Runtime = require('./engine/runtime'); +const ScratchStorage = require('scratch-storage'); +const sb2import = require('./import/sb2import'); +const StringUtil = require('./util/string-util'); -var loadCostume = require('./import/load-costume.js'); -var loadSound = require('./import/load-sound.js'); +const loadCostume = require('./import/load-costume.js'); +const loadSound = require('./import/load-sound.js'); -var RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_']; +const RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_']; -var AssetType = ScratchStorage.AssetType; +const AssetType = ScratchStorage.AssetType; /** * Handles connections between blocks, stage, and extensions. * @constructor */ -var VirtualMachine = function () { - var instance = this; - // Bind event emitter and runtime to VM instance - EventEmitter.call(instance); +class VirtualMachine extends EventEmitter { + constructor () { + super(); + + const instance = this; + /** + * VM runtime, to store blocks, I/O devices, sprites/targets, etc. + * @type {!Runtime} + */ + instance.runtime = new Runtime(); + /** + * The "currently editing"/selected target ID for the VM. + * Block events from any Blockly workspace are routed to this target. + * @type {!string} + */ + instance.editingTarget = null; + // Runtime emits are passed along as VM emits. + instance.runtime.on(Runtime.SCRIPT_GLOW_ON, glowData => { + instance.emit(Runtime.SCRIPT_GLOW_ON, glowData); + }); + instance.runtime.on(Runtime.SCRIPT_GLOW_OFF, glowData => { + instance.emit(Runtime.SCRIPT_GLOW_OFF, glowData); + }); + instance.runtime.on(Runtime.BLOCK_GLOW_ON, glowData => { + instance.emit(Runtime.BLOCK_GLOW_ON, glowData); + }); + instance.runtime.on(Runtime.BLOCK_GLOW_OFF, glowData => { + instance.emit(Runtime.BLOCK_GLOW_OFF, glowData); + }); + instance.runtime.on(Runtime.PROJECT_RUN_START, () => { + instance.emit(Runtime.PROJECT_RUN_START); + }); + instance.runtime.on(Runtime.PROJECT_RUN_STOP, () => { + instance.emit(Runtime.PROJECT_RUN_STOP); + }); + instance.runtime.on(Runtime.VISUAL_REPORT, visualReport => { + instance.emit(Runtime.VISUAL_REPORT, visualReport); + }); + instance.runtime.on(Runtime.SPRITE_INFO_REPORT, spriteInfo => { + instance.emit(Runtime.SPRITE_INFO_REPORT, spriteInfo); + }); + + this.blockListener = this.blockListener.bind(this); + this.flyoutBlockListener = this.flyoutBlockListener.bind(this); + } + /** - * VM runtime, to store blocks, I/O devices, sprites/targets, etc. - * @type {!Runtime} + * Start running the VM - do this before anything else. */ - instance.runtime = new Runtime(); + start () { + this.runtime.start(); + } + /** - * The "currently editing"/selected target ID for the VM. - * Block events from any Blockly workspace are routed to this target. - * @type {!string} + * "Green flag" handler - start all threads starting with a green flag. */ - instance.editingTarget = null; - // Runtime emits are passed along as VM emits. - instance.runtime.on(Runtime.SCRIPT_GLOW_ON, function (glowData) { - instance.emit(Runtime.SCRIPT_GLOW_ON, glowData); - }); - instance.runtime.on(Runtime.SCRIPT_GLOW_OFF, function (glowData) { - instance.emit(Runtime.SCRIPT_GLOW_OFF, glowData); - }); - instance.runtime.on(Runtime.BLOCK_GLOW_ON, function (glowData) { - instance.emit(Runtime.BLOCK_GLOW_ON, glowData); - }); - instance.runtime.on(Runtime.BLOCK_GLOW_OFF, function (glowData) { - instance.emit(Runtime.BLOCK_GLOW_OFF, glowData); - }); - instance.runtime.on(Runtime.PROJECT_RUN_START, function () { - instance.emit(Runtime.PROJECT_RUN_START); - }); - instance.runtime.on(Runtime.PROJECT_RUN_STOP, function () { - instance.emit(Runtime.PROJECT_RUN_STOP); - }); - instance.runtime.on(Runtime.VISUAL_REPORT, function (visualReport) { - instance.emit(Runtime.VISUAL_REPORT, visualReport); - }); - instance.runtime.on(Runtime.SPRITE_INFO_REPORT, function (spriteInfo) { - instance.emit(Runtime.SPRITE_INFO_REPORT, spriteInfo); - }); - - this.blockListener = this.blockListener.bind(this); - this.flyoutBlockListener = this.flyoutBlockListener.bind(this); -}; - -/** - * Inherit from EventEmitter - */ -util.inherits(VirtualMachine, EventEmitter); - -/** - * Start running the VM - do this before anything else. - */ -VirtualMachine.prototype.start = function () { - this.runtime.start(); -}; - -/** - * "Green flag" handler - start all threads starting with a green flag. - */ -VirtualMachine.prototype.greenFlag = function () { - this.runtime.greenFlag(); -}; + greenFlag () { + this.runtime.greenFlag(); + } -/** - * Set whether the VM is in "turbo mode." - * When true, loops don't yield to redraw. - * @param {boolean} turboModeOn Whether turbo mode should be set. - */ -VirtualMachine.prototype.setTurboMode = function (turboModeOn) { - this.runtime.turboMode = !!turboModeOn; -}; + /** + * Set whether the VM is in "turbo mode." + * When true, loops don't yield to redraw. + * @param {boolean} turboModeOn Whether turbo mode should be set. + */ + setTurboMode (turboModeOn) { + this.runtime.turboMode = !!turboModeOn; + } -/** - * Set whether the VM is in 2.0 "compatibility mode." - * When true, ticks go at 2.0 speed (30 TPS). - * @param {boolean} compatibilityModeOn Whether compatibility mode is set. - */ -VirtualMachine.prototype.setCompatibilityMode = function (compatibilityModeOn) { - this.runtime.setCompatibilityMode(!!compatibilityModeOn); -}; + /** + * Set whether the VM is in 2.0 "compatibility mode." + * When true, ticks go at 2.0 speed (30 TPS). + * @param {boolean} compatibilityModeOn Whether compatibility mode is set. + */ + setCompatibilityMode (compatibilityModeOn) { + this.runtime.setCompatibilityMode(!!compatibilityModeOn); + } -/** - * Stop all threads and running activities. - */ -VirtualMachine.prototype.stopAll = function () { - this.runtime.stopAll(); -}; + /** + * Stop all threads and running activities. + */ + stopAll () { + this.runtime.stopAll(); + } -/** - * Clear out current running project data. - */ -VirtualMachine.prototype.clear = function () { - this.runtime.dispose(); - this.editingTarget = null; - this.emitTargetsUpdate(); -}; + /** + * Clear out current running project data. + */ + clear () { + this.runtime.dispose(); + this.editingTarget = null; + this.emitTargetsUpdate(); + } -/** - * Get data for playground. Data comes back in an emitted event. - */ -VirtualMachine.prototype.getPlaygroundData = function () { - var instance = this; - // Only send back thread data for the current editingTarget. - var threadData = this.runtime.threads.filter(function (thread) { - return thread.target === instance.editingTarget; - }); - // Remove the target key, since it's a circular reference. - var filteredThreadData = JSON.stringify(threadData, function (key, value) { - if (key === 'target') return; - return value; - }, 2); - this.emit('playgroundData', { - blocks: this.editingTarget.blocks, - threads: filteredThreadData - }); -}; + /** + * Get data for playground. Data comes back in an emitted event. + */ + getPlaygroundData () { + const instance = this; + // Only send back thread data for the current editingTarget. + const threadData = this.runtime.threads.filter(thread => thread.target === instance.editingTarget); + // Remove the target key, since it's a circular reference. + const filteredThreadData = JSON.stringify(threadData, (key, value) => { + if (key === 'target') return; + return value; + }, 2); + this.emit('playgroundData', { + blocks: this.editingTarget.blocks, + threads: filteredThreadData + }); + } -/** - * Post I/O data to the virtual devices. - * @param {?string} device Name of virtual I/O device. - * @param {object} data Any data object to post to the I/O device. - */ -VirtualMachine.prototype.postIOData = function (device, data) { - if (this.runtime.ioDevices[device]) { - this.runtime.ioDevices[device].postData(data); + /** + * Post I/O data to the virtual devices. + * @param {?string} device Name of virtual I/O device. + * @param {object} data Any data object to post to the I/O device. + */ + postIOData (device, data) { + if (this.runtime.ioDevices[device]) { + this.runtime.ioDevices[device].postData(data); + } } -}; -/** - * Load a project from a Scratch 2.0 JSON representation. - * @param {?string} json JSON string representing the project. - * @return {!Promise} Promise that resolves after targets are installed. - */ -VirtualMachine.prototype.loadProject = function (json) { - // @todo: Handle other formats, e.g., Scratch 1.4, Scratch 3.0. - return sb2import(json, this.runtime).then(function (targets) { - this.clear(); - for (var n = 0; n < targets.length; n++) { - if (targets[n] !== null) { - this.runtime.targets.push(targets[n]); - targets[n].updateAllDrawableProperties(); + /** + * Load a project from a Scratch 2.0 JSON representation. + * @param {?string} json JSON string representing the project. + * @return {!Promise} Promise that resolves after targets are installed. + */ + loadProject (json) { + // @todo: Handle other formats, e.g., Scratch 1.4, Scratch 3.0. + return sb2import(json, this.runtime).then(targets => { + this.clear(); + for (let n = 0; n < targets.length; n++) { + if (targets[n] !== null) { + this.runtime.targets.push(targets[n]); + targets[n].updateAllDrawableProperties(); + } } - } // Select the first target for editing, e.g., the first sprite. - this.editingTarget = this.runtime.targets[1]; + this.editingTarget = this.runtime.targets[1]; // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); - }.bind(this)); -}; + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); + }); + } -/** - * Load a project from the Scratch web site, by ID. - * @param {string} id - the ID of the project to download, as a string. - */ -VirtualMachine.prototype.downloadProjectId = function (id) { - if (!this.runtime.storage) { - log.error('No storage module present; cannot load project: ', id); - return; + /** + * Load a project from the Scratch web site, by ID. + * @param {string} id - the ID of the project to download, as a string. + */ + downloadProjectId (id) { + if (!this.runtime.storage) { + log.error('No storage module present; cannot load project: ', id); + return; + } + const vm = this; + const promise = this.runtime.storage.load(AssetType.Project, id); + promise.then(projectAsset => { + vm.loadProject(projectAsset.decodeText()); + }); } - var vm = this; - var promise = this.runtime.storage.load(AssetType.Project, id); - promise.then(function (projectAsset) { - vm.loadProject(projectAsset.decodeText()); - }); -}; -/** - * Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format. - * @param {string} json JSON string representing the sprite. - */ -VirtualMachine.prototype.addSprite2 = function (json) { + /** + * Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format. + * @param {string} json JSON string representing the sprite. + */ + addSprite2 (json) { // Select new sprite. - sb2import(json, this.runtime, true).then(function (targets) { - this.runtime.targets.push(targets[0]); - this.editingTarget = targets[0]; + sb2import(json, this.runtime, true).then(targets => { + this.runtime.targets.push(targets[0]); + this.editingTarget = targets[0]; // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); - }.bind(this)); -}; + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); + }); + } -/** - * Add a costume to the current editing target. - * @param {string} md5ext - the MD5 and extension of the costume to be loaded. - * @param {!object} costumeObject Object representing the costume. - * @property {int} skinId - the ID of the costume's render skin, once installed. - * @property {number} rotationCenterX - the X component of the costume's origin. - * @property {number} rotationCenterY - the Y component of the costume's origin. - * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume. - */ -VirtualMachine.prototype.addCostume = function (md5ext, costumeObject) { - loadCostume(md5ext, costumeObject, this.runtime).then(function () { - this.editingTarget.sprite.costumes.push(costumeObject); - this.editingTarget.setCostume( - this.editingTarget.sprite.costumes.length - 1 - ); - }.bind(this)); -}; + /** + * Add a costume to the current editing target. + * @param {string} md5ext - the MD5 and extension of the costume to be loaded. + * @param {!object} costumeObject Object representing the costume. + * @property {int} skinId - the ID of the costume's render skin, once installed. + * @property {number} rotationCenterX - the X component of the costume's origin. + * @property {number} rotationCenterY - the Y component of the costume's origin. + * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume. + */ + addCostume (md5ext, costumeObject) { + loadCostume(md5ext, costumeObject, this.runtime).then(() => { + this.editingTarget.sprite.costumes.push(costumeObject); + this.editingTarget.setCostume( + this.editingTarget.sprite.costumes.length - 1 + ); + }); + } -/** - * Add a sound to the current editing target. - * @param {!object} soundObject Object representing the costume. - * @returns {?Promise} - a promise that resolves when the sound has been decoded and added - */ -VirtualMachine.prototype.addSound = function (soundObject) { - return loadSound(soundObject, this.runtime).then(function () { - this.editingTarget.sprite.sounds.push(soundObject); - this.emitTargetsUpdate(); - }.bind(this)); -}; + /** + * Add a sound to the current editing target. + * @param {!object} soundObject Object representing the costume. + * @returns {?Promise} - a promise that resolves when the sound has been decoded and added + */ + addSound (soundObject) { + return loadSound(soundObject, this.runtime).then(() => { + this.editingTarget.sprite.sounds.push(soundObject); + this.emitTargetsUpdate(); + }); + } -/** - * Add a backdrop to the stage. - * @param {string} md5ext - the MD5 and extension of the backdrop to be loaded. - * @param {!object} backdropObject Object representing the backdrop. - * @property {int} skinId - the ID of the backdrop's render skin, once installed. - * @property {number} rotationCenterX - the X component of the backdrop's origin. - * @property {number} rotationCenterY - the Y component of the backdrop's origin. - * @property {number} [bitmapResolution] - the resolution scale for a bitmap backdrop. - */ -VirtualMachine.prototype.addBackdrop = function (md5ext, backdropObject) { - loadCostume(md5ext, backdropObject, this.runtime).then(function () { - var stage = this.runtime.getTargetForStage(); - stage.sprite.costumes.push(backdropObject); - stage.setCostume(stage.sprite.costumes.length - 1); - }.bind(this)); -}; + /** + * Add a backdrop to the stage. + * @param {string} md5ext - the MD5 and extension of the backdrop to be loaded. + * @param {!object} backdropObject Object representing the backdrop. + * @property {int} skinId - the ID of the backdrop's render skin, once installed. + * @property {number} rotationCenterX - the X component of the backdrop's origin. + * @property {number} rotationCenterY - the Y component of the backdrop's origin. + * @property {number} [bitmapResolution] - the resolution scale for a bitmap backdrop. + */ + addBackdrop (md5ext, backdropObject) { + loadCostume(md5ext, backdropObject, this.runtime).then(() => { + const stage = this.runtime.getTargetForStage(); + stage.sprite.costumes.push(backdropObject); + stage.setCostume(stage.sprite.costumes.length - 1); + }); + } -/** - * Rename a sprite. - * @param {string} targetId ID of a target whose sprite to rename. - * @param {string} newName New name of the sprite. - */ -VirtualMachine.prototype.renameSprite = function (targetId, newName) { - var target = this.runtime.getTargetById(targetId); - if (target) { - if (!target.isSprite()) { - throw new Error('Cannot rename non-sprite targets.'); - } - var sprite = target.sprite; - if (!sprite) { - throw new Error('No sprite associated with this target.'); - } - if (newName && RESERVED_NAMES.indexOf(newName) === -1) { - var names = this.runtime.targets.filter(function (runtimeTarget) { - return runtimeTarget.isSprite(); - }).map(function (runtimeTarget) { - return runtimeTarget.sprite.name; - }); - - sprite.name = StringUtil.unusedName(newName, names); + /** + * Rename a sprite. + * @param {string} targetId ID of a target whose sprite to rename. + * @param {string} newName New name of the sprite. + */ + renameSprite (targetId, newName) { + const target = this.runtime.getTargetById(targetId); + if (target) { + if (!target.isSprite()) { + throw new Error('Cannot rename non-sprite targets.'); + } + const sprite = target.sprite; + if (!sprite) { + throw new Error('No sprite associated with this target.'); + } + if (newName && RESERVED_NAMES.indexOf(newName) === -1) { + const names = this.runtime.targets + .filter(runtimeTarget => runtimeTarget.isSprite()) + .map(runtimeTarget => runtimeTarget.sprite.name); + + sprite.name = StringUtil.unusedName(newName, names); + } + this.emitTargetsUpdate(); + } else { + throw new Error('No target with the provided id.'); } - this.emitTargetsUpdate(); - } else { - throw new Error('No target with the provided id.'); } -}; -/** - * Delete a sprite and all its clones. - * @param {string} targetId ID of a target whose sprite to delete. - */ -VirtualMachine.prototype.deleteSprite = function (targetId) { - var target = this.runtime.getTargetById(targetId); - if (target) { - if (!target.isSprite()) { - throw new Error('Cannot delete non-sprite targets.'); - } - var sprite = target.sprite; - if (!sprite) { - throw new Error('No sprite associated with this target.'); - } - var currentEditingTarget = this.editingTarget; - for (var i = 0; i < sprite.clones.length; i++) { - var clone = sprite.clones[i]; - this.runtime.stopForTarget(sprite.clones[i]); - this.runtime.disposeTarget(sprite.clones[i]); - // Ensure editing target is switched if we are deleting it. - if (clone === currentEditingTarget) { - this.setEditingTarget(this.runtime.targets[0].id); + /** + * Delete a sprite and all its clones. + * @param {string} targetId ID of a target whose sprite to delete. + */ + deleteSprite (targetId) { + const target = this.runtime.getTargetById(targetId); + if (target) { + if (!target.isSprite()) { + throw new Error('Cannot delete non-sprite targets.'); + } + const sprite = target.sprite; + if (!sprite) { + throw new Error('No sprite associated with this target.'); } + const currentEditingTarget = this.editingTarget; + for (let i = 0; i < sprite.clones.length; i++) { + const clone = sprite.clones[i]; + this.runtime.stopForTarget(sprite.clones[i]); + this.runtime.disposeTarget(sprite.clones[i]); + // Ensure editing target is switched if we are deleting it. + if (clone === currentEditingTarget) { + this.setEditingTarget(this.runtime.targets[0].id); + } + } + // Sprite object should be deleted by GC. + this.emitTargetsUpdate(); + } else { + throw new Error('No target with the provided id.'); } - // Sprite object should be deleted by GC. - this.emitTargetsUpdate(); - } else { - throw new Error('No target with the provided id.'); } -}; - -/** - * Set the audio engine for the VM/runtime - * @param {!AudioEngine} audioEngine The audio engine to attach - */ -VirtualMachine.prototype.attachAudioEngine = function (audioEngine) { - this.runtime.attachAudioEngine(audioEngine); -}; -/** - * Set the renderer for the VM/runtime - * @param {!RenderWebGL} renderer The renderer to attach - */ -VirtualMachine.prototype.attachRenderer = function (renderer) { - this.runtime.attachRenderer(renderer); -}; + /** + * Set the audio engine for the VM/runtime + * @param {!AudioEngine} audioEngine The audio engine to attach + */ + attachAudioEngine (audioEngine) { + this.runtime.attachAudioEngine(audioEngine); + } -/** - * Set the storage module for the VM/runtime - * @param {!ScratchStorage} storage The storage module to attach - */ -VirtualMachine.prototype.attachStorage = function (storage) { - this.runtime.attachStorage(storage); -}; + /** + * Set the renderer for the VM/runtime + * @param {!RenderWebGL} renderer The renderer to attach + */ + attachRenderer (renderer) { + this.runtime.attachRenderer(renderer); + } -/** - * Handle a Blockly event for the current editing target. - * @param {!Blockly.Event} e Any Blockly event. - */ -VirtualMachine.prototype.blockListener = function (e) { - if (this.editingTarget) { - this.editingTarget.blocks.blocklyListen(e, this.runtime); + /** + * Set the storage module for the VM/runtime + * @param {!ScratchStorage} storage The storage module to attach + */ + attachStorage (storage) { + this.runtime.attachStorage(storage); } -}; -/** - * Handle a Blockly event for the flyout. - * @param {!Blockly.Event} e Any Blockly event. - */ -VirtualMachine.prototype.flyoutBlockListener = function (e) { - this.runtime.flyoutBlocks.blocklyListen(e, this.runtime); -}; + /** + * Handle a Blockly event for the current editing target. + * @param {!Blockly.Event} e Any Blockly event. + */ + blockListener (e) { + if (this.editingTarget) { + this.editingTarget.blocks.blocklyListen(e, this.runtime); + } + } -/** - * Set an editing target. An editor UI can use this function to switch - * between editing different targets, sprites, etc. - * After switching the editing target, the VM may emit updates - * to the list of targets and any attached workspace blocks - * (see `emitTargetsUpdate` and `emitWorkspaceUpdate`). - * @param {string} targetId Id of target to set as editing. - */ -VirtualMachine.prototype.setEditingTarget = function (targetId) { - // Has the target id changed? If not, exit. - if (targetId === this.editingTarget.id) { - return; + /** + * Handle a Blockly event for the flyout. + * @param {!Blockly.Event} e Any Blockly event. + */ + flyoutBlockListener (e) { + this.runtime.flyoutBlocks.blocklyListen(e, this.runtime); } - var target = this.runtime.getTargetById(targetId); - if (target) { - this.editingTarget = target; - // Emit appropriate UI updates. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(target); + + /** + * Set an editing target. An editor UI can use this function to switch + * between editing different targets, sprites, etc. + * After switching the editing target, the VM may emit updates + * to the list of targets and any attached workspace blocks + * (see `emitTargetsUpdate` and `emitWorkspaceUpdate`). + * @param {string} targetId Id of target to set as editing. + */ + setEditingTarget (targetId) { + // Has the target id changed? If not, exit. + if (targetId === this.editingTarget.id) { + return; + } + const target = this.runtime.getTargetById(targetId); + if (target) { + this.editingTarget = target; + // Emit appropriate UI updates. + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(target); + } } -}; -/** - * Emit metadata about available targets. - * An editor UI could use this to display a list of targets and show - * the currently editing one. - */ -VirtualMachine.prototype.emitTargetsUpdate = function () { - this.emit('targetsUpdate', { - // [[target id, human readable target name], ...]. - targetList: this.runtime.targets.filter(function (target) { - // Don't report clones. - return !target.hasOwnProperty('isOriginal') || target.isOriginal; - }).map(function (target) { - return target.toJSON(); - }), - // Currently editing target id. - editingTarget: this.editingTarget ? this.editingTarget.id : null - }); -}; + /** + * Emit metadata about available targets. + * An editor UI could use this to display a list of targets and show + * the currently editing one. + */ + emitTargetsUpdate () { + this.emit('targetsUpdate', { + // [[target id, human readable target name], ...]. + targetList: this.runtime.targets + .filter( + // Don't report clones. + target => !target.hasOwnProperty('isOriginal') || target.isOriginal + ).map( + target => target.toJSON() + ), + // Currently editing target id. + editingTarget: this.editingTarget ? this.editingTarget.id : null + }); + } -/** - * Emit an Blockly/scratch-blocks compatible XML representation - * of the current editing target's blocks. - */ -VirtualMachine.prototype.emitWorkspaceUpdate = function () { - this.emit('workspaceUpdate', { - xml: this.editingTarget.blocks.toXML() - }); -}; + /** + * Emit an Blockly/scratch-blocks compatible XML representation + * of the current editing target's blocks. + */ + emitWorkspaceUpdate () { + this.emit('workspaceUpdate', { + xml: this.editingTarget.blocks.toXML() + }); + } -/** - * Get a target id for a drawable id. Useful for interacting with the renderer - * @param {int} drawableId The drawable id to request the target id for - * @returns {?string} The target id, if found. Will also be null if the target found is the stage. - */ -VirtualMachine.prototype.getTargetIdForDrawableId = function (drawableId) { - var target = this.runtime.getTargetByDrawableId(drawableId); - if (target && target.hasOwnProperty('id') && target.hasOwnProperty('isStage') && !target.isStage) { - return target.id; + /** + * Get a target id for a drawable id. Useful for interacting with the renderer + * @param {int} drawableId The drawable id to request the target id for + * @returns {?string} The target id, if found. Will also be null if the target found is the stage. + */ + getTargetIdForDrawableId (drawableId) { + const target = this.runtime.getTargetByDrawableId(drawableId); + if (target && target.hasOwnProperty('id') && target.hasOwnProperty('isStage') && !target.isStage) { + return target.id; + } + return null; } - return null; -}; -/** - * Put a target into a "drag" state, during which its X/Y positions will be unaffected - * by blocks. - * @param {string} targetId The id for the target to put into a drag state - */ -VirtualMachine.prototype.startDrag = function (targetId) { - var target = this.runtime.getTargetById(targetId); - if (target) { - target.startDrag(); - this.setEditingTarget(target.id); + /** + * Put a target into a "drag" state, during which its X/Y positions will be unaffected + * by blocks. + * @param {string} targetId The id for the target to put into a drag state + */ + startDrag (targetId) { + const target = this.runtime.getTargetById(targetId); + if (target) { + target.startDrag(); + this.setEditingTarget(target.id); + } } -}; -/** - * Remove a target from a drag state, so blocks may begin affecting X/Y position again - * @param {string} targetId The id for the target to remove from the drag state - */ -VirtualMachine.prototype.stopDrag = function (targetId) { - var target = this.runtime.getTargetById(targetId); - if (target) target.stopDrag(); -}; + /** + * Remove a target from a drag state, so blocks may begin affecting X/Y position again + * @param {string} targetId The id for the target to remove from the drag state + */ + stopDrag (targetId) { + const target = this.runtime.getTargetById(targetId); + if (target) target.stopDrag(); + } -/** - * Post/edit sprite info for the current editing target. - * @param {object} data An object with sprite info data to set. - */ -VirtualMachine.prototype.postSpriteInfo = function (data) { - this.editingTarget.postSpriteInfo(data); -}; + /** + * Post/edit sprite info for the current editing target. + * @param {object} data An object with sprite info data to set. + */ + postSpriteInfo (data) { + this.editingTarget.postSpriteInfo(data); + } +} module.exports = VirtualMachine; From dc1712fd7144ebf32bc57e0e366107501b3e51a5 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Fri, 21 Apr 2017 13:39:52 -0700 Subject: [PATCH 1526/1971] Merge pull request #541 from cwillisf/storage-internal-constants Get `Asset` and `AssetType` from storage instance --- packages/scratch-vm/src/virtual-machine.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 8dfed97ae6..8e4e9bcfe9 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -2,7 +2,6 @@ const EventEmitter = require('events'); const log = require('./util/log'); const Runtime = require('./engine/runtime'); -const ScratchStorage = require('scratch-storage'); const sb2import = require('./import/sb2import'); const StringUtil = require('./util/string-util'); @@ -11,8 +10,6 @@ const loadSound = require('./import/load-sound.js'); const RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_']; -const AssetType = ScratchStorage.AssetType; - /** * Handles connections between blocks, stage, and extensions. * @constructor @@ -170,12 +167,13 @@ class VirtualMachine extends EventEmitter { * @param {string} id - the ID of the project to download, as a string. */ downloadProjectId (id) { - if (!this.runtime.storage) { + const storage = this.runtime.storage; + if (!storage) { log.error('No storage module present; cannot load project: ', id); return; } const vm = this; - const promise = this.runtime.storage.load(AssetType.Project, id); + const promise = storage.load(storage.AssetType.Project, id); promise.then(projectAsset => { vm.loadProject(projectAsset.decodeText()); }); From 79a1151197c712fe84a35f16e1e2bdad59481e9c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 27 Apr 2017 12:39:59 -0400 Subject: [PATCH 1527/1971] Merge pull request #547 from paulkaplan/fix-add-sprite-refresh Fix sprite not drawing after being added --- packages/scratch-vm/src/virtual-machine.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 8e4e9bcfe9..c0b762bddf 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -184,11 +184,12 @@ class VirtualMachine extends EventEmitter { * @param {string} json JSON string representing the sprite. */ addSprite2 (json) { - // Select new sprite. sb2import(json, this.runtime, true).then(targets => { this.runtime.targets.push(targets[0]); this.editingTarget = targets[0]; - // Update the VM user's knowledge of targets and blocks on the workspace. + this.editingTarget.updateAllDrawableProperties(); + + // Update the VM user's knowledge of targets and blocks on the workspace. this.emitTargetsUpdate(); this.emitWorkspaceUpdate(); this.runtime.setEditingTarget(this.editingTarget); From 5d208d505d1ac491b058307b6411cd2569aa4128 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Mon, 8 May 2017 10:19:27 -0700 Subject: [PATCH 1528/1971] Merge pull request #555 from rasmushaglund/clone-fix3 Copy _customState for now until blocks have init functions --- packages/scratch-vm/src/sprites/rendered-target.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index e6323ec73e..ad23003c7c 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -696,6 +696,7 @@ class RenderedTarget extends Target { newClone.effects = JSON.parse(JSON.stringify(this.effects)); newClone.variables = JSON.parse(JSON.stringify(this.variables)); newClone.lists = JSON.parse(JSON.stringify(this.lists)); + newClone._customState = JSON.parse(JSON.stringify(this._customState)); newClone.initDrawable(); newClone.updateAllDrawableProperties(); // Place behind the current target. From 3dd28659b2a5575aadd9688c0166ccf0e7236f70 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 10 May 2017 11:27:17 -0400 Subject: [PATCH 1529/1971] Merge pull request #558 from fsih/monitors Will run blocks with boxes checked next to them once per frame in the playground, once the code to emit checkbox events is in scratch-blocks --- packages/scratch-vm/src/engine/runtime.js | 29 +++++++++++++++++++--- packages/scratch-vm/src/virtual-machine.js | 13 ++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index f57a0e9f96..34c008a1ed 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -52,6 +52,13 @@ class Runtime extends EventEmitter { */ this.flyoutBlocks = new Blocks(); + /** + * Storage container for monitor blocks. + * These will execute on a target maybe + * @type {!Blocks} + */ + this.monitorBlocks = new Blocks(); + /** * Currently known editing target for the VM. * @type {?Target} @@ -362,11 +369,13 @@ class Runtime extends EventEmitter { * Create a thread and push it to the list of threads. * @param {!string} id ID of block that starts the stack. * @param {!Target} target Target to run thread on. + * @param {?boolean} optShowVisualReport true if the script should show speech bubble for its value * @return {!Thread} The newly created thread. */ - _pushThread (id, target) { + _pushThread (id, target, optShowVisualReport) { const thread = new Thread(id); thread.target = target; + thread.showVisualReport = optShowVisualReport; thread.pushStack(id); this.threads.push(thread); return thread; @@ -416,8 +425,11 @@ class Runtime extends EventEmitter { /** * Toggle a script. * @param {!string} topBlockId ID of block that starts the script. + * @param {?object} opts optional arguments to toggle script + * @param {?string} opts.target target ID for target to run script on. If not supplied, uses editing target. + * @param {?boolean} opts.showVisualReport true if the speech bubble should pop up on the block, false if not. */ - toggleScript (topBlockId) { + toggleScript (topBlockId, opts) { // Remove any existing thread. for (let i = 0; i < this.threads.length; i++) { if (this.threads[i].topBlock === topBlockId) { @@ -426,7 +438,10 @@ class Runtime extends EventEmitter { } } // Otherwise add it. - this._pushThread(topBlockId, this._editingTarget); + this._pushThread( + topBlockId, + opts && opts.target ? opts.target : this._editingTarget, + opts ? opts.showVisualReport : false); } /** @@ -632,6 +647,7 @@ class Runtime extends EventEmitter { } } this.redrawRequested = false; + this._pushMonitors(); const doneThreads = this.sequencer.stepThreads(); this._updateGlows(doneThreads); this._setThreadCount(this.threads.length + doneThreads.length); @@ -641,6 +657,13 @@ class Runtime extends EventEmitter { } } + /** + * Queue monitor blocks to sequencer to be run. + */ + _pushMonitors () { + this.monitorBlocks.runAllMonitored(this); + } + /** * Set the current editing target known by the runtime. * @param {!Target} editingTarget New editing target. diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index c0b762bddf..b39d74d8ae 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -58,6 +58,7 @@ class VirtualMachine extends EventEmitter { this.blockListener = this.blockListener.bind(this); this.flyoutBlockListener = this.flyoutBlockListener.bind(this); + this.monitorBlockListener = this.monitorBlockListener.bind(this); } /** @@ -344,6 +345,18 @@ class VirtualMachine extends EventEmitter { this.runtime.flyoutBlocks.blocklyListen(e, this.runtime); } + /** + * Handle a Blockly event for the flyout to be passed to the monitor container. + * @param {!Blockly.Event} e Any Blockly event. + */ + monitorBlockListener (e) { + // Filter events by type, since monitor blocks only need to listen to these events. + // Monitor blocks shouldn't be destroyed when flyout blocks are deleted. + if (['create', 'change'].indexOf(e.type) !== -1) { + this.runtime.monitorBlocks.blocklyListen(e, this.runtime); + } + } + /** * Set an editing target. An editor UI can use this function to switch * between editing different targets, sprites, etc. From d84cc0173af176047d4fe93119049f4ca557dbe3 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 11 May 2017 13:07:47 -0400 Subject: [PATCH 1530/1971] Merge pull request #552 from rschamp/feature/serialization Add project serialization --- .../scratch-vm/src/sprites/rendered-target.js | 18 ++- packages/scratch-vm/src/virtual-machine.js | 105 +++++++++++++++--- 2 files changed, 99 insertions(+), 24 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index ad23003c7c..1ffc00a1ec 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -764,11 +764,13 @@ class RenderedTarget extends Target { this.dragging = false; } + /** * Serialize sprite info, used when emitting events about the sprite - * @returns {object} sprite data as a simple object + * @returns {object} Sprite data as a simple object */ toJSON () { + const costumes = this.getCostumes(); return { id: this.id, name: this.getName(), @@ -778,12 +780,16 @@ class RenderedTarget extends Target { size: this.size, direction: this.direction, draggable: this.draggable, - costume: this.getCurrentCostume(), - costumes: this.getCostumes(), - sounds: this.getSounds(), - costumeCount: this.getCostumes().length, + currentCostume: this.currentCostume, + costume: costumes[this.currentCostume], + costumeCount: costumes.length, visible: this.visible, - rotationStyle: this.rotationStyle + rotationStyle: this.rotationStyle, + blocks: this.blocks._blocks, + variables: this.variables, + lists: this.lists, + costumes: costumes, + sounds: this.getSounds() }; } diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index b39d74d8ae..719bf6cc71 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -2,7 +2,8 @@ const EventEmitter = require('events'); const log = require('./util/log'); const Runtime = require('./engine/runtime'); -const sb2import = require('./import/sb2import'); +const sb2 = require('./serialization/sb2'); +const sb3 = require('./serialization/sb3'); const StringUtil = require('./util/string-util'); const loadCostume = require('./import/load-costume.js'); @@ -145,22 +146,7 @@ class VirtualMachine extends EventEmitter { */ loadProject (json) { // @todo: Handle other formats, e.g., Scratch 1.4, Scratch 3.0. - return sb2import(json, this.runtime).then(targets => { - this.clear(); - for (let n = 0; n < targets.length; n++) { - if (targets[n] !== null) { - this.runtime.targets.push(targets[n]); - targets[n].updateAllDrawableProperties(); - } - } - // Select the first target for editing, e.g., the first sprite. - this.editingTarget = this.runtime.targets[1]; - - // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); - }); + return this.fromJSON(json); } /** @@ -180,12 +166,95 @@ class VirtualMachine extends EventEmitter { }); } + /** + * @returns {string} Project in a Scratch 3.0 JSON representation. + */ + saveProjectSb3 () { + // @todo: Handle other formats, e.g., Scratch 1.4, Scratch 2.0. + return this.toJSON(); + } + + /** + * Export project as a Scratch 3.0 JSON representation. + * @return {string} Serialized state of the runtime. + */ + toJSON () { + return JSON.stringify(sb3.serialize(this.runtime)); + } + + /** + * Load a project from a Scratch JSON representation. + * @param {string} json JSON string representing a project. + * @returns {Promise} Promise that resolves after the project has loaded + */ + fromJSON (json) { + // Clear the current runtime + this.clear(); + + // Validate & parse + if (typeof json !== 'string') { + log.error('Failed to parse project. Non-string supplied to fromJSON.'); + return; + } + json = JSON.parse(json); + if (typeof json !== 'object') { + log.error('Failed to parse project. JSON supplied to fromJSON is not an object.'); + return; + } + + // Establish version, deserialize, and load into runtime + // @todo Support Scratch 1.4 + // @todo This is an extremely naïve / dangerous way of determining version. + // See `scratch-parser` for a more sophisticated validation + // methodology that should be adapted for use here + let deserializer; + if ((typeof json.meta !== 'undefined') && (typeof json.meta.semver !== 'undefined')) { + deserializer = sb3; + } else { + deserializer = sb2; + } + + return deserializer.deserialize(json, this.runtime).then(targets => { + this.clear(); + for (let n = 0; n < targets.length; n++) { + if (targets[n] !== null) { + this.runtime.targets.push(targets[n]); + targets[n].updateAllDrawableProperties(); + } + } + // Select the first target for editing, e.g., the first sprite. + if (this.runtime.targets.length > 1) { + this.editingTarget = this.runtime.targets[1]; + } else { + this.editingTarget = this.runtime.targets[0]; + } + + // Update the VM user's knowledge of targets and blocks on the workspace. + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); + }); + } + /** * Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format. * @param {string} json JSON string representing the sprite. + * @returns {Promise} Promise that resolves after the sprite is added */ addSprite2 (json) { - sb2import(json, this.runtime, true).then(targets => { + // Validate & parse + if (typeof json !== 'string') { + log.error('Failed to parse sprite. Non-string supplied to addSprite2.'); + return; + } + json = JSON.parse(json); + if (typeof json !== 'object') { + log.error('Failed to parse sprite. JSON supplied to addSprite2 is not an object.'); + return; + } + + // Select new sprite. + return sb2.deserialize(json, this.runtime, true).then(targets => { this.runtime.targets.push(targets[0]); this.editingTarget = targets[0]; this.editingTarget.updateAllDrawableProperties(); From 6a29fa4907caf35f653bcd65acb4df73ca3c66e8 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 12 May 2017 17:45:48 -0400 Subject: [PATCH 1531/1971] Merge pull request #563 from rschamp/performance/spriteInfoUpdate Stop emitting SPRITE_INFO_REPORT --- packages/scratch-vm/src/engine/runtime.js | 37 ++++++++++++------- .../scratch-vm/src/sprites/rendered-target.js | 14 +++---- packages/scratch-vm/src/virtual-machine.js | 37 +++++++++---------- 3 files changed, 48 insertions(+), 40 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 34c008a1ed..2708ebe7e9 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -104,6 +104,13 @@ class Runtime extends EventEmitter { */ this._cloneCounter = 0; + /** + * Flag to emit a targets update at the end of a step. When target data + * changes, this flag is set to true. + * @type {boolean} + */ + this._refreshTargets = false; + /** * Whether the project is in "turbo mode." * @type {Boolean} @@ -226,11 +233,11 @@ class Runtime extends EventEmitter { } /** - * Event name for sprite info report. + * Event name for targets update report. * @const {string} */ - static get SPRITE_INFO_REPORT () { - return 'SPRITE_INFO_REPORT'; + static get TARGETS_UPDATE () { + return 'TARGETS_UPDATE'; } /** @@ -638,6 +645,7 @@ class Runtime extends EventEmitter { * inactive threads after each iteration. */ _step () { + this._refreshTargets = false; // Find all edge-activated hats, and add them to threads to be evaluated. for (const hatType in this._hats) { if (!this._hats.hasOwnProperty(hatType)) continue; @@ -655,6 +663,7 @@ class Runtime extends EventEmitter { // @todo: Only render when this.redrawRequested or clones rendered. this.renderer.draw(); } + if (this._refreshTargets) this.emit(Runtime.TARGETS_UPDATE); } /** @@ -673,7 +682,7 @@ class Runtime extends EventEmitter { // Script glows must be cleared. this._scriptGlowsPreviousFrame = []; this._updateGlows(); - this.spriteInfoReport(editingTarget); + this.requestTargetsUpdate(editingTarget); } /** @@ -809,16 +818,6 @@ class Runtime extends EventEmitter { this.emit(Runtime.VISUAL_REPORT, {id: blockId, value: String(value)}); } - /** - * Emit a sprite info report if the provided target is the original sprite - * @param {!Target} target Target to report sprite info for. - */ - spriteInfoReport (target) { - if (!target.isOriginal) return; - - this.emit(Runtime.SPRITE_INFO_REPORT, target.toJSON()); - } - /** * Get a target by its id. * @param {string} targetId Id of target to find. @@ -896,6 +895,16 @@ class Runtime extends EventEmitter { this.redrawRequested = true; } + /** + * Emit a targets update at the end of the step if the provided target is + * the original sprite + * @param {!Target} target Target requesting the targets update + */ + requestTargetsUpdate (target) { + if (!target.isOriginal) return; + this._refreshTargets = true; + } + /** * Set up timers to repeatedly step in a browser. */ diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 1ffc00a1ec..8489b37cb0 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -197,7 +197,7 @@ class RenderedTarget extends Target { this.y = y; } this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY); - this.runtime.spriteInfoReport(this); + this.runtime.requestTargetsUpdate(this); } /** @@ -240,7 +240,7 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } - this.runtime.spriteInfoReport(this); + this.runtime.requestTargetsUpdate(this); } /** @@ -250,7 +250,7 @@ class RenderedTarget extends Target { setDraggable (draggable) { if (this.isStage) return; this.draggable = !!draggable; - this.runtime.spriteInfoReport(this); + this.runtime.requestTargetsUpdate(this); } /** @@ -287,7 +287,7 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } - this.runtime.spriteInfoReport(this); + this.runtime.requestTargetsUpdate(this); } /** @@ -386,7 +386,7 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } - this.runtime.spriteInfoReport(this); + this.runtime.requestTargetsUpdate(this); } /** @@ -411,7 +411,7 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } - this.runtime.spriteInfoReport(this); + this.runtime.requestTargetsUpdate(this); } /** @@ -483,7 +483,7 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } - this.runtime.spriteInfoReport(this); + this.runtime.requestTargetsUpdate(this); } /** diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 719bf6cc71..79c16d2fc9 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -19,42 +19,41 @@ class VirtualMachine extends EventEmitter { constructor () { super(); - const instance = this; /** * VM runtime, to store blocks, I/O devices, sprites/targets, etc. * @type {!Runtime} */ - instance.runtime = new Runtime(); + this.runtime = new Runtime(); /** * The "currently editing"/selected target ID for the VM. * Block events from any Blockly workspace are routed to this target. * @type {!string} */ - instance.editingTarget = null; + this.editingTarget = null; // Runtime emits are passed along as VM emits. - instance.runtime.on(Runtime.SCRIPT_GLOW_ON, glowData => { - instance.emit(Runtime.SCRIPT_GLOW_ON, glowData); + this.runtime.on(Runtime.SCRIPT_GLOW_ON, glowData => { + this.emit(Runtime.SCRIPT_GLOW_ON, glowData); }); - instance.runtime.on(Runtime.SCRIPT_GLOW_OFF, glowData => { - instance.emit(Runtime.SCRIPT_GLOW_OFF, glowData); + this.runtime.on(Runtime.SCRIPT_GLOW_OFF, glowData => { + this.emit(Runtime.SCRIPT_GLOW_OFF, glowData); }); - instance.runtime.on(Runtime.BLOCK_GLOW_ON, glowData => { - instance.emit(Runtime.BLOCK_GLOW_ON, glowData); + this.runtime.on(Runtime.BLOCK_GLOW_ON, glowData => { + this.emit(Runtime.BLOCK_GLOW_ON, glowData); }); - instance.runtime.on(Runtime.BLOCK_GLOW_OFF, glowData => { - instance.emit(Runtime.BLOCK_GLOW_OFF, glowData); + this.runtime.on(Runtime.BLOCK_GLOW_OFF, glowData => { + this.emit(Runtime.BLOCK_GLOW_OFF, glowData); }); - instance.runtime.on(Runtime.PROJECT_RUN_START, () => { - instance.emit(Runtime.PROJECT_RUN_START); + this.runtime.on(Runtime.PROJECT_RUN_START, () => { + this.emit(Runtime.PROJECT_RUN_START); }); - instance.runtime.on(Runtime.PROJECT_RUN_STOP, () => { - instance.emit(Runtime.PROJECT_RUN_STOP); + this.runtime.on(Runtime.PROJECT_RUN_STOP, () => { + this.emit(Runtime.PROJECT_RUN_STOP); }); - instance.runtime.on(Runtime.VISUAL_REPORT, visualReport => { - instance.emit(Runtime.VISUAL_REPORT, visualReport); + this.runtime.on(Runtime.VISUAL_REPORT, visualReport => { + this.emit(Runtime.VISUAL_REPORT, visualReport); }); - instance.runtime.on(Runtime.SPRITE_INFO_REPORT, spriteInfo => { - instance.emit(Runtime.SPRITE_INFO_REPORT, spriteInfo); + this.runtime.on(Runtime.TARGETS_UPDATE, () => { + this.emitTargetsUpdate(); }); this.blockListener = this.blockListener.bind(this); From be5f54044c577bd580af22bd73353f7a7c1d9e7e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 18 May 2017 11:06:56 -0400 Subject: [PATCH 1532/1971] Merge pull request #568 from paulkaplan/hotfix/emit-update-on-add Fix update emitting for new costumes --- packages/scratch-vm/src/engine/runtime.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 2708ebe7e9..c4ea2b3581 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -645,7 +645,6 @@ class Runtime extends EventEmitter { * inactive threads after each iteration. */ _step () { - this._refreshTargets = false; // Find all edge-activated hats, and add them to threads to be evaluated. for (const hatType in this._hats) { if (!this._hats.hasOwnProperty(hatType)) continue; @@ -663,7 +662,10 @@ class Runtime extends EventEmitter { // @todo: Only render when this.redrawRequested or clones rendered. this.renderer.draw(); } - if (this._refreshTargets) this.emit(Runtime.TARGETS_UPDATE); + if (this._refreshTargets) { + this.emit(Runtime.TARGETS_UPDATE); + this._refreshTargets = false; + } } /** From 9de899da99fc9c6cd2ce059f93daca8089181c63 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 18 May 2017 15:40:11 -0400 Subject: [PATCH 1533/1971] Merge pull request #573 from paulkaplan/fix-sprite-renames Fix sprite rename incrementing when renamed to itself --- packages/scratch-vm/src/virtual-machine.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 79c16d2fc9..adbeba640c 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -329,9 +329,8 @@ class VirtualMachine extends EventEmitter { } if (newName && RESERVED_NAMES.indexOf(newName) === -1) { const names = this.runtime.targets - .filter(runtimeTarget => runtimeTarget.isSprite()) + .filter(runtimeTarget => runtimeTarget.isSprite() && runtimeTarget.id !== target.id) .map(runtimeTarget => runtimeTarget.sprite.name); - sprite.name = StringUtil.unusedName(newName, names); } this.emitTargetsUpdate(); From d223456ab84d2ebc2662b1b3034d834ecb917ab8 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 18 May 2017 17:05:37 -0400 Subject: [PATCH 1534/1971] Merge pull request #560 from fsih/updateMonitorsAction Fire update monitors action for checked monitors --- packages/scratch-vm/src/engine/runtime.js | 110 ++++++++++++++++++--- packages/scratch-vm/src/virtual-machine.js | 3 + 2 files changed, 98 insertions(+), 15 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index c4ea2b3581..92f98e4340 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -93,10 +93,10 @@ class Runtime extends EventEmitter { this._scriptGlowsPreviousFrame = []; /** - * Number of threads running during the previous frame + * Number of non-monitor threads running during the previous frame. * @type {number} */ - this._threadCount = 0; + this._nonMonitorThreadCount = 0; /** * Currently known number of clones, used to enforce clone limit. @@ -111,6 +111,11 @@ class Runtime extends EventEmitter { */ this._refreshTargets = false; + /** + * List of all monitors. + */ + this._monitorState = {}; + /** * Whether the project is in "turbo mode." * @type {Boolean} @@ -240,6 +245,14 @@ class Runtime extends EventEmitter { return 'TARGETS_UPDATE'; } + /** + * Event name for monitors update. + * @const {string} + */ + static get MONITORS_UPDATE () { + return 'MONITORS_UPDATE'; + } + /** * How rapidly we try to step threads by default, in ms. */ @@ -376,13 +389,22 @@ class Runtime extends EventEmitter { * Create a thread and push it to the list of threads. * @param {!string} id ID of block that starts the stack. * @param {!Target} target Target to run thread on. - * @param {?boolean} optShowVisualReport true if the script should show speech bubble for its value + * @param {?object} opts optional arguments + * @param {?boolean} opts.showVisualReport true if the script should show speech bubble for its value + * @param {?boolean} opts.updateMonitor true if the script should update a monitor value * @return {!Thread} The newly created thread. */ - _pushThread (id, target, optShowVisualReport) { + _pushThread (id, target, opts) { + opts = Object.assign({ + showVisualReport: false, + updateMonitor: false + }, opts); + const thread = new Thread(id); thread.target = target; - thread.showVisualReport = optShowVisualReport; + thread.showVisualReport = opts.showVisualReport; + thread.updateMonitor = opts.updateMonitor; + thread.pushStack(id); this.threads.push(thread); return thread; @@ -411,6 +433,8 @@ class Runtime extends EventEmitter { _restartThread (thread) { const newThread = new Thread(thread.topBlock); newThread.target = thread.target; + newThread.showVisualReport = thread.showVisualReport; + newThread.updateMonitor = thread.updateMonitor; newThread.pushStack(thread.topBlock); const i = this.threads.indexOf(thread); if (i > -1) { @@ -435,8 +459,14 @@ class Runtime extends EventEmitter { * @param {?object} opts optional arguments to toggle script * @param {?string} opts.target target ID for target to run script on. If not supplied, uses editing target. * @param {?boolean} opts.showVisualReport true if the speech bubble should pop up on the block, false if not. + * @param {?boolean} opts.updateMonitor true if the monitor for this block should get updated. */ toggleScript (topBlockId, opts) { + opts = Object.assign({ + target: this._editingTarget, + showVisualReport: false, + updateMonitor: false + }, opts); // Remove any existing thread. for (let i = 0; i < this.threads.length; i++) { if (this.threads[i].topBlock === topBlockId) { @@ -445,10 +475,7 @@ class Runtime extends EventEmitter { } } // Otherwise add it. - this._pushThread( - topBlockId, - opts && opts.target ? opts.target : this._editingTarget, - opts ? opts.showVisualReport : false); + this._pushThread(topBlockId, opts.target, opts); } /** @@ -657,15 +684,39 @@ class Runtime extends EventEmitter { this._pushMonitors(); const doneThreads = this.sequencer.stepThreads(); this._updateGlows(doneThreads); - this._setThreadCount(this.threads.length + doneThreads.length); + // Add done threads so that even if a thread finishes within 1 frame, the green + // flag will still indicate that a script ran. + this._emitProjectRunStatus( + this.threads.length + doneThreads.length - + this._getMonitorThreadCount([...this.threads, ...doneThreads])); if (this.renderer) { // @todo: Only render when this.redrawRequested or clones rendered. this.renderer.draw(); } + if (this._refreshTargets) { this.emit(Runtime.TARGETS_UPDATE); this._refreshTargets = false; } + + // @todo(vm#570) only emit if monitors has changed since last time. + this.emit(Runtime.MONITORS_UPDATE, + Object.keys(this._monitorState).map(key => this._monitorState[key]) + ); + } + + /** + * Get the number of threads in the given array that are monitor threads (threads + * that update monitor values, and don't count as running a script). + * @param {!Array.} threads The set of threads to look through. + * @return {number} The number of monitor threads in threads. + */ + _getMonitorThreadCount (threads) { + let count = 0; + threads.forEach(thread => { + if (thread.updateMonitor) count++; + }); + return count; } /** @@ -760,16 +811,16 @@ class Runtime extends EventEmitter { * Emit run start/stop after each tick. Emits when `this.threads.length` goes * between non-zero and zero * - * @param {number} threadCount The new threadCount + * @param {number} nonMonitorThreadCount The new nonMonitorThreadCount */ - _setThreadCount (threadCount) { - if (this._threadCount === 0 && threadCount > 0) { + _emitProjectRunStatus (nonMonitorThreadCount) { + if (this._nonMonitorThreadCount === 0 && nonMonitorThreadCount > 0) { this.emit(Runtime.PROJECT_RUN_START); } - if (this._threadCount > 0 && threadCount === 0) { + if (this._nonMonitorThreadCount > 0 && nonMonitorThreadCount === 0) { this.emit(Runtime.PROJECT_RUN_STOP); } - this._threadCount = threadCount; + this._nonMonitorThreadCount = nonMonitorThreadCount; } /** @@ -820,6 +871,35 @@ class Runtime extends EventEmitter { this.emit(Runtime.VISUAL_REPORT, {id: blockId, value: String(value)}); } + /** + * Add a monitor to the state. If the monitor already exists in the state, + * overwrites it. + * @param {!object} monitor Monitor to add. + */ + requestAddMonitor (monitor) { + this._monitorState[monitor.id] = monitor; + } + + /** + * Update a monitor in the state. Does nothing if the monitor does not already + * exist in the state. + * @param {!object} monitor Monitor to update. + */ + requestUpdateMonitor (monitor) { + if (this._monitorState.hasOwnProperty(monitor.id)) { + this._monitorState[monitor.id] = Object.assign({}, this._monitorState[monitor.id], monitor); + } + } + + /** + * Removes a monitor from the state. Does nothing if the monitor already does + * not exist in the state. + * @param {!object} monitorId ID of the monitor to remove. + */ + requestRemoveMonitor (monitorId) { + delete this._monitorState[monitorId]; + } + /** * Get a target by its id. * @param {string} targetId Id of target to find. diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index adbeba640c..cf4d0e6362 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -55,6 +55,9 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.TARGETS_UPDATE, () => { this.emitTargetsUpdate(); }); + this.runtime.on(Runtime.MONITORS_UPDATE, monitorList => { + this.emit(Runtime.MONITORS_UPDATE, monitorList); + }); this.blockListener = this.blockListener.bind(this); this.flyoutBlockListener = this.flyoutBlockListener.bind(this); From c82d1cee6352c039b11f1bf30492867d6420c295 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 22 May 2017 15:19:20 -0400 Subject: [PATCH 1535/1971] Merge pull request #569 from paulkaplan/feature/delete-costume-sounds Add public API for deleting costumes and sounds --- .../scratch-vm/src/sprites/rendered-target.js | 34 +++++++++++++++++++ packages/scratch-vm/src/virtual-machine.js | 16 +++++++++ 2 files changed, 50 insertions(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 8489b37cb0..0bc5cff6ec 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -389,6 +389,40 @@ class RenderedTarget extends Target { this.runtime.requestTargetsUpdate(this); } + /** + * Delete a costume by index. + * @param {number} index Costume index to be deleted + */ + deleteCostume (index) { + const originalCostumeCount = this.sprite.costumes.length; + if (originalCostumeCount === 1) return; + + this.sprite.costumes = this.sprite.costumes + .slice(0, index) + .concat(this.sprite.costumes.slice(index + 1)); + + if (index === this.currentCostume && index === originalCostumeCount - 1) { + this.setCostume(index - 1); + } else if (index < this.currentCostume) { + this.setCostume(this.currentCostume - 1); + } else { + this.setCostume(this.currentCostume); + } + + this.runtime.requestTargetsUpdate(this); + } + + /** + * Delete a sound by index. + * @param {number} index Sound index to be deleted + */ + deleteSound (index) { + this.sprite.sounds = this.sprite.sounds + .slice(0, index) + .concat(this.sprite.sounds.slice(index + 1)); + this.runtime.requestTargetsUpdate(this); + } + /** * Update the rotation style. * @param {!string} rotationStyle New rotation style. diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index cf4d0e6362..fef841a6a9 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -286,6 +286,14 @@ class VirtualMachine extends EventEmitter { }); } + /** + * Delete a costume from the current editing target. + * @param {int} costumeIndex - the index of the costume to be removed. + */ + deleteCostume (costumeIndex) { + this.editingTarget.deleteCostume(costumeIndex); + } + /** * Add a sound to the current editing target. * @param {!object} soundObject Object representing the costume. @@ -298,6 +306,14 @@ class VirtualMachine extends EventEmitter { }); } + /** + * Delete a sound from the current editing target. + * @param {int} soundIndex - the index of the sound to be removed. + */ + deleteSound (soundIndex) { + this.editingTarget.deleteSound(soundIndex); + } + /** * Add a backdrop to the stage. * @param {string} md5ext - the MD5 and extension of the backdrop to be loaded. From 0ba426bb7a631807b8246f40e7a0088bf3bc65f7 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 24 May 2017 14:43:28 -0700 Subject: [PATCH 1536/1971] Merge pull request #549 from cwillisf/device-manager-client Device manager client --- packages/scratch-vm/src/engine/runtime.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 92f98e4340..c938c37ed4 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -5,6 +5,7 @@ const Thread = require('./thread'); // Virtual I/O devices. const Clock = require('../io/clock'); +const DeviceManager = require('../io/deviceManager'); const Keyboard = require('../io/keyboard'); const Mouse = require('../io/mouse'); @@ -160,6 +161,7 @@ class Runtime extends EventEmitter { /** @type {Object.} */ this.ioDevices = { clock: new Clock(), + deviceManager: new DeviceManager(), keyboard: new Keyboard(this), mouse: new Mouse(this) }; From 69c66152b5f31b63f95d9af442b811472a1873b9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 30 May 2017 09:26:37 -0400 Subject: [PATCH 1537/1971] Merge pull request #581 from paulkaplan/feature/variable-persistence Add variable creation and variable serialization --- packages/scratch-vm/src/virtual-machine.js | 24 +++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index fef841a6a9..19a4a8dd4e 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -491,9 +491,18 @@ class VirtualMachine extends EventEmitter { * of the current editing target's blocks. */ emitWorkspaceUpdate () { - this.emit('workspaceUpdate', { - xml: this.editingTarget.blocks.toXML() - }); + // @todo Include variables scoped to editing target also. + const variableMap = this.runtime.getTargetForStage().variables; + const variables = Object.keys(variableMap).map(k => variableMap[k]); + + const xmlString = ` + + ${variables.map(v => v.toXML()).join()} + + ${this.editingTarget.blocks.toXML()} + `; + + this.emit('workspaceUpdate', {xml: xmlString}); } /** @@ -538,6 +547,15 @@ class VirtualMachine extends EventEmitter { postSpriteInfo (data) { this.editingTarget.postSpriteInfo(data); } + + /** + * Create a variable by name. + * @todo this only creates global variables by putting them on the stage + * @param {string} name The name of the variable + */ + createVariable (name) { + this.runtime.getTargetForStage().lookupOrCreateVariable(name); + } } module.exports = VirtualMachine; From 3706f85dba61d540992f0bbe71a4b177d1eba471 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 30 May 2017 12:38:09 -0400 Subject: [PATCH 1538/1971] Merge pull request #574 from fsih/immutableState Only send update monitor events when changes happen --- packages/scratch-vm/src/engine/runtime.js | 35 ++++++++++++++--------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index c938c37ed4..7756ceb141 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -2,6 +2,7 @@ const EventEmitter = require('events'); const Sequencer = require('./sequencer'); const Blocks = require('./blocks'); const Thread = require('./thread'); +const {OrderedMap} = require('immutable'); // Virtual I/O devices. const Clock = require('../io/clock'); @@ -113,9 +114,14 @@ class Runtime extends EventEmitter { this._refreshTargets = false; /** - * List of all monitors. + * Ordered map of all monitors, which are MonitorReporter objects. */ - this._monitorState = {}; + this._monitorState = OrderedMap({}); + + /** + * Monitor state from last tick + */ + this._prevMonitorState = OrderedMap({}); /** * Whether the project is in "turbo mode." @@ -701,10 +707,10 @@ class Runtime extends EventEmitter { this._refreshTargets = false; } - // @todo(vm#570) only emit if monitors has changed since last time. - this.emit(Runtime.MONITORS_UPDATE, - Object.keys(this._monitorState).map(key => this._monitorState[key]) - ); + if (!this._prevMonitorState.equals(this._monitorState)) { + this.emit(Runtime.MONITORS_UPDATE, this._monitorState); + this._prevMonitorState = this._monitorState; + } } /** @@ -876,30 +882,33 @@ class Runtime extends EventEmitter { /** * Add a monitor to the state. If the monitor already exists in the state, * overwrites it. - * @param {!object} monitor Monitor to add. + * @param {!MonitorRecord} monitor Monitor to add. */ requestAddMonitor (monitor) { - this._monitorState[monitor.id] = monitor; + this._monitorState = this._monitorState.set(monitor.id, monitor); } /** * Update a monitor in the state. Does nothing if the monitor does not already * exist in the state. - * @param {!object} monitor Monitor to update. + * @param {!Map} monitor Monitor values to update. Values on the monitor with overwrite + * values on the old monitor with the same ID. If a value isn't defined on the new monitor, + * the old monitor will keep its old value. */ requestUpdateMonitor (monitor) { - if (this._monitorState.hasOwnProperty(monitor.id)) { - this._monitorState[monitor.id] = Object.assign({}, this._monitorState[monitor.id], monitor); + if (this._monitorState.has(monitor.get('id'))) { + this._monitorState = + this._monitorState.set(monitor.get('id'), this._monitorState.get(monitor.get('id')).merge(monitor)); } } /** * Removes a monitor from the state. Does nothing if the monitor already does * not exist in the state. - * @param {!object} monitorId ID of the monitor to remove. + * @param {!string} monitorId ID of the monitor to remove. */ requestRemoveMonitor (monitorId) { - delete this._monitorState[monitorId]; + this._monitorState = this._monitorState.delete(monitorId); } /** From 0854c01a40b2f275a8ac9307915ef475a1a53212 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 7 Jun 2017 09:57:36 -0700 Subject: [PATCH 1539/1971] Merge pull request #556 from cwillisf/wedo2-blocks Add WeDo 2.0 blocks --- packages/scratch-vm/src/engine/runtime.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 7756ceb141..80a4040024 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -20,7 +20,8 @@ const defaultBlockPackages = { scratch3_sound: require('../blocks/scratch3_sound'), scratch3_sensing: require('../blocks/scratch3_sensing'), scratch3_data: require('../blocks/scratch3_data'), - scratch3_procedures: require('../blocks/scratch3_procedures') + scratch3_procedures: require('../blocks/scratch3_procedures'), + scratch3_wedo2: require('../blocks/scratch3_wedo2') }; /** From f4300d21e58f457c85696f3c197a576ef6864e27 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 12 Jun 2017 08:41:20 -0400 Subject: [PATCH 1540/1971] Merge pull request #594 from paulkaplan/select-other-sprites-after-delete Select the last target instead of the first after deleting --- packages/scratch-vm/src/virtual-machine.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 19a4a8dd4e..12e6e7a777 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -364,6 +364,8 @@ class VirtualMachine extends EventEmitter { */ deleteSprite (targetId) { const target = this.runtime.getTargetById(targetId); + const targetIndexBeforeDelete = this.runtime.targets.map(t => t.id).indexOf(target.id); + if (target) { if (!target.isSprite()) { throw new Error('Cannot delete non-sprite targets.'); @@ -379,7 +381,8 @@ class VirtualMachine extends EventEmitter { this.runtime.disposeTarget(sprite.clones[i]); // Ensure editing target is switched if we are deleting it. if (clone === currentEditingTarget) { - this.setEditingTarget(this.runtime.targets[0].id); + const nextTargetIndex = Math.min(this.runtime.targets.length - 1, targetIndexBeforeDelete); + this.setEditingTarget(this.runtime.targets[nextTargetIndex].id); } } // Sprite object should be deleted by GC. From b13d2d184fb603be6d66182dd91764e3c42f0754 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 12 Jun 2017 09:45:07 -0400 Subject: [PATCH 1541/1971] Merge pull request #598 from cwillisf/improve-clone-cleanup Improve cleanup on clone disposal --- packages/scratch-vm/src/sprites/rendered-target.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 0bc5cff6ec..2fb0ddf9e1 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -832,6 +832,8 @@ class RenderedTarget extends Target { */ dispose () { this.runtime.changeCloneCounter(-1); + this.runtime.stopForTarget(this); + this.sprite.removeClone(this); if (this.renderer && this.drawableID !== null) { this.renderer.destroyDrawable(this.drawableID); if (this.visible) { From 4704500e46a0f97ed534fcdce9b6802dbb9f8ee1 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 15 Jun 2017 16:40:14 -0400 Subject: [PATCH 1542/1971] Merge pull request #611 from fsih/refreshWorkspace Add ability to refresh workspace for current editing target --- packages/scratch-vm/src/virtual-machine.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 12e6e7a777..90f87f7406 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -469,6 +469,17 @@ class VirtualMachine extends EventEmitter { } } + /** + * Repopulate the workspace with the blocks of the current editingTarget. This + * allows us to get around bugs like gui#413. + */ + refreshWorkspace () { + if (this.editingTarget) { + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); + } + } + /** * Emit metadata about available targets. * An editor UI could use this to display a list of targets and show From 9d4eb8f8962eeb0ecc172d8739a4a18dc6710989 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 23 Jun 2017 13:05:03 -0400 Subject: [PATCH 1543/1971] Merge pull request #614 from marisaleung/develop_cherry-picks Updated variables and var_fire event listener. --- packages/scratch-vm/src/virtual-machine.js | 23 +++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 90f87f7406..2ca4e21d88 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -62,6 +62,7 @@ class VirtualMachine extends EventEmitter { this.blockListener = this.blockListener.bind(this); this.flyoutBlockListener = this.flyoutBlockListener.bind(this); this.monitorBlockListener = this.monitorBlockListener.bind(this); + this.variableListener = this.variableListener.bind(this); } /** @@ -446,6 +447,19 @@ class VirtualMachine extends EventEmitter { } } + /** + * Handle a Blockly event for the variable map. + * @param {!Blockly.Event} e Any Blockly event. + */ + variableListener (e) { + // Filter events by type, since blocks only needs to listen to these + // var events. + if (['var_create', 'var_rename', 'var_delete'].indexOf(e.type) !== -1) { + this.runtime.getTargetForStage().blocks.blocklyListen(e, + this.runtime); + } + } + /** * Set an editing target. An editor UI can use this function to switch * between editing different targets, sprites, etc. @@ -561,15 +575,6 @@ class VirtualMachine extends EventEmitter { postSpriteInfo (data) { this.editingTarget.postSpriteInfo(data); } - - /** - * Create a variable by name. - * @todo this only creates global variables by putting them on the stage - * @param {string} name The name of the variable - */ - createVariable (name) { - this.runtime.getTargetForStage().lookupOrCreateVariable(name); - } } module.exports = VirtualMachine; From 11940b13aa68a20bfe97681eb6d232b5c71ea3e8 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Tue, 27 Jun 2017 22:37:30 -0700 Subject: [PATCH 1544/1971] Merge pull request #619 from cwillisf/clone-custom-state Clone custom state --- packages/scratch-vm/src/engine/runtime.js | 18 ++++++++++++++++++ .../scratch-vm/src/sprites/rendered-target.js | 15 ++++++++------- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 80a4040024..1e06121d07 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -968,6 +968,16 @@ class Runtime extends EventEmitter { return this._cloneCounter < Runtime.MAX_CLONES; } + /** + * Report that a new target has been created, possibly by cloning an existing target. + * @param {Target} newTarget - the newly created target. + * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any. + * @fires Runtime#targetWasCreated + */ + fireTargetWasCreated (newTarget, sourceTarget) { + this.emit('targetWasCreated', newTarget, sourceTarget); + } + /** * Get a target representing the Scratch stage, if one exists. * @return {?Target} The target, if found. @@ -1014,4 +1024,12 @@ class Runtime extends EventEmitter { } } +/** + * Event fired after a new target has been created, possibly by cloning an existing target. + * + * @event Runtime#targetWasCreated + * @param {Target} newTarget - the newly created target. + * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any. + */ + module.exports = Runtime; diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 2fb0ddf9e1..7f7fbe0d6f 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -4,14 +4,16 @@ const Target = require('../engine/target'); /** * Rendered target: instance of a sprite (clone), or the stage. - * @param {!Sprite} sprite Reference to the parent sprite. - * @param {Runtime} runtime Reference to the runtime. - * @constructor */ class RenderedTarget extends Target { + /** + * @param {!Sprite} sprite Reference to the parent sprite. + * @param {Runtime} runtime Reference to the runtime. + * @constructor + */ constructor (sprite, runtime) { - super(sprite.blocks); - this.runtime = runtime; + super(runtime, sprite.blocks); + /** * Reference to the sprite that this is a render of. * @type {!Sprite} @@ -19,7 +21,7 @@ class RenderedTarget extends Target { this.sprite = sprite; /** * Reference to the global renderer for this VM, if one exists. - * @type {?RenderWebGLWorker} + * @type {?RenderWebGL} */ this.renderer = null; if (this.runtime) { @@ -730,7 +732,6 @@ class RenderedTarget extends Target { newClone.effects = JSON.parse(JSON.stringify(this.effects)); newClone.variables = JSON.parse(JSON.stringify(this.variables)); newClone.lists = JSON.parse(JSON.stringify(this.lists)); - newClone._customState = JSON.parse(JSON.stringify(this._customState)); newClone.initDrawable(); newClone.updateAllDrawableProperties(); // Place behind the current target. From c1e3fbf949dfc5f8413904c48538849257283cec Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 30 Jun 2017 08:10:55 -0400 Subject: [PATCH 1545/1971] Merge pull request #629 from marisaleung/develop ChangeBlock uses id's of variable instead of name. --- packages/scratch-vm/src/engine/runtime.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 1e06121d07..7983a4565f 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -991,6 +991,14 @@ class Runtime extends EventEmitter { } } + /** + * Get the editing target. + * @return {?Target} The editing target. + */ + getEditingTarget () { + return this._editingTarget; + } + /** * Tell the runtime to request a redraw. * Use after a clone/sprite has completed some visible operation on the stage. From e0ce321916b8c4a7ee86918463b5c60ebb4dcb4d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 13 Jul 2017 09:00:19 -0400 Subject: [PATCH 1546/1971] Merge pull request #634 from paulkaplan/sound-costume-renaming Add public API for costume and sound renaming --- packages/scratch-vm/src/virtual-machine.js | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 2ca4e21d88..0cc3b7bb6d 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -287,6 +287,19 @@ class VirtualMachine extends EventEmitter { }); } + /** + * Rename a costume on the current editing target. + * @param {int} costumeIndex - the index of the costume to be renamed. + * @param {string} newName - the desired new name of the costume (will be modified if already in use). + */ + renameCostume (costumeIndex, newName) { + const usedNames = this.editingTarget.sprite.costumes + .filter((costume, index) => costumeIndex !== index) + .map(costume => costume.name); + this.editingTarget.sprite.costumes[costumeIndex].name = StringUtil.unusedName(newName, usedNames); + this.emitTargetsUpdate(); + } + /** * Delete a costume from the current editing target. * @param {int} costumeIndex - the index of the costume to be removed. @@ -307,6 +320,19 @@ class VirtualMachine extends EventEmitter { }); } + /** + * Rename a sound on the current editing target. + * @param {int} soundIndex - the index of the sound to be renamed. + * @param {string} newName - the desired new name of the sound (will be modified if already in use). + */ + renameSound (soundIndex, newName) { + const usedNames = this.editingTarget.sprite.sounds + .filter((sound, index) => soundIndex !== index) + .map(sound => sound.name); + this.editingTarget.sprite.sounds[soundIndex].name = StringUtil.unusedName(newName, usedNames); + this.emitTargetsUpdate(); + } + /** * Delete a sound from the current editing target. * @param {int} soundIndex - the index of the sound to be removed. From 3fbd33d01f86da218c2699524340c242999bdf01 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 19 Jul 2017 10:04:57 -0400 Subject: [PATCH 1547/1971] Merge pull request #641 from paulkaplan/fix-variable-importing Fix variable import scoping --- packages/scratch-vm/src/virtual-machine.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 0cc3b7bb6d..c3a0ba1916 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -545,8 +545,11 @@ class VirtualMachine extends EventEmitter { * of the current editing target's blocks. */ emitWorkspaceUpdate () { - // @todo Include variables scoped to editing target also. - const variableMap = this.runtime.getTargetForStage().variables; + const variableMap = Object.assign({}, + this.runtime.getTargetForStage().variables, + this.editingTarget.variables + ); + const variables = Object.keys(variableMap).map(k => variableMap[k]); const xmlString = ` From 60c5d00ff878c48a2c4056402dde26ea0cd85129 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 26 Jul 2017 08:41:47 -0400 Subject: [PATCH 1548/1971] Merge pull request #652 from paulkaplan/fix-asset-naming Use unused names when adding assets --- .../scratch-vm/src/sprites/rendered-target.js | 45 +++++++++++++++++++ packages/scratch-vm/src/virtual-machine.js | 14 ++---- 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 7f7fbe0d6f..1951700d33 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -1,5 +1,6 @@ const log = require('../util/log'); const MathUtil = require('../util/math-util'); +const StringUtil = require('../util/string-util'); const Target = require('../engine/target'); /** @@ -391,6 +392,28 @@ class RenderedTarget extends Target { this.runtime.requestTargetsUpdate(this); } + /** + * Add a costume, taking care to avoid duplicate names. + * @param {!object} costumeObject Object representing the costume. + */ + addCostume (costumeObject) { + const usedNames = this.sprite.costumes.map(costume => costume.name); + costumeObject.name = StringUtil.unusedName(costumeObject.name, usedNames); + this.sprite.costumes.push(costumeObject); + } + + /** + * Rename a costume, taking care to avoid duplicate names. + * @param {int} costumeIndex - the index of the costume to be renamed. + * @param {string} newName - the desired new name of the costume (will be modified if already in use). + */ + renameCostume (costumeIndex, newName) { + const usedNames = this.sprite.costumes + .filter((costume, index) => costumeIndex !== index) + .map(costume => costume.name); + this.sprite.costumes[costumeIndex].name = StringUtil.unusedName(newName, usedNames); + } + /** * Delete a costume by index. * @param {number} index Costume index to be deleted @@ -414,6 +437,28 @@ class RenderedTarget extends Target { this.runtime.requestTargetsUpdate(this); } + /** + * Add a sound, taking care to avoid duplicate names. + * @param {!object} soundObject Object representing the sound. + */ + addSound (soundObject) { + const usedNames = this.sprite.sounds.map(sound => sound.name); + soundObject.name = StringUtil.unusedName(soundObject.name, usedNames); + this.sprite.sounds.push(soundObject); + } + + /** + * Rename a sound, taking care to avoid duplicate names. + * @param {int} soundIndex - the index of the sound to be renamed. + * @param {string} newName - the desired new name of the sound (will be modified if already in use). + */ + renameSound (soundIndex, newName) { + const usedNames = this.sprite.sounds + .filter((sound, index) => soundIndex !== index) + .map(sound => sound.name); + this.sprite.sounds[soundIndex].name = StringUtil.unusedName(newName, usedNames); + } + /** * Delete a sound by index. * @param {number} index Sound index to be deleted diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index c3a0ba1916..01d6d0a7d8 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -280,7 +280,7 @@ class VirtualMachine extends EventEmitter { */ addCostume (md5ext, costumeObject) { loadCostume(md5ext, costumeObject, this.runtime).then(() => { - this.editingTarget.sprite.costumes.push(costumeObject); + this.editingTarget.addCostume(costumeObject); this.editingTarget.setCostume( this.editingTarget.sprite.costumes.length - 1 ); @@ -293,10 +293,7 @@ class VirtualMachine extends EventEmitter { * @param {string} newName - the desired new name of the costume (will be modified if already in use). */ renameCostume (costumeIndex, newName) { - const usedNames = this.editingTarget.sprite.costumes - .filter((costume, index) => costumeIndex !== index) - .map(costume => costume.name); - this.editingTarget.sprite.costumes[costumeIndex].name = StringUtil.unusedName(newName, usedNames); + this.editingTarget.renameCostume(costumeIndex, newName); this.emitTargetsUpdate(); } @@ -315,7 +312,7 @@ class VirtualMachine extends EventEmitter { */ addSound (soundObject) { return loadSound(soundObject, this.runtime).then(() => { - this.editingTarget.sprite.sounds.push(soundObject); + this.editingTarget.addSound(soundObject); this.emitTargetsUpdate(); }); } @@ -326,10 +323,7 @@ class VirtualMachine extends EventEmitter { * @param {string} newName - the desired new name of the sound (will be modified if already in use). */ renameSound (soundIndex, newName) { - const usedNames = this.editingTarget.sprite.sounds - .filter((sound, index) => soundIndex !== index) - .map(sound => sound.name); - this.editingTarget.sprite.sounds[soundIndex].name = StringUtil.unusedName(newName, usedNames); + this.editingTarget.renameSound(soundIndex, newName); this.emitTargetsUpdate(); } From b94d08229943ec9ea5d720286d33e9525a9b10d1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 26 Jul 2017 09:00:00 -0400 Subject: [PATCH 1549/1971] Merge pull request #653 from paulkaplan/sound-ids Use sound ID instead of md5 for playing sounds --- packages/scratch-vm/src/virtual-machine.js | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 01d6d0a7d8..762ef8c01a 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -327,6 +327,32 @@ class VirtualMachine extends EventEmitter { this.emitTargetsUpdate(); } + /** + * Get a sound buffer from the audio engine. + * @param {int} soundIndex - the index of the sound to be got. + * @return {AudioBuffer} the sound's audio buffer. + */ + getSoundBuffer (soundIndex) { + const id = this.editingTarget.sprite.sounds[soundIndex].soundId; + if (id && this.runtime && this.runtime.audioEngine) { + return this.runtime.audioEngine.getSoundBuffer(id); + } + return null; + } + + /** + * Update a sound buffer. + * @param {int} soundIndex - the index of the sound to be updated. + * @param {AudioBuffer} newBuffer - new audio buffer for the audio engine. + */ + updateSoundBuffer (soundIndex, newBuffer) { + const id = this.editingTarget.sprite.sounds[soundIndex].soundId; + if (id && this.runtime && this.runtime.audioEngine) { + this.runtime.audioEngine.updateSoundBuffer(id, newBuffer); + } + this.emitTargetsUpdate(); + } + /** * Delete a sound from the current editing target. * @param {int} soundIndex - the index of the sound to be removed. From 920dae1fca012111b020ac8d5dfd5edadf196a50 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Wed, 26 Jul 2017 17:44:02 -0400 Subject: [PATCH 1550/1971] Fix setting Infinity to direction (#612) * Create rendered-target.js * Update math-util.js * Update rendered-target.js * Update math-util.js * Update rendered-target.js --- packages/scratch-vm/src/sprites/rendered-target.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 1951700d33..bbf85231cd 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -231,6 +231,9 @@ class RenderedTarget extends Target { if (this.isStage) { return; } + if (!isFinite(direction)) { + return; + } // Keep direction between -179 and +180. this.direction = MathUtil.wrapClamp(direction, -179, 180); if (this.renderer) { From f2868fe79ad599c3c6b5e194cfa47e0a055f5f54 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 3 Aug 2017 17:38:03 -0400 Subject: [PATCH 1551/1971] Merge pull request #655 from fsih/edgeActivatedHats Activate the greater than hat block by clicking --- packages/scratch-vm/src/engine/runtime.js | 31 ++++++++++++++++------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 7983a4565f..b0a1b482a9 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -399,19 +399,19 @@ class Runtime extends EventEmitter { * @param {!string} id ID of block that starts the stack. * @param {!Target} target Target to run thread on. * @param {?object} opts optional arguments - * @param {?boolean} opts.showVisualReport true if the script should show speech bubble for its value + * @param {?boolean} opts.stackClick true if the script was activated by clicking on the stack * @param {?boolean} opts.updateMonitor true if the script should update a monitor value * @return {!Thread} The newly created thread. */ _pushThread (id, target, opts) { opts = Object.assign({ - showVisualReport: false, + stackClick: false, updateMonitor: false }, opts); const thread = new Thread(id); thread.target = target; - thread.showVisualReport = opts.showVisualReport; + thread.stackClick = opts.stackClick; thread.updateMonitor = opts.updateMonitor; thread.pushStack(id); @@ -442,7 +442,7 @@ class Runtime extends EventEmitter { _restartThread (thread) { const newThread = new Thread(thread.topBlock); newThread.target = thread.target; - newThread.showVisualReport = thread.showVisualReport; + newThread.stackClick = thread.stackClick; newThread.updateMonitor = thread.updateMonitor; newThread.pushStack(thread.topBlock); const i = this.threads.indexOf(thread); @@ -467,18 +467,28 @@ class Runtime extends EventEmitter { * @param {!string} topBlockId ID of block that starts the script. * @param {?object} opts optional arguments to toggle script * @param {?string} opts.target target ID for target to run script on. If not supplied, uses editing target. - * @param {?boolean} opts.showVisualReport true if the speech bubble should pop up on the block, false if not. * @param {?boolean} opts.updateMonitor true if the monitor for this block should get updated. + * @param {?boolean} opts.stackClick true if the user activated the stack by clicking, false if not. This + * determines whether we show a visual report when turning on the script. */ toggleScript (topBlockId, opts) { opts = Object.assign({ target: this._editingTarget, - showVisualReport: false, - updateMonitor: false + updateMonitor: false, + stackClick: false }, opts); // Remove any existing thread. for (let i = 0; i < this.threads.length; i++) { - if (this.threads[i].topBlock === topBlockId) { + // Toggling a script that's already running turns it off + if (this.threads[i].topBlock === topBlockId && this.threads[i].status !== Thread.STATUS_DONE) { + const blockContainer = opts.target.blocks; + const opcode = blockContainer.getOpcode(blockContainer.getBlock(topBlockId)); + + if (this.getIsEdgeActivatedHat(opcode) && this.threads[i].stackClick !== opts.stackClick) { + // Allow edge activated hat thread stack click to coexist with + // edge activated hat thread that runs every frame + continue; + } this._removeThread(this.threads[i]); return; } @@ -577,6 +587,7 @@ class Runtime extends EventEmitter { // any existing threads starting with the top block. for (let i = 0; i < instance.threads.length; i++) { if (instance.threads[i].topBlock === topBlockId && + !instance.threads[i].stackClick && // stack click threads and hat threads can coexist instance.threads[i].target === target) { instance._restartThread(instance.threads[i]); return; @@ -587,7 +598,9 @@ class Runtime extends EventEmitter { // give up if any threads with the top block are running. for (let j = 0; j < instance.threads.length; j++) { if (instance.threads[j].topBlock === topBlockId && - instance.threads[j].target === target) { + instance.threads[j].target === target && + !instance.threads[j].stackClick && // stack click threads and hat threads can coexist + instance.threads[j].status !== Thread.STATUS_DONE) { // Some thread is already running. return; } From 707601555191ad8d1f02e362bd8dde317988544f Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 4 Aug 2017 13:14:49 -0400 Subject: [PATCH 1552/1971] Merge pull request #637 from fsih/timerBlock Fix monitor not updating every frame --- packages/scratch-vm/src/engine/runtime.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index b0a1b482a9..122750fb75 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -467,14 +467,12 @@ class Runtime extends EventEmitter { * @param {!string} topBlockId ID of block that starts the script. * @param {?object} opts optional arguments to toggle script * @param {?string} opts.target target ID for target to run script on. If not supplied, uses editing target. - * @param {?boolean} opts.updateMonitor true if the monitor for this block should get updated. * @param {?boolean} opts.stackClick true if the user activated the stack by clicking, false if not. This * determines whether we show a visual report when turning on the script. */ toggleScript (topBlockId, opts) { opts = Object.assign({ target: this._editingTarget, - updateMonitor: false, stackClick: false }, opts); // Remove any existing thread. @@ -497,6 +495,24 @@ class Runtime extends EventEmitter { this._pushThread(topBlockId, opts.target, opts); } + /** + * Enqueue a script that when finished will update the monitor for the block. + * @param {!string} topBlockId ID of block that starts the script. + * @param {?string} optTarget target ID for target to run script on. If not supplied, uses editing target. + */ + addMonitorScript (topBlockId, optTarget) { + if (!optTarget) optTarget = this._editingTarget; + for (let i = 0; i < this.threads.length; i++) { + // Don't re-add the script if it's already running + if (this.threads[i].topBlock === topBlockId && this.threads[i].status !== Thread.STATUS_DONE && + this.threads[i].updateMonitor) { + return; + } + } + // Otherwise add it. + this._pushThread(topBlockId, optTarget, {updateMonitor: true}); + } + /** * Run a function `f` for all scripts in a workspace. * `f` will be called with two parameters: From 93700105c03564fd4f9f2f77bb90f6383e9092a2 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 23 Aug 2017 09:51:38 -0400 Subject: [PATCH 1553/1971] Merge pull request #562 from bigeyex/feature/fix-deleted-clone-sequence-order Fix the next thread is skipped after clone deleted --- packages/scratch-vm/src/engine/runtime.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 122750fb75..a14f154691 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -662,6 +662,7 @@ class Runtime extends EventEmitter { continue; } if (this.threads[i].target === target) { + this.threads[i].isKilled = true; this._removeThread(this.threads[i]); } } From 89521dc2db02bcc18440fcb6bfe5086dc25a35f4 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 31 Aug 2017 13:43:41 -0400 Subject: [PATCH 1554/1971] Merge pull request #671 from fsih/setGetSvg Add methods to retrieve and update SVGs --- packages/scratch-vm/src/virtual-machine.js | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 762ef8c01a..61afda3869 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -361,6 +361,40 @@ class VirtualMachine extends EventEmitter { this.editingTarget.deleteSound(soundIndex); } + /** + * Get an SVG string from storage. + * @param {int} costumeIndex - the index of the costume to be got. + * @return {string} the costume's SVG string, or null if it's not an SVG costume. + */ + getCostumeSvg (costumeIndex) { + const id = this.editingTarget.sprite.costumes[costumeIndex].assetId; + if (id && this.runtime && this.runtime.storage && + this.runtime.storage.get(id).dataFormat === 'svg') { + return this.runtime.storage.get(id).decodeText(); + } + return null; + } + + /** + * Update a costume with the given SVG + * @param {int} costumeIndex - the index of the costume to be updated. + * @param {string} svg - new SVG for the renderer. + */ + updateSvg (costumeIndex, svg) { + const costume = this.editingTarget.sprite.costumes[costumeIndex]; + if (costume && this.runtime && this.runtime.renderer) { + const rotationCenter = [ + costume.rotationCenterX / costume.bitmapResolution, + costume.rotationCenterY / costume.bitmapResolution + ]; + + this.runtime.renderer.updateSVGSkin(costume.skinId, svg, rotationCenter); + } + // @todo: Also update storage in addition to renderer. Without storage, if you switch + // costumes and switch back, you will lose your changes in the paint editor. + // @todo: emitTargetsUpdate if we need to update the storage ID on the updated costume. + } + /** * Add a backdrop to the stage. * @param {string} md5ext - the MD5 and extension of the backdrop to be loaded. From 957cafece6f2642db4ffd4fa7f7bc616fc281484 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 6 Sep 2017 17:02:08 -0400 Subject: [PATCH 1555/1971] Merge pull request #673 from fsih/updateRotationCenter Update rotation center --- packages/scratch-vm/src/virtual-machine.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 61afda3869..6e16ef90ac 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -379,16 +379,15 @@ class VirtualMachine extends EventEmitter { * Update a costume with the given SVG * @param {int} costumeIndex - the index of the costume to be updated. * @param {string} svg - new SVG for the renderer. + * @param {number} rotationCenterX x of point about which the costume rotates, relative to its upper left corner + * @param {number} rotationCenterY y of point about which the costume rotates, relative to its upper left corner */ - updateSvg (costumeIndex, svg) { + updateSvg (costumeIndex, svg, rotationCenterX, rotationCenterY) { const costume = this.editingTarget.sprite.costumes[costumeIndex]; if (costume && this.runtime && this.runtime.renderer) { - const rotationCenter = [ - costume.rotationCenterX / costume.bitmapResolution, - costume.rotationCenterY / costume.bitmapResolution - ]; - - this.runtime.renderer.updateSVGSkin(costume.skinId, svg, rotationCenter); + costume.rotationCenterX = rotationCenterX; + costume.rotationCenterY = rotationCenterY; + this.runtime.renderer.updateSVGSkin(costume.skinId, svg, [rotationCenterX, rotationCenterY]); } // @todo: Also update storage in addition to renderer. Without storage, if you switch // costumes and switch back, you will lose your changes in the paint editor. From 92333232043ad1ab25d39d2306777b6fbfa3fafd Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 13 Sep 2017 11:57:13 -0400 Subject: [PATCH 1556/1971] Merge pull request #676 from paulkaplan/duplicate Add sprite duplication method --- .../scratch-vm/src/sprites/rendered-target.js | 27 +++++++++++++++++++ packages/scratch-vm/src/virtual-machine.js | 23 ++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index bbf85231cd..653bc4efd7 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -787,6 +787,33 @@ class RenderedTarget extends Target { return newClone; } + /** + * Make a duplicate using a duplicate sprite. + * @return {RenderedTarget} New clone. + */ + duplicate () { + return this.sprite.duplicate().then(newSprite => { + const newTarget = newSprite.createClone(); + // Copy all properties. + // @todo refactor with clone methods + newTarget.x = Math.random() * 400 / 2; + newTarget.y = Math.random() * 300 / 2; + newTarget.direction = this.direction; + newTarget.draggable = this.draggable; + newTarget.visible = this.visible; + newTarget.size = this.size; + newTarget.currentCostume = this.currentCostume; + newTarget.rotationStyle = this.rotationStyle; + newTarget.effects = JSON.parse(JSON.stringify(this.effects)); + newTarget.variables = JSON.parse(JSON.stringify(this.variables)); + newTarget.lists = JSON.parse(JSON.stringify(this.lists)); + newTarget.initDrawable(); + newTarget.updateAllDrawableProperties(); + newTarget.goBehindOther(this); + return newTarget; + }); + } + /** * Called when the project receives a "green flag." * For a rendered target, this clears graphic effects. diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 6e16ef90ac..23932e6b25 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -6,8 +6,8 @@ const sb2 = require('./serialization/sb2'); const sb3 = require('./serialization/sb3'); const StringUtil = require('./util/string-util'); -const loadCostume = require('./import/load-costume.js'); -const loadSound = require('./import/load-sound.js'); +const {loadCostume} = require('./import/load-costume.js'); +const {loadSound} = require('./import/load-sound.js'); const RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_']; @@ -472,6 +472,25 @@ class VirtualMachine extends EventEmitter { } } + /** + * Duplicate a sprite. + * @param {string} targetId ID of a target whose sprite to duplicate. + */ + duplicateSprite (targetId) { + const target = this.runtime.getTargetById(targetId); + if (!target) { + throw new Error('No target with the provided id.'); + } else if (!target.isSprite()) { + throw new Error('Cannot duplicate non-sprite targets.'); + } else if (!target.sprite) { + throw new Error('No sprite associated with this target.'); + } + target.duplicate().then(newTarget => { + this.runtime.targets.push(newTarget); + this.setEditingTarget(newTarget.id); + }); + } + /** * Set the audio engine for the VM/runtime * @param {!AudioEngine} audioEngine The audio engine to attach From 2aef6156e84f88723c5cc51df81b7e8a56c0c224 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Thu, 14 Sep 2017 13:34:18 -0700 Subject: [PATCH 1557/1971] Merge pull request #675 from cwillisf/feature/extensions Feature/extensions --- packages/scratch-vm/src/engine/runtime.js | 225 +++++++++++++++++- .../extension-support/extension-manager.js | 170 +++++++++++++ packages/scratch-vm/src/virtual-machine.js | 11 + 3 files changed, 403 insertions(+), 3 deletions(-) create mode 100644 packages/scratch-vm/src/extension-support/extension-manager.js diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index a14f154691..a36081d60c 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1,8 +1,11 @@ const EventEmitter = require('events'); -const Sequencer = require('./sequencer'); +const {OrderedMap} = require('immutable'); + +const ArgumentType = require('../extension-support/argument-type'); const Blocks = require('./blocks'); +const BlockType = require('../extension-support/block-type'); +const Sequencer = require('./sequencer'); const Thread = require('./thread'); -const {OrderedMap} = require('immutable'); // Virtual I/O devices. const Clock = require('../io/clock'); @@ -24,6 +27,53 @@ const defaultBlockPackages = { scratch3_wedo2: require('../blocks/scratch3_wedo2') }; +/** + * Information used for converting Scratch argument types into scratch-blocks data. + * @type {object.}} + */ +const ArgumentTypeMap = (() => { + const map = {}; + map[ArgumentType.NUMBER] = { + shadowType: 'math_number', + fieldType: 'NUM' + }; + map[ArgumentType.STRING] = { + shadowType: 'text', + fieldType: 'TEXT' + }; + // @TODO: talk to Rachel & co. to figure out what goes here. Make it OK to not have a field. Add `check` support. + map[ArgumentType.BOOLEAN] = { + shadowType: '' + }; + return map; +})(); + +/** + * These constants are copied from scratch-blocks/core/constants.js + * @TODO find a way to require() these... maybe make a scratch-blocks/dist/constants.js or something like that? + * @readonly + * @enum {int} + */ +const ScratchBlocksConstants = { + /** + * ENUM for output shape: hexagonal (booleans/predicates). + * @const + */ + OUTPUT_SHAPE_HEXAGONAL: 1, + + /** + * ENUM for output shape: rounded (numbers). + * @const + */ + OUTPUT_SHAPE_ROUND: 2, + + /** + * ENUM for output shape: squared (any/all values; strings). + * @const + */ + OUTPUT_SHAPE_SQUARE: 3 +}; + /** * Manages targets, scripts, and the sequencer. * @constructor @@ -75,6 +125,13 @@ class Runtime extends EventEmitter { */ this._primitives = {}; + /** + * Map to look up all block information by extended opcode. + * @type {Array.} + * @private + */ + this._blockInfo = []; + /** * Map to look up hat blocks' metadata. * Keys are opcode for hat, values are metadata objects. @@ -262,6 +319,14 @@ class Runtime extends EventEmitter { return 'MONITORS_UPDATE'; } + /** + * Event name for reporting that an extension was added. + * @const {string} + */ + static get EXTENSION_ADDED () { + return 'EXTENSION_ADDED'; + } + /** * How rapidly we try to step threads by default, in ms. */ @@ -320,6 +385,160 @@ class Runtime extends EventEmitter { } } + /** + * Register the primitives provided by an extension. + * @param {ExtensionInfo} extensionInfo - information about the extension (id, blocks, etc.) + * @private + */ + _registerExtensionPrimitives (extensionInfo) { + const categoryInfo = { + id: extensionInfo.id, + name: extensionInfo.name, + color1: '#FF6680', + color2: '#FF4D6A', + color3: '#FF3355', + blocks: [] + }; + + this._blockInfo.push(categoryInfo); + + for (const blockInfo of extensionInfo.blocks) { + const convertedBlock = this._convertForScratchBlocks(blockInfo, categoryInfo); + const opcode = convertedBlock.json.type; + categoryInfo.blocks.push(convertedBlock); + this._primitives[opcode] = convertedBlock.info.func; + } + + this.emit(Runtime.EXTENSION_ADDED, categoryInfo.blocks); + } + + /** + * Convert BlockInfo into scratch-blocks JSON & XML, and generate a proxy function. + * @param {BlockInfo} blockInfo - the block to convert + * @param {CategoryInfo} categoryInfo - the category for this block + * @returns {{info: BlockInfo, json: object, xml: string}} - the converted & original block information + * @private + */ + _convertForScratchBlocks (blockInfo, categoryInfo) { + const extendedOpcode = `${categoryInfo.id}.${blockInfo.opcode}`; + const blockJSON = { + type: extendedOpcode, + inputsInline: true, + category: categoryInfo.name, + colour: categoryInfo.color1, + colourSecondary: categoryInfo.color2, + colorTertiary: categoryInfo.color3, + args0: [] + }; + + const inputList = []; + + // TODO: store this somewhere so that we can map args appropriately after translation. + // This maps an arg name to its relative position in the original (usually English) block text. + // When displaying a block in another language we'll need to run a `replace` action similar to the one below, + // but each `[ARG]` will need to be replaced with the number in this map instead of `args0.length`. + const argsMap = {}; + + blockJSON.message0 = blockInfo.text.replace(/\[(.+?)]/g, (match, placeholder) => { + + // Sanitize the placeholder to ensure valid XML + placeholder = placeholder.replace(/[<"&]/, '_'); + + blockJSON.args0.push({ + type: 'input_value', + name: placeholder + }); + + // scratch-blocks uses 1-based argument indexing + const argNum = blockJSON.args0.length; + argsMap[placeholder] = argNum; + + const argInfo = blockInfo.arguments[placeholder] || {}; + const argTypeInfo = ArgumentTypeMap[argInfo.type] || {}; + const defaultValue = (typeof argInfo.defaultValue === 'undefined' ? '' : argInfo.defaultValue.toString()); + inputList.push( + `` + + `` + + `${defaultValue}` + + '' + + '' + ); + + return `%${argNum}`; + }); + + switch (blockInfo.blockType) { + case BlockType.COMMAND: + blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE; + blockJSON.previousStatement = null; // null = available connection; undefined = hat + if (!blockInfo.isTerminal) { + blockJSON.nextStatement = null; // null = available connection; undefined = terminal + } + break; + case BlockType.REPORTER: + blockJSON.output = 'String'; // TODO: distinguish number & string here? + blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_ROUND; + break; + case BlockType.BOOLEAN: + blockJSON.output = 'Boolean'; + blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_HEXAGONAL; + break; + case BlockType.HAT: + blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE; + blockJSON.nextStatement = null; // null = available connection; undefined = terminal + break; + case BlockType.CONDITIONAL: + // Statement inputs get names like 'SUBSTACK', 'SUBSTACK2', 'SUBSTACK3', ... + for (let branchNum = 1; branchNum <= blockInfo.branchCount; ++branchNum) { + blockJSON[`message${branchNum}`] = '%1'; + blockJSON[`args${branchNum}`] = [{ + type: 'input_statement', + name: `SUBSTACK${branchNum > 1 ? branchNum : ''}` + }]; + } + blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE; + blockJSON.previousStatement = null; // null = available connection; undefined = hat + blockJSON.nextStatement = null; // null = available connection; undefined = terminal + break; + } + + if (blockInfo.isTerminal) { + delete blockJSON.nextStatement; + } + + const blockXML = `${inputList.join('')}`; + + return { + info: blockInfo, + json: blockJSON, + xml: blockXML + }; + } + + /** + * @returns {string} scratch-blocks XML description for all dynamic blocks, wrapped in elements. + */ + getBlocksXML () { + const xmlParts = []; + for (const categoryInfo of this._blockInfo) { + const {name, color1, color2} = categoryInfo; + xmlParts.push(``); + // @todo only add this label for user-loaded extensions? + xmlParts.push(`'); + } + return xmlParts.join('\n'); + } + + /** + * @returns {Array.} - an array containing the scratch-blocks JSON information for each dynamic block. + */ + getBlocksJSON () { + return this._blockInfo.reduce( + (result, categoryInfo) => result.concat(categoryInfo.blocks.map(blockInfo => blockInfo.json)), []); + } + /** * Retrieve the function associated with the given opcode. * @param {!string} opcode The opcode to look up. @@ -481,7 +700,7 @@ class Runtime extends EventEmitter { if (this.threads[i].topBlock === topBlockId && this.threads[i].status !== Thread.STATUS_DONE) { const blockContainer = opts.target.blocks; const opcode = blockContainer.getOpcode(blockContainer.getBlock(topBlockId)); - + if (this.getIsEdgeActivatedHat(opcode) && this.threads[i].stackClick !== opts.stackClick) { // Allow edge activated hat thread stack click to coexist with // edge activated hat thread that runs every frame diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js new file mode 100644 index 0000000000..62a7a0cf82 --- /dev/null +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -0,0 +1,170 @@ +const dispatch = require('../dispatch/central-dispatch'); +const log = require('../util/log'); + +const BlockType = require('./block-type'); + +/** + * @typedef {object} ArgumentInfo - Information about an extension block argument + * @property {ArgumentType} type - the type of value this argument can take + * @property {*|undefined} default - the default value of this argument (default: blank) + */ + +/** + * @typedef {object} BlockInfo - Information about an extension block + * @property {string} opcode - the block opcode + * @property {string|object} text - the human-readable text on this block + * @property {BlockType|undefined} blockType - the type of block (default: BlockType.COMMAND) + * @property {int|undefined} branchCount - the number of branches this block controls, if conditional (default: 0) + * @property {Boolean|undefined} isTerminal - true if this block ends a stack (default: false) + * @property {Boolean|undefined} blockAllThreads - true if all threads must wait for this block to run (default: false) + * @property {object.|undefined} arguments - information about this block's arguments, if any + * @property {string|Function|undefined} func - the method for this block on the extension service (default: opcode) + * @property {Array.|undefined} filter - the list of targets for which this block should appear (default: all) + */ + +/** + * @typedef {object} CategoryInfo - Information about a block category + * @property {string} id - the unique ID of this category + * @property {string} color1 - the primary color for this category, in '#rrggbb' format + * @property {string} color2 - the secondary color for this category, in '#rrggbb' format + * @property {string} color3 - the tertiary color for this category, in '#rrggbb' format + * @property {Array.} block - the blocks in this category + */ + +/** + * @typedef {object} PendingExtensionWorker - Information about an extension worker still initializing + * @property {string} extensionURL - the URL of the extension to be loaded by this worker + * @property {Function} resolve - function to call on successful worker startup + * @property {Function} reject - function to call on failed worker startup + */ + +class ExtensionManager { + constructor () { + /** + * The ID number to provide to the next extension worker. + * @type {int} + */ + this.nextExtensionWorker = 0; + + /** + * FIFO queue of extensions which have been requested but not yet loaded in a worker, + * along with promise resolution functions to call once the worker is ready or failed. + * + * @type {Array.} + */ + this.pendingExtensions = []; + + /** + * Map of worker ID to workers which have been allocated but have not yet finished initialization. + * @type {Array.} + */ + this.pendingWorkers = []; + + dispatch.setService('extensions', this).catch(e => { + log.error(`ExtensionManager was unable to register extension service: ${JSON.stringify(e)}`); + }); + } + + /** + * Load an extension by URL + * @param {string} extensionURL - the URL for the extension to load + * @returns {Promise} resolved once the extension is loaded and initialized or rejected on failure + */ + loadExtensionURL (extensionURL) { + return new Promise((resolve, reject) => { + // If we `require` this at the global level it breaks non-webpack targets, including tests + const ExtensionWorker = require('worker-loader?name=extension-worker.js!./extension-worker'); + + this.pendingExtensions.push({extensionURL, resolve, reject}); + dispatch.addWorker(new ExtensionWorker()); + }); + } + + allocateWorker () { + const id = this.nextExtensionWorker++; + const workerInfo = this.pendingExtensions.shift(); + this.pendingWorkers[id] = workerInfo; + return [id, workerInfo.extensionURL]; + } + + registerExtensionService (serviceName) { + dispatch.call(serviceName, 'getInfo').then(info => { + this._registerExtensionInfo(serviceName, info); + }); + } + + onWorkerInit (id, e) { + const workerInfo = this.pendingWorkers[id]; + delete this.pendingWorkers[id]; + if (e) { + workerInfo.reject(e); + } else { + workerInfo.resolve(id); + } + } + + _registerExtensionInfo (serviceName, extensionInfo) { + extensionInfo = this._prepareExtensionInfo(serviceName, extensionInfo); + dispatch.call('runtime', '_registerExtensionPrimitives', extensionInfo).catch(e => { + log.error(`Failed to register primitives for extension on service ${serviceName}: ${JSON.stringify(e)}`); + }); + } + + /** + * Modify the provided text as necessary to ensure that it may be used as an attribute value in valid XML. + * @param {string} text - the text to be sanitized + * @returns {string} - the sanitized text + * @private + */ + _sanitizeID (text) { + return text.toString().replace(/[<"&]/, '_'); + } + + /** + * Apply minor cleanup and defaults for optional extension fields. + * TODO: make the ID unique in cases where two copies of the same extension are loaded. + * @param {string} serviceName - the name of the service hosting this extension block + * @param {ExtensionInfo} extensionInfo - the extension info to be sanitized + * @returns {ExtensionInfo} - a new extension info object with cleaned-up values + * @private + */ + _prepareExtensionInfo (serviceName, extensionInfo) { + extensionInfo = Object.assign({}, extensionInfo); + extensionInfo.id = this._sanitizeID(extensionInfo.id); + extensionInfo.name = extensionInfo.name || extensionInfo.id; + extensionInfo.blocks = extensionInfo.blocks || []; + extensionInfo.targetTypes = extensionInfo.targetTypes || []; + extensionInfo.blocks = extensionInfo.blocks.reduce((result, blockInfo) => { + try { + result.push(this._prepareBlockInfo(serviceName, blockInfo)); + } catch (e) { + // TODO: more meaningful error reporting + log.error(`Skipping malformed block: ${JSON.stringify(e)}`); + } + return result; + }, []); + return extensionInfo; + } + + /** + * Apply defaults for optional block fields. + * @param {string} serviceName - the name of the service hosting this extension block + * @param {BlockInfo} blockInfo - the block info from the extension + * @returns {BlockInfo} - a new block info object which has values for all relevant optional fields. + * @private + */ + _prepareBlockInfo (serviceName, blockInfo) { + blockInfo = Object.assign({}, { + blockType: BlockType.COMMAND, + terminal: false, + blockAllThreads: false, + arguments: {} + }, blockInfo); + blockInfo.opcode = this._sanitizeID(blockInfo.opcode); + blockInfo.func = blockInfo.func ? this._sanitizeID(blockInfo.func) : blockInfo.opcode; + blockInfo.func = dispatch.call.bind(dispatch, serviceName, blockInfo.func); + return blockInfo; + } +} + +module.exports = ExtensionManager; diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 23932e6b25..721477f096 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -1,5 +1,7 @@ const EventEmitter = require('events'); +const centralDispatch = require('./dispatch/central-dispatch'); +const ExtensionManager = require('./extension-support/extension-manager'); const log = require('./util/log'); const Runtime = require('./engine/runtime'); const sb2 = require('./serialization/sb2'); @@ -24,6 +26,10 @@ class VirtualMachine extends EventEmitter { * @type {!Runtime} */ this.runtime = new Runtime(); + centralDispatch.setService('runtime', this.runtime).catch(e => { + log.error(`Failed to register runtime service: ${JSON.stringify(e)}`); + }); + /** * The "currently editing"/selected target ID for the VM. * Block events from any Blockly workspace are routed to this target. @@ -58,6 +64,11 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.MONITORS_UPDATE, monitorList => { this.emit(Runtime.MONITORS_UPDATE, monitorList); }); + this.runtime.on(Runtime.EXTENSION_ADDED, blocksInfo => { + this.emit(Runtime.EXTENSION_ADDED, blocksInfo); + }); + + this.extensionManager = new ExtensionManager(); this.blockListener = this.blockListener.bind(this); this.flyoutBlockListener = this.flyoutBlockListener.bind(this); From 4533d69f84d94eb0e32a9245a19a4b154678c96a Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Mon, 2 Oct 2017 18:48:35 -0400 Subject: [PATCH 1558/1971] Merge pull request #684 from cwillisf/allow-workerless-extensions Allow workerless extensions --- packages/scratch-vm/src/engine/runtime.js | 22 +++++++++++++------ .../extension-support/extension-manager.js | 13 +++++++++++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index a36081d60c..1b3641ab7b 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -33,6 +33,9 @@ const defaultBlockPackages = { */ const ArgumentTypeMap = (() => { const map = {}; + map[ArgumentType.COLOR] = { + shadowType: 'colour_picker' + }; map[ArgumentType.NUMBER] = { shadowType: 'math_number', fieldType: 'NUM' @@ -456,13 +459,18 @@ class Runtime extends EventEmitter { const argInfo = blockInfo.arguments[placeholder] || {}; const argTypeInfo = ArgumentTypeMap[argInfo.type] || {}; const defaultValue = (typeof argInfo.defaultValue === 'undefined' ? '' : argInfo.defaultValue.toString()); - inputList.push( - `` + - `` + - `${defaultValue}` + - '' + - '' - ); + + // is the ScratchBlocks name for a block input. + // The is a placeholder for a reporter and is visible when there's no reporter in this input. + inputList.push(``); + + // is a text field that the user can type into. Some shadows, like the color picker, don't allow + // text input and therefore don't need a field element. + if (argTypeInfo.fieldType) { + inputList.push(`${defaultValue}`); + } + + inputList.push(''); return `%${argNum}`; }); diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 62a7a0cf82..827bc491d1 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -103,6 +103,18 @@ class ExtensionManager { } } + /** + * Register an internal (non-Worker) extension object + * @param {object} extensionObject - the extension object to register + * @returns {Promise} resolved once the extension is fully registered or rejected on failure + */ + _registerInternalExtension (extensionObject) { + const extensionInfo = extensionObject.getInfo(); + const serviceName = `extension.internal.${extensionInfo.id}`; + return dispatch.setService(serviceName, extensionObject) + .then(() => dispatch.call('extensions', 'registerExtensionService', serviceName)); + } + _registerExtensionInfo (serviceName, extensionInfo) { extensionInfo = this._prepareExtensionInfo(serviceName, extensionInfo); dispatch.call('runtime', '_registerExtensionPrimitives', extensionInfo).catch(e => { @@ -162,6 +174,7 @@ class ExtensionManager { }, blockInfo); blockInfo.opcode = this._sanitizeID(blockInfo.opcode); blockInfo.func = blockInfo.func ? this._sanitizeID(blockInfo.func) : blockInfo.opcode; + blockInfo.text = blockInfo.text || blockInfo.opcode; blockInfo.func = dispatch.call.bind(dispatch, serviceName, blockInfo.func); return blockInfo; } From 3c9f34549802a82a785a396e7bfc31c8025ef0d2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 4 Oct 2017 15:37:06 -0400 Subject: [PATCH 1559/1971] Merge pull request #686 from paulkaplan/fix-extension-labels Remove label generation for extensions --- packages/scratch-vm/src/engine/runtime.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 1b3641ab7b..6d8ce1dd18 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -531,8 +531,6 @@ class Runtime extends EventEmitter { for (const categoryInfo of this._blockInfo) { const {name, color1, color2} = categoryInfo; xmlParts.push(``); - // @todo only add this label for user-loaded extensions? - xmlParts.push(`'); } From c32eb365c6964facb678a54d79804027c39c122a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 11 Oct 2017 14:24:10 -0400 Subject: [PATCH 1560/1971] Merge pull request #691 from paulkaplan/say-think Add say/think functionality to looks blocks --- packages/scratch-vm/src/engine/runtime.js | 27 +++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 6d8ce1dd18..e6a6d81039 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -283,7 +283,8 @@ class Runtime extends EventEmitter { } /** - * Event name for glowing the green flag + * Event name when threads start running. + * Used by the UI to indicate running status. * @const {string} */ static get PROJECT_RUN_START () { @@ -291,13 +292,23 @@ class Runtime extends EventEmitter { } /** - * Event name for unglowing the green flag + * Event name when threads stop running + * Used by the UI to indicate not-running status. * @const {string} */ static get PROJECT_RUN_STOP () { return 'PROJECT_RUN_STOP'; } + /** + * Event name for project being stopped or restarted by the user. + * Used by blocks that need to reset state. + * @const {string} + */ + static get PROJECT_STOP_ALL () { + return 'PROJECT_STOP_ALL'; + } + /** * Event name for visual value report. * @const {string} @@ -911,6 +922,9 @@ class Runtime extends EventEmitter { * Stop "everything." */ stopAll () { + // Emit stop event to allow blocks to clean up any state. + this.emit(Runtime.PROJECT_STOP_ALL); + // Dispose all clones. const newTargets = []; for (let i = 0; i < this.targets.length; i++) { @@ -1233,6 +1247,15 @@ class Runtime extends EventEmitter { this.emit('targetWasCreated', newTarget, sourceTarget); } + /** + * Report that a clone target is being removed. + * @param {Target} target - the target being removed + * @fires Runtime#targetWasRemoved + */ + fireTargetWasRemoved (target) { + this.emit('targetWasRemoved', target); + } + /** * Get a target representing the Scratch stage, if one exists. * @return {?Target} The target, if found. From 9a3526159351fc9a1abb4b07c88e50ae05fb5606 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Fri, 13 Oct 2017 09:48:56 -0700 Subject: [PATCH 1561/1971] Merge pull request #687 from cwillisf/builtin-extensions Builtin extensions --- packages/scratch-vm/src/engine/runtime.js | 9 ++-- .../extension-support/extension-manager.js | 41 +++++++++++++++++-- packages/scratch-vm/src/virtual-machine.js | 2 +- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index e6a6d81039..9039d5df56 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1,5 +1,6 @@ const EventEmitter = require('events'); const {OrderedMap} = require('immutable'); +const escapeHtml = require('escape-html'); const ArgumentType = require('../extension-support/argument-type'); const Blocks = require('./blocks'); @@ -19,12 +20,10 @@ const defaultBlockPackages = { scratch3_looks: require('../blocks/scratch3_looks'), scratch3_motion: require('../blocks/scratch3_motion'), scratch3_operators: require('../blocks/scratch3_operators'), - scratch3_pen: require('../blocks/scratch3_pen'), scratch3_sound: require('../blocks/scratch3_sound'), scratch3_sensing: require('../blocks/scratch3_sensing'), scratch3_data: require('../blocks/scratch3_data'), - scratch3_procedures: require('../blocks/scratch3_procedures'), - scratch3_wedo2: require('../blocks/scratch3_wedo2') + scratch3_procedures: require('../blocks/scratch3_procedures') }; /** @@ -469,7 +468,9 @@ class Runtime extends EventEmitter { const argInfo = blockInfo.arguments[placeholder] || {}; const argTypeInfo = ArgumentTypeMap[argInfo.type] || {}; - const defaultValue = (typeof argInfo.defaultValue === 'undefined' ? '' : argInfo.defaultValue.toString()); + const defaultValue = (typeof argInfo.defaultValue === 'undefined' ? + '' : + escapeHtml(argInfo.defaultValue.toString())); // is the ScratchBlocks name for a block input. // The is a placeholder for a reporter and is visible when there's no reporter in this input. diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 827bc491d1..bdbc274f16 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -3,6 +3,16 @@ const log = require('../util/log'); const BlockType = require('./block-type'); +// These extensions are currently built into the VM repository but should not be loaded at startup. +// TODO: move these out into a separate repository? +// TODO: change extension spec so that library info, including extension ID, can be collected through static methods +const Scratch3PenBlocks = require('../blocks/scratch3_pen'); +const Scratch3WeDo2Blocks = require('../blocks/scratch3_wedo2'); +const builtinExtensions = { + pen: Scratch3PenBlocks, + wedo2: Scratch3WeDo2Blocks +}; + /** * @typedef {object} ArgumentInfo - Information about an extension block argument * @property {ArgumentType} type - the type of value this argument can take @@ -39,7 +49,7 @@ const BlockType = require('./block-type'); */ class ExtensionManager { - constructor () { + constructor (runtime) { /** * The ID number to provide to the next extension worker. * @type {int} @@ -60,17 +70,30 @@ class ExtensionManager { */ this.pendingWorkers = []; + /** + * Keep a reference to the runtime so we can construct internal extension objects. + * TODO: remove this in favor of extensions accessing the runtime as a service. + * @type {Runtime} + */ + this.runtime = runtime; + dispatch.setService('extensions', this).catch(e => { log.error(`ExtensionManager was unable to register extension service: ${JSON.stringify(e)}`); }); } /** - * Load an extension by URL - * @param {string} extensionURL - the URL for the extension to load + * Load an extension by URL or internal extension ID + * @param {string} extensionURL - the URL for the extension to load OR the ID of an internal extension * @returns {Promise} resolved once the extension is loaded and initialized or rejected on failure */ loadExtensionURL (extensionURL) { + if (builtinExtensions.hasOwnProperty(extensionURL)) { + const extension = builtinExtensions[extensionURL]; + const extensionInstance = new extension(this.runtime); + return this._registerInternalExtension(extensionInstance); + } + return new Promise((resolve, reject) => { // If we `require` this at the global level it breaks non-webpack targets, including tests const ExtensionWorker = require('worker-loader?name=extension-worker.js!./extension-worker'); @@ -175,7 +198,17 @@ class ExtensionManager { blockInfo.opcode = this._sanitizeID(blockInfo.opcode); blockInfo.func = blockInfo.func ? this._sanitizeID(blockInfo.func) : blockInfo.opcode; blockInfo.text = blockInfo.text || blockInfo.opcode; - blockInfo.func = dispatch.call.bind(dispatch, serviceName, blockInfo.func); + + /** + * This is only here because the VM performs poorly when blocks return promises. + * @TODO make it possible for the VM to resolve a promise and continue during the same frame. + */ + if (dispatch._isRemoteService(serviceName)) { + blockInfo.func = dispatch.call.bind(dispatch, serviceName, blockInfo.func); + } else { + const serviceObject = dispatch.services[serviceName]; + blockInfo.func = serviceObject[blockInfo.func].bind(serviceObject); + } return blockInfo; } } diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 721477f096..eec4d07520 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -68,7 +68,7 @@ class VirtualMachine extends EventEmitter { this.emit(Runtime.EXTENSION_ADDED, blocksInfo); }); - this.extensionManager = new ExtensionManager(); + this.extensionManager = new ExtensionManager(this.runtime); this.blockListener = this.blockListener.bind(this); this.flyoutBlockListener = this.flyoutBlockListener.bind(this); From 1d44ae61efa1f66cdf30868e023b57aa2c054e2d Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Fri, 13 Oct 2017 09:49:42 -0700 Subject: [PATCH 1562/1971] Merge pull request #700 from cwillisf/extension-menus Extensions: implement drop-down menus --- packages/scratch-vm/src/engine/runtime.js | 117 +++++++++++++++--- .../extension-support/extension-manager.js | 2 +- 2 files changed, 102 insertions(+), 17 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 9039d5df56..afc4826381 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -32,6 +32,10 @@ const defaultBlockPackages = { */ const ArgumentTypeMap = (() => { const map = {}; + map[ArgumentType.ANGLE] = { + shadowType: 'math_angle', + fieldType: 'NUM' + }; map[ArgumentType.COLOR] = { shadowType: 'colour_picker' }; @@ -43,9 +47,8 @@ const ArgumentTypeMap = (() => { shadowType: 'text', fieldType: 'TEXT' }; - // @TODO: talk to Rachel & co. to figure out what goes here. Make it OK to not have a field. Add `check` support. map[ArgumentType.BOOLEAN] = { - shadowType: '' + check: 'Boolean' }; return map; })(); @@ -398,6 +401,17 @@ class Runtime extends EventEmitter { } } + /** + * Generate an extension-specific menu ID. + * @param {string} menuName - the name of the menu. + * @param {string} extensionId - the ID of the extension hosting the menu. + * @returns {string} - the constructed ID. + * @private + */ + _makeExtensionMenuId (menuName, extensionId) { + return `${extensionId}.menu.${escapeHtml(menuName)}`; + } + /** * Register the primitives provided by an extension. * @param {ExtensionInfo} extensionInfo - information about the extension (id, blocks, etc.) @@ -410,11 +424,19 @@ class Runtime extends EventEmitter { color1: '#FF6680', color2: '#FF4D6A', color3: '#FF3355', - blocks: [] + blocks: [], + menus: [] }; this._blockInfo.push(categoryInfo); + for (const menuName in extensionInfo.menus) { + if (extensionInfo.menus.hasOwnProperty(menuName)) { + const menuItems = extensionInfo.menus[menuName]; + const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuItems, extensionInfo); + categoryInfo.menus.push(convertedMenu); + } + } for (const blockInfo of extensionInfo.blocks) { const convertedBlock = this._convertForScratchBlocks(blockInfo, categoryInfo); const opcode = convertedBlock.json.type; @@ -422,7 +444,54 @@ class Runtime extends EventEmitter { this._primitives[opcode] = convertedBlock.info.func; } - this.emit(Runtime.EXTENSION_ADDED, categoryInfo.blocks); + this.emit(Runtime.EXTENSION_ADDED, categoryInfo.blocks.concat(categoryInfo.menus)); + } + + /** + * Build the scratch-blocks JSON for a menu. Note that scratch-blocks treats menus as a special kind of block. + * @param {string} menuName - the name of the menu + * @param {array} menuItems - the list of items for this menu + * @param {CategoryInfo} categoryInfo - the category for this block + * @returns {object} - a JSON-esque object ready for scratch-blocks' consumption + * @private + */ + _buildMenuForScratchBlocks (menuName, menuItems, categoryInfo) { + const menuId = this._makeExtensionMenuId(menuName, categoryInfo.id); + + /** @TODO: support dynamic menus when 'menuItems' is a method name string (see extension spec) */ + if (typeof menuItems === 'string') { + throw new Error(`Dynamic extension menus are not yet supported. Menu name: ${menuName}`); + } + const options = menuItems.map(item => { + switch (typeof item) { + case 'string': + return [item, item]; + case 'object': + return [item.text, item.value]; + default: + throw new Error(`Can't interpret menu item: ${item}`); + } + }); + + return { + json: { + message0: '%1', + type: menuId, + inputsInline: true, + output: 'String', + colour: categoryInfo.color1, + colourSecondary: categoryInfo.color2, + colourTertiary: categoryInfo.color3, + outputShape: ScratchBlocksConstants.OUTPUT_SHAPE_ROUND, + args0: [ + { + type: 'field_dropdown', + name: menuName, + options: options + } + ] + } + }; } /** @@ -457,14 +526,10 @@ class Runtime extends EventEmitter { // Sanitize the placeholder to ensure valid XML placeholder = placeholder.replace(/[<"&]/, '_'); - blockJSON.args0.push({ + const argJSON = { type: 'input_value', name: placeholder - }); - - // scratch-blocks uses 1-based argument indexing - const argNum = blockJSON.args0.length; - argsMap[placeholder] = argNum; + }; const argInfo = blockInfo.arguments[placeholder] || {}; const argTypeInfo = ArgumentTypeMap[argInfo.type] || {}; @@ -472,18 +537,38 @@ class Runtime extends EventEmitter { '' : escapeHtml(argInfo.defaultValue.toString())); + if (argTypeInfo.check) { + argJSON.check = argTypeInfo.check; + } + + const shadowType = (argInfo.menu ? + this._makeExtensionMenuId(argInfo.menu, categoryInfo.id) : + argTypeInfo.shadowType); + const fieldType = argInfo.menu || argTypeInfo.fieldType; + // is the ScratchBlocks name for a block input. + inputList.push(``); + // The is a placeholder for a reporter and is visible when there's no reporter in this input. - inputList.push(``); + // Boolean inputs don't need to specify a shadow in the XML. + if (shadowType) { + inputList.push(``); + + // is a text field that the user can type into. Some shadows, like the color picker, don't allow + // text input and therefore don't need a field element. + if (fieldType) { + inputList.push(`${defaultValue}`); + } - // is a text field that the user can type into. Some shadows, like the color picker, don't allow - // text input and therefore don't need a field element. - if (argTypeInfo.fieldType) { - inputList.push(`${defaultValue}`); + inputList.push(''); } - inputList.push(''); + inputList.push(''); + // scratch-blocks uses 1-based argument indexing + blockJSON.args0.push(argJSON); + const argNum = blockJSON.args0.length; + argsMap[placeholder] = argNum; return `%${argNum}`; }); diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index bdbc274f16..d7c5fab05f 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -174,7 +174,7 @@ class ExtensionManager { result.push(this._prepareBlockInfo(serviceName, blockInfo)); } catch (e) { // TODO: more meaningful error reporting - log.error(`Skipping malformed block: ${JSON.stringify(e)}`); + log.error(`Error processing block: ${e.message}, Block:\n${JSON.stringify(blockInfo)}`); } return result; }, []); From 5fd8be5aa66f71972a2a734d625b2ef920b7f548 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Fri, 13 Oct 2017 16:03:51 -0700 Subject: [PATCH 1563/1971] Merge pull request #713 from cwillisf/fix-extension-hats Fix extension hats --- packages/scratch-vm/src/engine/runtime.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index afc4826381..1e0e90ca05 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -442,6 +442,9 @@ class Runtime extends EventEmitter { const opcode = convertedBlock.json.type; categoryInfo.blocks.push(convertedBlock); this._primitives[opcode] = convertedBlock.info.func; + if (blockInfo.blockType === BlockType.HAT) { + this._hats[opcode] = {edgeActivated: true}; /** @TODO let extension specify this */ + } } this.emit(Runtime.EXTENSION_ADDED, categoryInfo.blocks.concat(categoryInfo.menus)); @@ -898,13 +901,14 @@ class Runtime extends EventEmitter { // If no fields are present, check inputs (horizontal blocks) if (Object.keys(hatFields).length === 0) { + hatFields = {}; // don't overwrite the block's actual fields list const hatInputs = blocks.getInputs(block); for (const input in hatInputs) { if (!hatInputs.hasOwnProperty(input)) continue; const id = hatInputs[input].block; const inpBlock = blocks.getBlock(id); const fields = blocks.getFields(inpBlock); - hatFields = Object.assign(fields, hatFields); + Object.assign(hatFields, fields); } } From 76489c021dac5616e04058ae9e5bb8193293a8e1 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 13 Oct 2017 19:11:41 -0400 Subject: [PATCH 1564/1971] Merge pull request #718 from fsih/updateSvgStorage Update storage when the SVG updates --- packages/scratch-vm/src/virtual-machine.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index eec4d07520..6f0658414b 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -400,9 +400,13 @@ class VirtualMachine extends EventEmitter { costume.rotationCenterY = rotationCenterY; this.runtime.renderer.updateSVGSkin(costume.skinId, svg, [rotationCenterX, rotationCenterY]); } - // @todo: Also update storage in addition to renderer. Without storage, if you switch - // costumes and switch back, you will lose your changes in the paint editor. - // @todo: emitTargetsUpdate if we need to update the storage ID on the updated costume. + const storage = this.runtime.storage; + costume.assetId = storage.builtinHelper.cache( + storage.AssetType.ImageVector, + storage.DataFormat.SVG, + (new TextEncoder()).encode(svg) + ); + this.emitTargetsUpdate(); } /** From 12d79d906b70c20da763e9a8bb7753717df46a7b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 1 Nov 2017 16:20:33 -0400 Subject: [PATCH 1565/1971] Merge pull request #745 from paulkaplan/legacy-pen-blocks Support legacy hue and shade pen blocks --- packages/scratch-vm/src/engine/runtime.js | 3 ++- packages/scratch-vm/src/extension-support/extension-manager.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 1e0e90ca05..b97387135f 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -630,8 +630,9 @@ class Runtime extends EventEmitter { const xmlParts = []; for (const categoryInfo of this._blockInfo) { const {name, color1, color2} = categoryInfo; + const paletteBlocks = categoryInfo.blocks.filter(block => !block.info.hideFromPalette); xmlParts.push(``); - xmlParts.push.apply(xmlParts, categoryInfo.blocks.map(blockInfo => blockInfo.xml)); + xmlParts.push.apply(xmlParts, paletteBlocks.map(block => block.xml)); xmlParts.push(''); } return xmlParts.join('\n'); diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index d7c5fab05f..2179e3f23f 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -30,6 +30,7 @@ const builtinExtensions = { * @property {object.|undefined} arguments - information about this block's arguments, if any * @property {string|Function|undefined} func - the method for this block on the extension service (default: opcode) * @property {Array.|undefined} filter - the list of targets for which this block should appear (default: all) + * @property {Boolean|undefined} hideFromPalette - true if should not be appear in the palette. (default false) */ /** From 98d3b24480e3bfceb66ff66ba2827028b3769ba8 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 1 Nov 2017 21:29:00 -0700 Subject: [PATCH 1566/1971] Merge pull request #724 from cwillisf/no-dupe-extensions Prevent duplicate extensions --- .../extension-support/extension-manager.js | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 2179e3f23f..5cfa2b4a37 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -71,6 +71,13 @@ class ExtensionManager { */ this.pendingWorkers = []; + /** + * Set of loaded extension URLs/IDs (equivalent for built-in extensions). + * @type {Set.} + * @private + */ + this._loadedExtensions = new Set(); + /** * Keep a reference to the runtime so we can construct internal extension objects. * TODO: remove this in favor of extensions accessing the runtime as a service. @@ -83,6 +90,17 @@ class ExtensionManager { }); } + /** + * Check whether an extension is registered or is in the process of loading. This is intended to control loading or + * adding extensions so it may return `true` before the extension is ready to be used. Use the promise returned by + * `loadExtensionURL` if you need to wait until the extension is truly ready. + * @param {string} extensionID - the ID of the extension. + * @returns {boolean} - true if loaded, false otherwise. + */ + isExtensionLoaded (extensionID) { + return this._loadedExtensions.has(extensionID); + } + /** * Load an extension by URL or internal extension ID * @param {string} extensionURL - the URL for the extension to load OR the ID of an internal extension @@ -90,9 +108,18 @@ class ExtensionManager { */ loadExtensionURL (extensionURL) { if (builtinExtensions.hasOwnProperty(extensionURL)) { + /** @TODO dupe handling for non-builtin extensions. See commit 670e51d33580e8a2e852b3b038bb3afc282f81b9 */ + if (this.isExtensionLoaded(extensionURL)) { + const message = `Rejecting attempt to load a second extension with ID ${extensionURL}`; + log.warn(message); + return Promise.reject(new Error(message)); + } + const extension = builtinExtensions[extensionURL]; const extensionInstance = new extension(this.runtime); - return this._registerInternalExtension(extensionInstance); + return this._registerInternalExtension(extensionInstance).then(() => { + this._loadedExtensions.add(extensionURL); + }); } return new Promise((resolve, reject) => { @@ -111,12 +138,21 @@ class ExtensionManager { return [id, workerInfo.extensionURL]; } + /** + * Collect extension metadata from the specified service and begin the extension registration process. + * @param {string} serviceName - the name of the service hosting the extension. + */ registerExtensionService (serviceName) { dispatch.call(serviceName, 'getInfo').then(info => { this._registerExtensionInfo(serviceName, info); }); } + /** + * Called by an extension worker to indicate that the worker has finished initialization. + * @param {int} id - the worker ID. + * @param {*?} e - the error encountered during initialization, if any. + */ onWorkerInit (id, e) { const workerInfo = this.pendingWorkers[id]; delete this.pendingWorkers[id]; @@ -134,11 +170,18 @@ class ExtensionManager { */ _registerInternalExtension (extensionObject) { const extensionInfo = extensionObject.getInfo(); - const serviceName = `extension.internal.${extensionInfo.id}`; + const fakeWorkerId = this.nextExtensionWorker++; + const serviceName = `extension.${fakeWorkerId}.${extensionInfo.id}`; return dispatch.setService(serviceName, extensionObject) .then(() => dispatch.call('extensions', 'registerExtensionService', serviceName)); } + /** + * Sanitize extension info then register its primitives with the VM. + * @param {string} serviceName - the name of the service hosting the extension + * @param {ExtensionInfo} extensionInfo - the extension's metadata + * @private + */ _registerExtensionInfo (serviceName, extensionInfo) { extensionInfo = this._prepareExtensionInfo(serviceName, extensionInfo); dispatch.call('runtime', '_registerExtensionPrimitives', extensionInfo).catch(e => { From 1e2428cf7418ac6022857537e12cd93f3b1a85f1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 3 Nov 2017 15:15:15 -0400 Subject: [PATCH 1567/1971] Merge pull request #755 from paulkaplan/fix-extension-colors Use category info that has colors when building menu blocks --- packages/scratch-vm/src/engine/runtime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index b97387135f..c86516bf79 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -433,7 +433,7 @@ class Runtime extends EventEmitter { for (const menuName in extensionInfo.menus) { if (extensionInfo.menus.hasOwnProperty(menuName)) { const menuItems = extensionInfo.menus[menuName]; - const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuItems, extensionInfo); + const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuItems, categoryInfo); categoryInfo.menus.push(convertedMenu); } } From cc7d6843720b4586bd16fc94bdad3939703776eb Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Fri, 3 Nov 2017 13:59:01 -0700 Subject: [PATCH 1568/1971] Merge pull request #756 from cwillisf/autoload-extensions Autoload extensions --- packages/scratch-vm/src/virtual-machine.js | 59 ++++++++++++++-------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 6f0658414b..6971e4509b 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -33,7 +33,7 @@ class VirtualMachine extends EventEmitter { /** * The "currently editing"/selected target ID for the VM. * Block events from any Blockly workspace are routed to this target. - * @type {!string} + * @type {Target} */ this.editingTarget = null; // Runtime emits are passed along as VM emits. @@ -228,19 +228,42 @@ class VirtualMachine extends EventEmitter { deserializer = sb2; } - return deserializer.deserialize(json, this.runtime).then(targets => { - this.clear(); - for (let n = 0; n < targets.length; n++) { - if (targets[n] !== null) { - this.runtime.targets.push(targets[n]); - targets[n].updateAllDrawableProperties(); - } + return deserializer.deserialize(json, this.runtime) + .then(({targets, extensions}) => + this.installTargets(targets, extensions, true)); + } + + /** + * Install `deserialize` results: zero or more targets after the extensions (if any) used by those targets. + * @param {Array.} targets - the targets to be installed + * @param {ImportedExtensionsInfo} extensions - metadata about extensions used by these targets + * @param {boolean} wholeProject - set to true if installing a whole project, as opposed to a single sprite. + * @returns {Promise} resolved once targets have been installed + */ + installTargets (targets, extensions, wholeProject) { + const extensionPromises = []; + extensions.extensionIDs.forEach(extensionID => { + if (!this.extensionManager.isExtensionLoaded(extensionID)) { + const extensionURL = extensions.extensionURLs.get(extensionID) || extensionID; + extensionPromises.push(this.extensionManager.loadExtensionURL(extensionURL)); } + }); + + targets = targets.filter(target => !!target); + + return Promise.all(extensionPromises).then(() => { + if (wholeProject) { + this.clear(); + } + targets.forEach(target => { + this.runtime.targets.push(target); + (/** @type RenderedTarget */ target).updateAllDrawableProperties(); + }); // Select the first target for editing, e.g., the first sprite. - if (this.runtime.targets.length > 1) { - this.editingTarget = this.runtime.targets[1]; + if (wholeProject && (targets.length > 1)) { + this.editingTarget = targets[1]; } else { - this.editingTarget = this.runtime.targets[0]; + this.editingTarget = targets[0]; } // Update the VM user's knowledge of targets and blocks on the workspace. @@ -267,17 +290,9 @@ class VirtualMachine extends EventEmitter { return; } - // Select new sprite. - return sb2.deserialize(json, this.runtime, true).then(targets => { - this.runtime.targets.push(targets[0]); - this.editingTarget = targets[0]; - this.editingTarget.updateAllDrawableProperties(); - - // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); - }); + return sb2.deserialize(json, this.runtime, true) + .then(({targets, extensions}) => + this.installTargets(targets, extensions, false)); } /** From dbe97665782bb25528593c20feb27ce5ad5c2946 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 6 Nov 2017 10:02:23 -0500 Subject: [PATCH 1569/1971] Merge pull request #754 from ericrosenbaum/feature/pen-block-icon Extension block icons (including pen icon) --- packages/scratch-vm/src/engine/runtime.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index c86516bf79..56fceadfc7 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -421,6 +421,7 @@ class Runtime extends EventEmitter { const categoryInfo = { id: extensionInfo.id, name: extensionInfo.name, + iconURI: extensionInfo.iconURI, color1: '#FF6680', color2: '#FF4D6A', color3: '#FF3355', @@ -524,8 +525,21 @@ class Runtime extends EventEmitter { // but each `[ARG]` will need to be replaced with the number in this map instead of `args0.length`. const argsMap = {}; - blockJSON.message0 = blockInfo.text.replace(/\[(.+?)]/g, (match, placeholder) => { + blockJSON.message0 = ''; + // If an icon for the extension exists, prepend it to each block + if (categoryInfo.iconURI) { + blockJSON.message0 = '%1'; + const iconJSON = { + type: 'field_image', + src: categoryInfo.iconURI, + width: 40, + height: 40 + }; + blockJSON.args0.push(iconJSON); + } + + blockJSON.message0 += blockInfo.text.replace(/\[(.+?)]/g, (match, placeholder) => { // Sanitize the placeholder to ensure valid XML placeholder = placeholder.replace(/[<"&]/, '_'); From 19d07a4c38e83e302f14a40760a9e36e0f850a85 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 7 Nov 2017 18:36:20 -0500 Subject: [PATCH 1570/1971] Merge pull request #769 from ericrosenbaum/feature/extension-music Move music blocks into an extension --- .../scratch-vm/src/extension-support/extension-manager.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 5cfa2b4a37..938e791dcf 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -8,9 +8,11 @@ const BlockType = require('./block-type'); // TODO: change extension spec so that library info, including extension ID, can be collected through static methods const Scratch3PenBlocks = require('../blocks/scratch3_pen'); const Scratch3WeDo2Blocks = require('../blocks/scratch3_wedo2'); +const Scratch3MusicBlocks = require('../blocks/scratch3_music'); const builtinExtensions = { pen: Scratch3PenBlocks, - wedo2: Scratch3WeDo2Blocks + wedo2: Scratch3WeDo2Blocks, + music: Scratch3MusicBlocks }; /** From de3d91ec18a34dca91128ce260d1e301afad98a4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 13 Nov 2017 10:17:09 -0500 Subject: [PATCH 1571/1971] Merge pull request #772 from kchadha/improving-test-coverage Improving test coverage --- packages/scratch-vm/src/virtual-machine.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 6971e4509b..6dfc7acade 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -474,9 +474,9 @@ class VirtualMachine extends EventEmitter { */ deleteSprite (targetId) { const target = this.runtime.getTargetById(targetId); - const targetIndexBeforeDelete = this.runtime.targets.map(t => t.id).indexOf(target.id); if (target) { + const targetIndexBeforeDelete = this.runtime.targets.map(t => t.id).indexOf(target.id); if (!target.isSprite()) { throw new Error('Cannot delete non-sprite targets.'); } @@ -492,7 +492,11 @@ class VirtualMachine extends EventEmitter { // Ensure editing target is switched if we are deleting it. if (clone === currentEditingTarget) { const nextTargetIndex = Math.min(this.runtime.targets.length - 1, targetIndexBeforeDelete); - this.setEditingTarget(this.runtime.targets[nextTargetIndex].id); + if (this.runtime.targets.length > 0){ + this.setEditingTarget(this.runtime.targets[nextTargetIndex].id); + } else { + this.editingTarget = null; + } } } // Sprite object should be deleted by GC. @@ -505,6 +509,8 @@ class VirtualMachine extends EventEmitter { /** * Duplicate a sprite. * @param {string} targetId ID of a target whose sprite to duplicate. + * @returns {Promise} Promise that resolves when duplicated target has + * been added to the runtime. */ duplicateSprite (targetId) { const target = this.runtime.getTargetById(targetId); @@ -515,7 +521,7 @@ class VirtualMachine extends EventEmitter { } else if (!target.sprite) { throw new Error('No sprite associated with this target.'); } - target.duplicate().then(newTarget => { + return target.duplicate().then(newTarget => { this.runtime.targets.push(newTarget); this.setEditingTarget(newTarget.id); }); From 84279e14cd08ab8536ede9523e0579d037ad4166 Mon Sep 17 00:00:00 2001 From: kchadha Date: Thu, 16 Nov 2017 10:02:37 -0500 Subject: [PATCH 1572/1971] Merge pull request #780 from mzgoddard/runtime-profiler Runtime profiler --- packages/scratch-vm/src/engine/runtime.js | 73 +++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 56fceadfc7..39be44db86 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -7,6 +7,7 @@ const Blocks = require('./blocks'); const BlockType = require('../extension-support/block-type'); const Sequencer = require('./sequencer'); const Thread = require('./thread'); +const Profiler = require('./profiler'); // Virtual I/O devices. const Clock = require('../io/clock'); @@ -79,6 +80,24 @@ const ScratchBlocksConstants = { OUTPUT_SHAPE_SQUARE: 3 }; +/** + * Numeric ID for Runtime._step in Profiler instances. + * @type {number} + */ +let stepProfilerId = -1; + +/** + * Numeric ID for Sequencer.stepThreads in Profiler instances. + * @type {number} + */ +let stepThreadsProfilerId = -1; + +/** + * Numeric ID for RenderWebGL.draw in Profiler instances. + * @type {number} + */ +let rendererDrawProfilerId = -1; + /** * Manages targets, scripts, and the sequencer. * @constructor @@ -234,6 +253,13 @@ class Runtime extends EventEmitter { keyboard: new Keyboard(this), mouse: new Mouse(this) }; + + /** + * A runtime profiler that records timed events for later playback to + * diagnose Scratch performance. + * @type {Profiler} + */ + this.profiler = null; } /** @@ -1055,6 +1081,12 @@ class Runtime extends EventEmitter { * inactive threads after each iteration. */ _step () { + if (this.profiler !== null) { + if (stepProfilerId === -1) { + stepProfilerId = this.profiler.idByName('Runtime._step'); + } + this.profiler.start(stepProfilerId); + } // Find all edge-activated hats, and add them to threads to be evaluated. for (const hatType in this._hats) { if (!this._hats.hasOwnProperty(hatType)) continue; @@ -1065,7 +1097,16 @@ class Runtime extends EventEmitter { } this.redrawRequested = false; this._pushMonitors(); + if (this.profiler !== null) { + if (stepThreadsProfilerId === -1) { + stepThreadsProfilerId = this.profiler.idByName('Sequencer.stepThreads'); + } + this.profiler.start(stepThreadsProfilerId); + } const doneThreads = this.sequencer.stepThreads(); + if (this.profiler !== null) { + this.profiler.stop(); + } this._updateGlows(doneThreads); // Add done threads so that even if a thread finishes within 1 frame, the green // flag will still indicate that a script ran. @@ -1074,7 +1115,16 @@ class Runtime extends EventEmitter { this._getMonitorThreadCount([...this.threads, ...doneThreads])); if (this.renderer) { // @todo: Only render when this.redrawRequested or clones rendered. + if (this.profiler !== null) { + if (rendererDrawProfilerId === -1) { + rendererDrawProfilerId = this.profiler.idByName('RenderWebGL.draw'); + } + this.profiler.start(rendererDrawProfilerId); + } this.renderer.draw(); + if (this.profiler !== null) { + this.profiler.stop(); + } } if (this._refreshTargets) { @@ -1086,6 +1136,11 @@ class Runtime extends EventEmitter { this.emit(Runtime.MONITORS_UPDATE, this._monitorState); this._prevMonitorState = this._monitorState; } + + if (this.profiler !== null) { + this.profiler.stop(); + this.profiler.reportFrames(); + } } /** @@ -1413,6 +1468,24 @@ class Runtime extends EventEmitter { this._step(); }, interval); } + + /** + * Turn on profiling. + * @param {Profiler/FrameCallback} onFrame A callback handle passed a + * profiling frame when the profiler reports its collected data. + */ + enableProfiling (onFrame) { + if (Profiler.available()) { + this.profiler = new Profiler(onFrame); + } + } + + /** + * Turn off profiling. + */ + disableProfiling () { + this.profiler = null; + } } /** From 64b12623d3963460af4f8e142b8815624c789bc1 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 16 Nov 2017 11:48:23 -0500 Subject: [PATCH 1573/1971] Merge pull request #768 from mzgoddard/broadcast-and-wait-chained Broadcast and wait chained --- packages/scratch-vm/src/engine/runtime.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 39be44db86..2f34cd8d00 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -804,6 +804,7 @@ class Runtime extends EventEmitter { * This is used by `startHats` to and is necessary to ensure 2.0-like execution order. * Test project: https://scratch.mit.edu/projects/130183108/ * @param {!Thread} thread Thread object to restart. + * @return {Thread} The restarted thread. */ _restartThread (thread) { const newThread = new Thread(thread.topBlock); @@ -814,9 +815,10 @@ class Runtime extends EventEmitter { const i = this.threads.indexOf(thread); if (i > -1) { this.threads[i] = newThread; - } else { - this.threads.push(thread); + return newThread; } + this.threads.push(thread); + return thread; } /** @@ -825,7 +827,11 @@ class Runtime extends EventEmitter { * @return {boolean} True if the thread is active/running. */ isActiveThread (thread) { - return this.threads.indexOf(thread) > -1; + return ( + ( + thread.stack.length > 0 && + thread.status !== Thread.STATUS_DONE) && + this.threads.indexOf(thread) > -1); } /** @@ -972,7 +978,7 @@ class Runtime extends EventEmitter { if (instance.threads[i].topBlock === topBlockId && !instance.threads[i].stackClick && // stack click threads and hat threads can coexist instance.threads[i].target === target) { - instance._restartThread(instance.threads[i]); + newThreads.push(instance._restartThread(instance.threads[i])); return; } } From d1199b1fd832b0297de6fb6f0e7835755c400863 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 20 Nov 2017 13:56:23 -0500 Subject: [PATCH 1574/1971] Merge pull request #785 from ericrosenbaum/feature/load-drums-from-storage Load drum sounds from storage and play them --- packages/scratch-vm/src/extension-support/extension-manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 938e791dcf..c3346a22db 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -8,7 +8,7 @@ const BlockType = require('./block-type'); // TODO: change extension spec so that library info, including extension ID, can be collected through static methods const Scratch3PenBlocks = require('../blocks/scratch3_pen'); const Scratch3WeDo2Blocks = require('../blocks/scratch3_wedo2'); -const Scratch3MusicBlocks = require('../blocks/scratch3_music'); +const Scratch3MusicBlocks = require('../extensions/scratch3_music'); const builtinExtensions = { pen: Scratch3PenBlocks, wedo2: Scratch3WeDo2Blocks, From fa4c1c7c5b1c011ee71477f7b6343031cae411db Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 21 Nov 2017 10:23:53 -0500 Subject: [PATCH 1575/1971] Merge pull request #784 from fsih/perSpriteMonitors Execute monitors on a given target ID when block is sprite-specific --- packages/scratch-vm/src/engine/runtime.js | 29 +++++++++++++++++++--- packages/scratch-vm/src/virtual-machine.js | 1 + 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 2f34cd8d00..8131bdd865 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -195,6 +195,13 @@ class Runtime extends EventEmitter { */ this._refreshTargets = false; + /** + * Map to look up all monitor block information by opcode. + * @type {object} + * @private + */ + this.monitorBlockInfo = {}; + /** * Ordered map of all monitors, which are MonitorReporter objects. */ @@ -423,6 +430,10 @@ class Runtime extends EventEmitter { } } } + // Collect monitored from package. + if (packageObject.getMonitored) { + this.monitorBlockInfo = Object.assign({}, this.monitorBlockInfo, packageObject.getMonitored()); + } } } } @@ -870,7 +881,7 @@ class Runtime extends EventEmitter { /** * Enqueue a script that when finished will update the monitor for the block. * @param {!string} topBlockId ID of block that starts the script. - * @param {?string} optTarget target ID for target to run script on. If not supplied, uses editing target. + * @param {?Target} optTarget target Target to run script on. If not supplied, uses editing target. */ addMonitorScript (topBlockId, optTarget) { if (!optTarget) optTarget = this._editingTarget; @@ -1321,7 +1332,7 @@ class Runtime extends EventEmitter { * @param {!MonitorRecord} monitor Monitor to add. */ requestAddMonitor (monitor) { - this._monitorState = this._monitorState.set(monitor.id, monitor); + this._monitorState = this._monitorState.set(monitor.get('id'), monitor); } /** @@ -1332,9 +1343,10 @@ class Runtime extends EventEmitter { * the old monitor will keep its old value. */ requestUpdateMonitor (monitor) { - if (this._monitorState.has(monitor.get('id'))) { + const id = monitor.get('id'); + if (this._monitorState.has(id)) { this._monitorState = - this._monitorState.set(monitor.get('id'), this._monitorState.get(monitor.get('id')).merge(monitor)); + this._monitorState.set(id, this._monitorState.get(id).merge(monitor)); } } @@ -1347,6 +1359,15 @@ class Runtime extends EventEmitter { this._monitorState = this._monitorState.delete(monitorId); } + /** + * Removes all monitors with the given target ID from the state. Does nothing if + * the monitor already does not exist in the state. + * @param {!string} targetId Remove all monitors with given target ID. + */ + requestRemoveMonitorByTargetId (targetId) { + this._monitorState = this._monitorState.filterNot(value => value.targetId === targetId); + } + /** * Get a target by its id. * @param {string} targetId Id of target to find. diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 6dfc7acade..994de9860a 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -484,6 +484,7 @@ class VirtualMachine extends EventEmitter { if (!sprite) { throw new Error('No sprite associated with this target.'); } + this.runtime.requestRemoveMonitorByTargetId(targetId); const currentEditingTarget = this.editingTarget; for (let i = 0; i < sprite.clones.length; i++) { const clone = sprite.clones[i]; From 2ff75f7572467bbfdda4d690578c02b488c000d5 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 4 Dec 2017 11:41:16 -0500 Subject: [PATCH 1576/1971] Merge pull request #841 from sjhuang26/issue-795-clear-answer Clear answer on green flag --- packages/scratch-vm/src/engine/runtime.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 8131bdd865..a8e044e372 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -317,6 +317,15 @@ class Runtime extends EventEmitter { return 'BLOCK_GLOW_OFF'; } + /** + * Event name when the project is started (threads may not necessarily be + * running). + * @const {string} + */ + static get PROJECT_START () { + return 'PROJECT_START'; + } + /** * Event name when threads start running. * Used by the UI to indicate running status. @@ -1057,6 +1066,7 @@ class Runtime extends EventEmitter { */ greenFlag () { this.stopAll(); + this.emit(Runtime.PROJECT_START); this.ioDevices.clock.resetProjectTimer(); this.clearEdgeActivatedValues(); // Inform all targets of the green flag. From e386f10cc54e8c7cf21e5224142f3127fc60d28d Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 5 Dec 2017 15:43:03 -0500 Subject: [PATCH 1577/1971] Merge pull request #838 from ericrosenbaum/feature/move-extensions-into-folder Move pen and wedo into extensions folder --- .../scratch-vm/src/extension-support/extension-manager.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index c3346a22db..af0dce066a 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -6,8 +6,8 @@ const BlockType = require('./block-type'); // These extensions are currently built into the VM repository but should not be loaded at startup. // TODO: move these out into a separate repository? // TODO: change extension spec so that library info, including extension ID, can be collected through static methods -const Scratch3PenBlocks = require('../blocks/scratch3_pen'); -const Scratch3WeDo2Blocks = require('../blocks/scratch3_wedo2'); +const Scratch3PenBlocks = require('../extensions/scratch3_pen'); +const Scratch3WeDo2Blocks = require('../extensions/scratch3_wedo2'); const Scratch3MusicBlocks = require('../extensions/scratch3_music'); const builtinExtensions = { pen: Scratch3PenBlocks, From eee621528b05c7431b3af4bf2bfa9f665f091bcf Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 6 Dec 2017 17:18:10 -0800 Subject: [PATCH 1578/1971] Merge pull request #793 from cwillisf/remove-threads-safely Make _removeThread safe during thread iteration --- packages/scratch-vm/src/engine/runtime.js | 28 ++++++++++------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index a8e044e372..d0c09cc704 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -806,17 +806,14 @@ class Runtime extends EventEmitter { } /** - * Remove a thread from the list of threads. - * @param {?Thread} thread Thread object to remove from actives + * Stop a thread: stop running it immediately, and remove it from the thread list later. + * @param {!Thread} thread Thread object to remove from actives */ - _removeThread (thread) { + _stopThread (thread) { + // Mark the thread for later removal + thread.isKilled = true; // Inform sequencer to stop executing that thread. this.sequencer.retireThread(thread); - // Remove from the list. - const i = this.threads.indexOf(thread); - if (i > -1) { - this.threads.splice(i, 1); - } } /** @@ -879,7 +876,7 @@ class Runtime extends EventEmitter { // edge activated hat thread that runs every frame continue; } - this._removeThread(this.threads[i]); + this._stopThread(this.threads[i]); return; } } @@ -1055,8 +1052,7 @@ class Runtime extends EventEmitter { continue; } if (this.threads[i].target === target) { - this.threads[i].isKilled = true; - this._removeThread(this.threads[i]); + this._stopThread(this.threads[i]); } } } @@ -1096,11 +1092,7 @@ class Runtime extends EventEmitter { } this.targets = newTargets; // Dispose all threads. - const threadsCopy = this.threads.slice(); - while (threadsCopy.length > 0) { - const poppedThread = threadsCopy.pop(); - this._removeThread(poppedThread); - } + this.threads.forEach(thread => this._stopThread(thread)); } /** @@ -1114,6 +1106,10 @@ class Runtime extends EventEmitter { } this.profiler.start(stepProfilerId); } + + // Clean up threads that were told to stop during or since the last step + this.threads = this.threads.filter(thread => !thread.isKilled); + // Find all edge-activated hats, and add them to threads to be evaluated. for (const hatType in this._hats) { if (!this._hats.hasOwnProperty(hatType)) continue; From 37b8fc20ca0a49cbcb0c73d00c8a3bd28f5eea47 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 8 Dec 2017 11:40:31 -0500 Subject: [PATCH 1579/1971] Merge pull request #825 from ericrosenbaum/bugfix/clones-get-audioplayer-ref MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clones get a reference to parent’s audioPlayer --- packages/scratch-vm/src/sprites/rendered-target.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 653bc4efd7..9e181b9573 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -137,7 +137,11 @@ class RenderedTarget extends Target { */ this.audioPlayer = null; if (this.runtime && this.runtime.audioEngine) { - this.audioPlayer = this.runtime.audioEngine.createPlayer(); + if (this.isOriginal) { + this.audioPlayer = this.runtime.audioEngine.createPlayer(); + } else { + this.audioPlayer = this.sprite.clones[0].audioPlayer; + } } } From 86f5941894a4afc43368b9bb0196f56033723573 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Mon, 11 Dec 2017 15:41:45 -0500 Subject: [PATCH 1580/1971] Preliminary localization (#777) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * localize the block and menu strings in the pen extension * adds .tx/config to be able to push translations to transifex * includes format-message to localize strings and extracting them. * add setLocale function to VM to allow GUI to pass in locale data. * refresh block definitions when the locale changes. ### Still to be decided For now just extracting messages from the pen extension into their own file. We’ll need to decide if each category gets its own file, or group all the strings into one resource. --- packages/scratch-vm/src/engine/runtime.js | 43 +++++++++++++++++++ .../extension-support/extension-manager.js | 26 +++++++++-- packages/scratch-vm/src/virtual-machine.js | 16 +++++++ 3 files changed, 81 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index d0c09cc704..7ab5aa2b1d 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -385,6 +385,14 @@ class Runtime extends EventEmitter { return 'EXTENSION_ADDED'; } + /** + * Event name for reporting that blocksInfo was updated. + * @const {string} + */ + static get BLOCKSINFO_UPDATE () { + return 'BLOCKSINFO_UPDATE'; + } + /** * How rapidly we try to step threads by default, in ms. */ @@ -497,6 +505,41 @@ class Runtime extends EventEmitter { this.emit(Runtime.EXTENSION_ADDED, categoryInfo.blocks.concat(categoryInfo.menus)); } + /** + * Reregister the primitives for an extension + * @param {ExtensionInfo} extensionInfo - new info (results of running getInfo) + * for an extension + * @private + */ + _refreshExtensionPrimitives (extensionInfo) { + let extensionBlocks = []; + for (const categoryInfo of this._blockInfo) { + if (extensionInfo.id === categoryInfo.id) { + categoryInfo.blocks = []; + categoryInfo.menus = []; + for (const menuName in extensionInfo.menus) { + if (extensionInfo.menus.hasOwnProperty(menuName)) { + const menuItems = extensionInfo.menus[menuName]; + const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuItems, categoryInfo); + categoryInfo.menus.push(convertedMenu); + } + } + for (const blockInfo of extensionInfo.blocks) { + const convertedBlock = this._convertForScratchBlocks(blockInfo, categoryInfo); + const opcode = convertedBlock.json.type; + categoryInfo.blocks.push(convertedBlock); + this._primitives[opcode] = convertedBlock.info.func; + if (blockInfo.blockType === BlockType.HAT) { + this._hats[opcode] = {edgeActivated: true}; /** @TODO let extension specify this */ + } + } + extensionBlocks = extensionBlocks.concat(categoryInfo.blocks, categoryInfo.menus); + } + } + + this.emit(Runtime.BLOCKSINFO_UPDATE, extensionBlocks); + } + /** * Build the scratch-blocks JSON for a menu. Note that scratch-blocks treats menus as a special kind of block. * @param {string} menuName - the name of the menu diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index af0dce066a..3228f5c2a4 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -78,7 +78,7 @@ class ExtensionManager { * @type {Set.} * @private */ - this._loadedExtensions = new Set(); + this._loadedExtensions = new Map(); /** * Keep a reference to the runtime so we can construct internal extension objects. @@ -119,8 +119,8 @@ class ExtensionManager { const extension = builtinExtensions[extensionURL]; const extensionInstance = new extension(this.runtime); - return this._registerInternalExtension(extensionInstance).then(() => { - this._loadedExtensions.add(extensionURL); + return this._registerInternalExtension(extensionInstance).then(serviceName => { + this._loadedExtensions.set(extensionURL, serviceName); }); } @@ -133,6 +133,21 @@ class ExtensionManager { }); } + /** + * regenerate blockinfo for any loaded extensions + */ + refreshBlocks () { + this._loadedExtensions.forEach(serviceName => { + dispatch.call(serviceName, 'getInfo') + .then(info => { + dispatch.call('runtime', '_refreshExtensionPrimitives', info); + }) + .catch(e => { + log.error(`Failed to refresh buildtin extension primitives: ${JSON.stringify(e)}`); + }); + }); + } + allocateWorker () { const id = this.nextExtensionWorker++; const workerInfo = this.pendingExtensions.shift(); @@ -175,7 +190,10 @@ class ExtensionManager { const fakeWorkerId = this.nextExtensionWorker++; const serviceName = `extension.${fakeWorkerId}.${extensionInfo.id}`; return dispatch.setService(serviceName, extensionObject) - .then(() => dispatch.call('extensions', 'registerExtensionService', serviceName)); + .then(() => { + dispatch.call('extensions', 'registerExtensionService', serviceName); + return serviceName; + }); } /** diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 994de9860a..7c1605d47d 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -7,6 +7,7 @@ const Runtime = require('./engine/runtime'); const sb2 = require('./serialization/sb2'); const sb3 = require('./serialization/sb3'); const StringUtil = require('./util/string-util'); +const formatMessage = require('format-message'); const {loadCostume} = require('./import/load-costume.js'); const {loadSound} = require('./import/load-sound.js'); @@ -67,6 +68,9 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.EXTENSION_ADDED, blocksInfo => { this.emit(Runtime.EXTENSION_ADDED, blocksInfo); }); + this.runtime.on(Runtime.BLOCKSINFO_UPDATE, blocksInfo => { + this.emit(Runtime.BLOCKSINFO_UPDATE, blocksInfo); + }); this.extensionManager = new ExtensionManager(this.runtime); @@ -552,6 +556,18 @@ class VirtualMachine extends EventEmitter { this.runtime.attachStorage(storage); } + /** + * set the current locale and builtin messages for the VM + * @param {[type]} locale current locale + * @param {[type]} messages builtin messages map for current locale + */ + setLocale (locale, messages) { + if (locale !== formatMessage.setup().locale) { + formatMessage.setup({locale: locale, translations: {[locale]: messages}}); + this.extensionManager.refreshBlocks(); + } + } + /** * Handle a Blockly event for the current editing target. * @param {!Blockly.Event} e Any Blockly event. From 71268463beac55cc89abedd20a447b66999ed11f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 12 Dec 2017 10:55:27 -0500 Subject: [PATCH 1581/1971] Merge pull request #810 from paulkaplan/select-after-drag Select target after drag end --- packages/scratch-vm/src/virtual-machine.js | 23 ++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 7c1605d47d..1c8df4df92 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -37,6 +37,13 @@ class VirtualMachine extends EventEmitter { * @type {Target} */ this.editingTarget = null; + + /** + * The currently dragging target, for redirecting IO data. + * @type {Target} + */ + this._dragTarget = null; + // Runtime emits are passed along as VM emits. this.runtime.on(Runtime.SCRIPT_GLOW_ON, glowData => { this.emit(Runtime.SCRIPT_GLOW_ON, glowData); @@ -708,8 +715,8 @@ class VirtualMachine extends EventEmitter { startDrag (targetId) { const target = this.runtime.getTargetById(targetId); if (target) { + this._dragTarget = target; target.startDrag(); - this.setEditingTarget(target.id); } } @@ -719,15 +726,23 @@ class VirtualMachine extends EventEmitter { */ stopDrag (targetId) { const target = this.runtime.getTargetById(targetId); - if (target) target.stopDrag(); + if (target) { + this._dragTarget = null; + target.stopDrag(); + this.setEditingTarget(target.id); + } } /** - * Post/edit sprite info for the current editing target. + * Post/edit sprite info for the current editing target or the drag target. * @param {object} data An object with sprite info data to set. */ postSpriteInfo (data) { - this.editingTarget.postSpriteInfo(data); + if (this._dragTarget) { + this._dragTarget.postSpriteInfo(data); + } else { + this.editingTarget.postSpriteInfo(data); + } } } From 77f18266e612f9a2d6410b2ad705c1dd4b148655 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 13 Dec 2017 15:00:07 -0500 Subject: [PATCH 1582/1971] Merge pull request #854 from ericrosenbaum/bugfix/extension-block-outline-colour Fix tertiary color of extension blocks --- packages/scratch-vm/src/engine/runtime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 7ab5aa2b1d..9c7f3e9e4a 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -602,7 +602,7 @@ class Runtime extends EventEmitter { category: categoryInfo.name, colour: categoryInfo.color1, colourSecondary: categoryInfo.color2, - colorTertiary: categoryInfo.color3, + colourTertiary: categoryInfo.color3, args0: [] }; From 5b69f312ac1b5605326ef100c02aedd755d201d7 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 15 Dec 2017 11:16:56 -0500 Subject: [PATCH 1583/1971] Merge pull request #864 from paulkaplan/fix-promise-from-add-costume Return a promise to indicate when a costume has loaded. --- packages/scratch-vm/src/virtual-machine.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 1c8df4df92..92a8d97940 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -314,9 +314,10 @@ class VirtualMachine extends EventEmitter { * @property {number} rotationCenterX - the X component of the costume's origin. * @property {number} rotationCenterY - the Y component of the costume's origin. * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume. + * @returns {?Promise} - a promise that resolves when the costume has been added */ addCostume (md5ext, costumeObject) { - loadCostume(md5ext, costumeObject, this.runtime).then(() => { + return loadCostume(md5ext, costumeObject, this.runtime).then(() => { this.editingTarget.addCostume(costumeObject); this.editingTarget.setCostume( this.editingTarget.sprite.costumes.length - 1 From 312fae1c7de0c609fc4e3f3bce3cdc49d3066179 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 15 Dec 2017 15:27:26 -0500 Subject: [PATCH 1584/1971] Merge pull request #863 from ericrosenbaum/feature/blockly-extension-for-scratch-extensions Add blockly extension for scratch extensions --- packages/scratch-vm/src/engine/runtime.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 9c7f3e9e4a..2215a02ab0 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -603,7 +603,8 @@ class Runtime extends EventEmitter { colour: categoryInfo.color1, colourSecondary: categoryInfo.color2, colourTertiary: categoryInfo.color3, - args0: [] + args0: [], + extensions: ['scratch_extension'] }; const inputList = []; From dc90d4c03ae4d7448fe857aa1e29aff86a6968bb Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 19 Dec 2017 13:58:28 -0500 Subject: [PATCH 1585/1971] Merge pull request #869 from ericrosenbaum/feature/add-extension-separator Add separator to extension block definition --- packages/scratch-vm/src/engine/runtime.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 2215a02ab0..73d1e10a45 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -617,16 +617,20 @@ class Runtime extends EventEmitter { blockJSON.message0 = ''; - // If an icon for the extension exists, prepend it to each block + // If an icon for the extension exists, prepend it to each block, with a vertical separator. if (categoryInfo.iconURI) { - blockJSON.message0 = '%1'; + blockJSON.message0 = '%1 %2'; const iconJSON = { type: 'field_image', src: categoryInfo.iconURI, width: 40, height: 40 }; + const separatorJSON = { + type: 'field_vertical_separator' + }; blockJSON.args0.push(iconJSON); + blockJSON.args0.push(separatorJSON); } blockJSON.message0 += blockInfo.text.replace(/\[(.+?)]/g, (match, placeholder) => { From 7206bb85309c7e58898d389e63800e8d71ed2a6d Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 19 Dec 2017 16:27:07 -0500 Subject: [PATCH 1586/1971] Merge pull request #871 from ericrosenbaum/feature/extension-category-icons Category menu icons for extensions --- packages/scratch-vm/src/engine/runtime.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 73d1e10a45..bf7969a0ce 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -475,7 +475,8 @@ class Runtime extends EventEmitter { const categoryInfo = { id: extensionInfo.id, name: extensionInfo.name, - iconURI: extensionInfo.iconURI, + blockIconURI: extensionInfo.blockIconURI, + menuIconURI: extensionInfo.menuIconURI, color1: '#FF6680', color2: '#FF4D6A', color3: '#FF3355', @@ -618,11 +619,11 @@ class Runtime extends EventEmitter { blockJSON.message0 = ''; // If an icon for the extension exists, prepend it to each block, with a vertical separator. - if (categoryInfo.iconURI) { + if (categoryInfo.blockIconURI) { blockJSON.message0 = '%1 %2'; const iconJSON = { type: 'field_image', - src: categoryInfo.iconURI, + src: categoryInfo.blockIconURI, width: 40, height: 40 }; @@ -739,7 +740,20 @@ class Runtime extends EventEmitter { for (const categoryInfo of this._blockInfo) { const {name, color1, color2} = categoryInfo; const paletteBlocks = categoryInfo.blocks.filter(block => !block.info.hideFromPalette); - xmlParts.push(``); + const colorXML = `colour="${color1}" secondaryColour="${color2}"`; + + // Use a menu icon if there is one. Otherwise, use the block icon. If there's no icon, + // the category menu will show its default colored circle. + let menuIconURI = ''; + if (categoryInfo.menuIconURI) { + menuIconURI = categoryInfo.menuIconURI; + } else if (categoryInfo.blockIconURI) { + menuIconURI = categoryInfo.blockIconURI; + } + const menuIconXML = menuIconURI ? + `iconURI="${menuIconURI}"` : ''; + + xmlParts.push(``); xmlParts.push.apply(xmlParts, paletteBlocks.map(block => block.xml)); xmlParts.push(''); } From e25e8c5bbdb45deefe6502fec47ddd1b9ab70c58 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 19 Dec 2017 16:27:17 -0500 Subject: [PATCH 1587/1971] Merge pull request #870 from paulkaplan/fix-backdrop-promise Return promise from backdrop loading --- packages/scratch-vm/src/virtual-machine.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 92a8d97940..0881af2cf4 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -444,9 +444,10 @@ class VirtualMachine extends EventEmitter { * @property {number} rotationCenterX - the X component of the backdrop's origin. * @property {number} rotationCenterY - the Y component of the backdrop's origin. * @property {number} [bitmapResolution] - the resolution scale for a bitmap backdrop. + * @returns {?Promise} - a promise that resolves when the backdrop has been added */ addBackdrop (md5ext, backdropObject) { - loadCostume(md5ext, backdropObject, this.runtime).then(() => { + return loadCostume(md5ext, backdropObject, this.runtime).then(() => { const stage = this.runtime.getTargetForStage(); stage.sprite.costumes.push(backdropObject); stage.setCostume(stage.sprite.costumes.length - 1); From fb0ea94054bd46818cff044066dfe717f2052e75 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 20 Dec 2017 14:44:12 -0500 Subject: [PATCH 1588/1971] Merge pull request #874 from thisandagain/feature/733 Accept both objects and strings for 'loadProject' --- packages/scratch-vm/src/virtual-machine.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 0881af2cf4..e0bb9961b4 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -217,16 +217,14 @@ class VirtualMachine extends EventEmitter { this.clear(); // Validate & parse - if (typeof json !== 'string') { - log.error('Failed to parse project. Non-string supplied to fromJSON.'); - return; - } - json = JSON.parse(json); - if (typeof json !== 'object') { - log.error('Failed to parse project. JSON supplied to fromJSON is not an object.'); + if (typeof json !== 'string' && typeof json !== 'object') { + log.error('Failed to parse project. Invalid type supplied to fromJSON.'); return; } + // Attempt to parse JSON if string is supplied + if (typeof json === 'string') json = JSON.parse(json); + // Establish version, deserialize, and load into runtime // @todo Support Scratch 1.4 // @todo This is an extremely naïve / dangerous way of determining version. From f587825a3e15ee784956cc9e0df2a5c5ef92d38d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 21 Dec 2017 15:48:07 -0500 Subject: [PATCH 1589/1971] Merge pull request #876 from paulkaplan/add-size-setter Allow size to be set through postSpriteInfo, and emit updates when it is --- packages/scratch-vm/src/sprites/rendered-target.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 9e181b9573..c41b967a35 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -329,6 +329,7 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } + this.runtime.requestTargetsUpdate(this); } /** @@ -862,6 +863,9 @@ class RenderedTarget extends Target { if (data.hasOwnProperty('visible')) { this.setVisible(data.visible); } + if (data.hasOwnProperty('size')) { + this.setSize(data.size); + } } /** From e569327051cf2cef527c26c34da55844bc3131bf Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 27 Dec 2017 11:47:56 -0500 Subject: [PATCH 1590/1971] Merge pull request #860 from kchadha/broadcast-input-functionality Broadcast Inputs and Implicit Message Deletion --- packages/scratch-vm/src/virtual-machine.js | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index e0bb9961b4..7b997dbdd8 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -8,6 +8,7 @@ const sb2 = require('./serialization/sb2'); const sb3 = require('./serialization/sb3'); const StringUtil = require('./util/string-util'); const formatMessage = require('format-message'); +const Variable = require('./engine/variable'); const {loadCostume} = require('./import/load-costume.js'); const {loadSound} = require('./import/load-sound.js'); @@ -677,6 +678,35 @@ class VirtualMachine extends EventEmitter { * of the current editing target's blocks. */ emitWorkspaceUpdate () { + // Create a list of broadcast message Ids according to the stage variables + const stageVariables = this.runtime.getTargetForStage().variables; + let messageIds = []; + for (const varId in stageVariables) { + if (stageVariables[varId].type === Variable.BROADCAST_MESSAGE_TYPE) { + messageIds.push(varId); + } + } + // Go through all blocks on all targets, removing referenced + // broadcast ids from the list. + for (let i = 0; i < this.runtime.targets.length; i++) { + const currTarget = this.runtime.targets[i]; + const currBlocks = currTarget.blocks._blocks; + for (const blockId in currBlocks) { + if (currBlocks[blockId].fields.BROADCAST_OPTION) { + const id = currBlocks[blockId].fields.BROADCAST_OPTION.id; + const index = messageIds.indexOf(id); + if (index !== -1) { + messageIds = messageIds.slice(0, index) + .concat(messageIds.slice(index + 1)); + } + } + } + } + // Anything left in messageIds is not referenced by a block, so delete it. + for (let i = 0; i < messageIds.length; i++) { + const id = messageIds[i]; + delete this.runtime.getTargetForStage().variables[id]; + } const variableMap = Object.assign({}, this.runtime.getTargetForStage().variables, this.editingTarget.variables From fad5396778fab712a1e6d709d46d4f551c84e980 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 29 Dec 2017 09:34:40 -0500 Subject: [PATCH 1591/1971] Merge pull request #880 from paulkaplan/layering-blocks New layering blocks --- .../scratch-vm/src/sprites/rendered-target.js | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index c41b967a35..85c1d53659 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -699,10 +699,29 @@ class RenderedTarget extends Target { } /** - * Move back a number of layers. - * @param {number} nLayers How many layers to go back. + * Move to the back layer. */ - goBackLayers (nLayers) { + goToBack () { + if (this.renderer) { + this.renderer.setDrawableOrder(this.drawableID, -Infinity, false, 1); + } + } + + /** + * Move forward a number of layers. + * @param {number} nLayers How many layers to go forward. + */ + goForwardLayers (nLayers) { + if (this.renderer) { + this.renderer.setDrawableOrder(this.drawableID, nLayers, true, 1); + } + } + + /** + * Move backward a number of layers. + * @param {number} nLayers How many layers to go backward. + */ + goBackwardLayers (nLayers) { if (this.renderer) { this.renderer.setDrawableOrder(this.drawableID, -nLayers, true, 1); } From 7960fcf08daf9d1aa23daabdb10bf9f1a8d25b7a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 29 Dec 2017 10:22:18 -0500 Subject: [PATCH 1592/1971] Merge pull request #881 from sjhuang26/issue-gui1127-pen-trails No pen trails when dragging --- packages/scratch-vm/src/sprites/rendered-target.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 85c1d53659..1288240da9 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -203,7 +203,7 @@ class RenderedTarget extends Target { this.x = x; this.y = y; } - this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY); + this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY, force); this.runtime.requestTargetsUpdate(this); } @@ -864,11 +864,10 @@ class RenderedTarget extends Target { */ postSpriteInfo (data) { const force = data.hasOwnProperty('force') ? data.force : null; - if (data.hasOwnProperty('x')) { - this.setXY(data.x, this.y, force); - } - if (data.hasOwnProperty('y')) { - this.setXY(this.x, data.y, force); + const isXChanged = data.hasOwnProperty('x'); + const isYChanged = data.hasOwnProperty('y'); + if (isXChanged || isYChanged) { + this.setXY(isXChanged ? data.x : this.x, isYChanged ? data.y : this.y, force); } if (data.hasOwnProperty('direction')) { this.setDirection(data.direction); From 1fcb00ecd0397dfc38f2bf122c3187616c6ec792 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 12 Jan 2018 10:33:43 -0500 Subject: [PATCH 1593/1971] Merge pull request #893 from fsih/nameName Unique sprite and backdrop names --- packages/scratch-vm/src/virtual-machine.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 7b997dbdd8..61b61e5184 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -268,6 +268,8 @@ class VirtualMachine extends EventEmitter { targets.forEach(target => { this.runtime.targets.push(target); (/** @type RenderedTarget */ target).updateAllDrawableProperties(); + // Ensure unique sprite name + if (target.isSprite()) this.renameSprite(target.id, target.getName()); }); // Select the first target for editing, e.g., the first sprite. if (wholeProject && (targets.length > 1)) { @@ -448,7 +450,7 @@ class VirtualMachine extends EventEmitter { addBackdrop (md5ext, backdropObject) { return loadCostume(md5ext, backdropObject, this.runtime).then(() => { const stage = this.runtime.getTargetForStage(); - stage.sprite.costumes.push(backdropObject); + stage.addCostume(backdropObject); stage.setCostume(stage.sprite.costumes.length - 1); }); } @@ -629,7 +631,7 @@ class VirtualMachine extends EventEmitter { */ setEditingTarget (targetId) { // Has the target id changed? If not, exit. - if (targetId === this.editingTarget.id) { + if (this.editingTarget && targetId === this.editingTarget.id) { return; } const target = this.runtime.getTargetById(targetId); From 8be47d423392b1744cbea4e892d62afd487ce4ca Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 12 Jan 2018 14:49:27 -0500 Subject: [PATCH 1594/1971] Merge pull request #894 from paulkaplan/fix-duplicate-drag Fix dragging duplicated sprites --- packages/scratch-vm/src/sprites/rendered-target.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 1288240da9..fbd6b4bc81 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -831,7 +831,6 @@ class RenderedTarget extends Target { newTarget.effects = JSON.parse(JSON.stringify(this.effects)); newTarget.variables = JSON.parse(JSON.stringify(this.variables)); newTarget.lists = JSON.parse(JSON.stringify(this.lists)); - newTarget.initDrawable(); newTarget.updateAllDrawableProperties(); newTarget.goBehindOther(this); return newTarget; From 0d43e4da52c5421267baf19eb5dc896e4b456c34 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 17 Jan 2018 10:02:10 -0500 Subject: [PATCH 1595/1971] Merge pull request #897 from fsih/textencoding Use text-encoding library for text encoder --- packages/scratch-vm/src/virtual-machine.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 61b61e5184..0612261a9d 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -1,3 +1,4 @@ +const TextEncoder = require('text-encoding'); const EventEmitter = require('events'); const centralDispatch = require('./dispatch/central-dispatch'); From 20da264b93dc09a6cd05186e9334e1cc7177e4e7 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 17 Jan 2018 10:59:42 -0500 Subject: [PATCH 1596/1971] Merge pull request #898 from LLK/revert-897-textencoding Revert "Use text-encoding library for text encoder" --- packages/scratch-vm/src/virtual-machine.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 0612261a9d..61b61e5184 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -1,4 +1,3 @@ -const TextEncoder = require('text-encoding'); const EventEmitter = require('events'); const centralDispatch = require('./dispatch/central-dispatch'); From b6c4a328ac51720d0e4bc6d44dbe4d0f48585de4 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 17 Jan 2018 12:24:33 -0500 Subject: [PATCH 1597/1971] Merge pull request #900 from paulkaplan/fix-text-encoder-require Require TextEncoder correctly --- packages/scratch-vm/src/virtual-machine.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 61b61e5184..cab4090857 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -1,3 +1,4 @@ +const TextEncoder = require('text-encoding').TextEncoder; const EventEmitter = require('events'); const centralDispatch = require('./dispatch/central-dispatch'); From 855656d3e9f3501352c8893252459a1636adf939 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 17 Jan 2018 13:49:23 -0500 Subject: [PATCH 1598/1971] Merge pull request #889 from paulkaplan/fix-mouse-positions Fix mouse positions for different stage zoom levels --- packages/scratch-vm/src/sprites/rendered-target.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index fbd6b4bc81..5f126256f9 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -616,8 +616,7 @@ class RenderedTarget extends Target { // Limits test to this Drawable, so this will return true // even if the clone is obscured by another Drawable. const pickResult = this.runtime.renderer.pick( - x + (this.runtime.constructor.STAGE_WIDTH / 2), - -y + (this.runtime.constructor.STAGE_HEIGHT / 2), + x, y, null, null, [this.drawableID] ); From 6522bdf3c75257b47c2966ec09483fb7533c898f Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 23 Jan 2018 11:55:39 -0500 Subject: [PATCH 1599/1971] Merge pull request #891 from ericrosenbaum/bugfix/audioplayer-clone-state Audio effects state for clones --- .../scratch-vm/src/sprites/rendered-target.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 5f126256f9..515a30a0ed 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -131,17 +131,15 @@ class RenderedTarget extends Target { 'control_start_as_clone', null, this ); } + } - /** - * Audio player - */ + /** + * Initialize the audio player for this sprite or clone. + */ + initAudio () { this.audioPlayer = null; if (this.runtime && this.runtime.audioEngine) { - if (this.isOriginal) { - this.audioPlayer = this.runtime.audioEngine.createPlayer(); - } else { - this.audioPlayer = this.sprite.clones[0].audioPlayer; - } + this.audioPlayer = this.runtime.audioEngine.createPlayer(); } } @@ -940,6 +938,10 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } + if (this.audioPlayer) { + this.audioPlayer.stopAllSounds(); + this.audioPlayer.dispose(); + } } } From 49a299d03fa1b4b64d6b75f7f292aaa408a86e92 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Fri, 26 Jan 2018 15:48:05 -0800 Subject: [PATCH 1600/1971] Merge pull request #917 from mzgoddard/push-reported Push reported --- packages/scratch-vm/src/engine/runtime.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index bf7969a0ce..ea86a9536c 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -861,6 +861,9 @@ class Runtime extends EventEmitter { thread.target = target; thread.stackClick = opts.stackClick; thread.updateMonitor = opts.updateMonitor; + thread.blockContainer = opts.updateMonitor ? + this.monitorBlocks : + target.blocks; thread.pushStack(id); this.threads.push(thread); @@ -890,6 +893,7 @@ class Runtime extends EventEmitter { newThread.target = thread.target; newThread.stackClick = thread.stackClick; newThread.updateMonitor = thread.updateMonitor; + newThread.blockContainer = thread.blockContainer; newThread.pushStack(thread.topBlock); const i = this.threads.indexOf(thread); if (i > -1) { From f78ebd081c739794cf8ac86bd5560980a4a7b9e5 Mon Sep 17 00:00:00 2001 From: kchadha Date: Fri, 2 Feb 2018 13:49:36 -0500 Subject: [PATCH 1601/1971] Merge pull request #928 from LLK/revert-917-push-reported Revert "Push reported" --- packages/scratch-vm/src/engine/runtime.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index ea86a9536c..bf7969a0ce 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -861,9 +861,6 @@ class Runtime extends EventEmitter { thread.target = target; thread.stackClick = opts.stackClick; thread.updateMonitor = opts.updateMonitor; - thread.blockContainer = opts.updateMonitor ? - this.monitorBlocks : - target.blocks; thread.pushStack(id); this.threads.push(thread); @@ -893,7 +890,6 @@ class Runtime extends EventEmitter { newThread.target = thread.target; newThread.stackClick = thread.stackClick; newThread.updateMonitor = thread.updateMonitor; - newThread.blockContainer = thread.blockContainer; newThread.pushStack(thread.topBlock); const i = this.threads.indexOf(thread); if (i > -1) { From 3ed2e0cb360e4478f1dd6e8b4d79b7a0403e53db Mon Sep 17 00:00:00 2001 From: kchadha Date: Mon, 12 Feb 2018 11:53:50 -0500 Subject: [PATCH 1602/1971] Merge pull request #931 from kchadha/bugfix-912 Renaming sprite/costume/sound/backdrop updates corresponding blocks. --- .../scratch-vm/src/sprites/rendered-target.js | 22 +++++++++++++++++-- packages/scratch-vm/src/virtual-machine.js | 9 +++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 515a30a0ed..2a6a0f7d3c 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -417,7 +417,22 @@ class RenderedTarget extends Target { const usedNames = this.sprite.costumes .filter((costume, index) => costumeIndex !== index) .map(costume => costume.name); - this.sprite.costumes[costumeIndex].name = StringUtil.unusedName(newName, usedNames); + const oldName = this.sprite.costumes[costumeIndex].name; + const newUnusedName = StringUtil.unusedName(newName, usedNames); + this.sprite.costumes[costumeIndex].name = newUnusedName; + + if (this.isStage) { + // Since this is a backdrop, go through all targets and + // update any blocks referencing the old backdrop name + const targets = this.runtime.targets; + for (let i = 0; i < targets.length; i++) { + const currTarget = targets[i]; + currTarget.blocks.updateAssetName(oldName, newUnusedName, 'backdrop'); + } + } else { + this.blocks.updateAssetName(oldName, newUnusedName, 'costume'); + } + } /** @@ -462,7 +477,10 @@ class RenderedTarget extends Target { const usedNames = this.sprite.sounds .filter((sound, index) => soundIndex !== index) .map(sound => sound.name); - this.sprite.sounds[soundIndex].name = StringUtil.unusedName(newName, usedNames); + const oldName = this.sprite.sounds[soundIndex].name; + const newUnusedName = StringUtil.unusedName(newName, usedNames); + this.sprite.sounds[soundIndex].name = newUnusedName; + this.blocks.updateAssetName(oldName, newUnusedName, 'sound'); } /** diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index cab4090857..d930ad5349 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -475,7 +475,14 @@ class VirtualMachine extends EventEmitter { const names = this.runtime.targets .filter(runtimeTarget => runtimeTarget.isSprite() && runtimeTarget.id !== target.id) .map(runtimeTarget => runtimeTarget.sprite.name); - sprite.name = StringUtil.unusedName(newName, names); + const oldName = sprite.name; + const newUnusedName = StringUtil.unusedName(newName, names); + sprite.name = newUnusedName; + const allTargets = this.runtime.targets; + for (let i = 0; i < allTargets.length; i++) { + const currTarget = allTargets[i]; + currTarget.blocks.updateAssetName(oldName, newName, 'sprite'); + } } this.emitTargetsUpdate(); } else { From f93edefb8d78a81adbb3cddc4a5a9783aac06ca8 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 13 Feb 2018 19:44:04 -0500 Subject: [PATCH 1603/1971] Merge pull request #933 from ericrosenbaum/bugfix/handle-sprites-named-stage When getting sprite by name, skip the stage --- packages/scratch-vm/src/engine/runtime.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index bf7969a0ce..739d36295d 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1458,6 +1458,9 @@ class Runtime extends EventEmitter { getSpriteTargetByName (spriteName) { for (let i = 0; i < this.targets.length; i++) { const target = this.targets[i]; + if (target.isStage) { + continue; + } if (target.sprite && target.sprite.name === spriteName) { return target; } From bcf44de3f45c14c8749960ee5b14f27a7148fcc1 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Fri, 16 Feb 2018 12:21:27 -0800 Subject: [PATCH 1604/1971] Merge pull request #920 from towerofnix/scroll-detection Scroll wheel detection for "when key (up/down) pressed" blocks --- packages/scratch-vm/src/engine/runtime.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 739d36295d..aded2ccfb3 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -14,6 +14,7 @@ const Clock = require('../io/clock'); const DeviceManager = require('../io/deviceManager'); const Keyboard = require('../io/keyboard'); const Mouse = require('../io/mouse'); +const MouseWheel = require('../io/mouseWheel'); const defaultBlockPackages = { scratch3_control: require('../blocks/scratch3_control'), @@ -258,7 +259,8 @@ class Runtime extends EventEmitter { clock: new Clock(), deviceManager: new DeviceManager(), keyboard: new Keyboard(this), - mouse: new Mouse(this) + mouse: new Mouse(this), + mouseWheel: new MouseWheel(this) }; /** From 16dc2b8cfa5736ed90d4d1d4647b1dcfa00acd73 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 26 Feb 2018 07:59:32 -0500 Subject: [PATCH 1605/1971] Merge pull request #944 from fsih/duplicateCostume Duplicate costume --- .../scratch-vm/src/sprites/rendered-target.js | 18 ++++-- packages/scratch-vm/src/virtual-machine.js | 58 ++++++++++++++++++- 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 2a6a0f7d3c..ae02b72bd4 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -401,11 +401,16 @@ class RenderedTarget extends Target { /** * Add a costume, taking care to avoid duplicate names. * @param {!object} costumeObject Object representing the costume. + * @param {?int} index Index at which to add costume */ - addCostume (costumeObject) { + addCostume (costumeObject, index) { const usedNames = this.sprite.costumes.map(costume => costume.name); costumeObject.name = StringUtil.unusedName(costumeObject.name, usedNames); - this.sprite.costumes.push(costumeObject); + if (index) { + this.sprite.costumes.splice(index, 0, costumeObject); + } else { + this.sprite.costumes.push(costumeObject); + } } /** @@ -461,11 +466,16 @@ class RenderedTarget extends Target { /** * Add a sound, taking care to avoid duplicate names. * @param {!object} soundObject Object representing the sound. + * @param {?int} index Index at which to add costume */ - addSound (soundObject) { + addSound (soundObject, index) { const usedNames = this.sprite.sounds.map(sound => sound.name); soundObject.name = StringUtil.unusedName(soundObject.name, usedNames); - this.sprite.sounds.push(soundObject); + if (index) { + this.sprite.sounds.splice(index, 0, soundObject); + } else { + this.sprite.sounds.push(soundObject); + } } /** diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index d930ad5349..f9fa45be38 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -322,11 +322,41 @@ class VirtualMachine extends EventEmitter { return loadCostume(md5ext, costumeObject, this.runtime).then(() => { this.editingTarget.addCostume(costumeObject); this.editingTarget.setCostume( - this.editingTarget.sprite.costumes.length - 1 + this.editingTarget.getCostumes().length - 1 ); }); } + /** + * Duplicate the costume at the given index. Add it at that index + 1. + * @param {!int} costumeIndex Index of costume to duplicate + * @returns {?Promise} - a promise that resolves when the costume has been decoded and added + */ + duplicateCostume (costumeIndex) { + const originalCostume = this.editingTarget.getCostumes()[costumeIndex]; + const clone = Object.assign({}, originalCostume); + const md5ext = `${clone.assetId}.${clone.dataFormat}`; + return loadCostume(md5ext, clone, this.runtime).then(() => { + this.editingTarget.addCostume(clone, costumeIndex + 1); + this.editingTarget.setCostume(costumeIndex + 1); + this.emitTargetsUpdate(); + }); + } + + /** + * Duplicate the sound at the given index. Add it at that index + 1. + * @param {!int} soundIndex Index of sound to duplicate + * @returns {?Promise} - a promise that resolves when the sound has been decoded and added + */ + duplicateSound (soundIndex) { + const originalSound = this.editingTarget.getSounds()[soundIndex]; + const clone = Object.assign({}, originalSound); + return loadSound(clone, this.runtime).then(() => { + this.editingTarget.addSound(clone, soundIndex + 1); + this.emitTargetsUpdate(); + }); + } + /** * Rename a costume on the current editing target. * @param {int} costumeIndex - the index of the costume to be renamed. @@ -384,12 +414,34 @@ class VirtualMachine extends EventEmitter { * Update a sound buffer. * @param {int} soundIndex - the index of the sound to be updated. * @param {AudioBuffer} newBuffer - new audio buffer for the audio engine. + * @param {ArrayBuffer} soundEncoding - the new (wav) encoded sound to be stored */ - updateSoundBuffer (soundIndex, newBuffer) { - const id = this.editingTarget.sprite.sounds[soundIndex].soundId; + updateSoundBuffer (soundIndex, newBuffer, soundEncoding) { + const sound = this.editingTarget.sprite.sounds[soundIndex]; + const id = sound ? sound.soundId : null; if (id && this.runtime && this.runtime.audioEngine) { this.runtime.audioEngine.updateSoundBuffer(id, newBuffer); } + // Update sound in runtime + if (soundEncoding) { + // Now that we updated the sound, the format should also be updated + // so that the sound can eventually be decoded the right way. + // Sounds that were formerly 'adpcm', but were updated in sound editor + // will not get decoded by the audio engine correctly unless the format + // is updated as below. + sound.format = ''; + const storage = this.runtime.storage; + sound.assetId = storage.builtinHelper.cache( + storage.AssetType.Sound, + storage.DataFormat.WAV, + soundEncoding + ); + sound.md5 = `${sound.assetId}.${sound.dataFormat}`; + } + // If soundEncoding is null, it's because gui had a problem + // encoding the updated sound. We don't want to store anything in this + // case, and gui should have logged an error. + this.emitTargetsUpdate(); } From ecd87f2cd8dc687d951ee450a01f90f7ef11e4ae Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 26 Feb 2018 16:26:53 -0500 Subject: [PATCH 1606/1971] Merge pull request #939 from fsih/blockDrag Block drag --- packages/scratch-vm/src/engine/runtime.js | 32 ++++++++++++++++++++++ packages/scratch-vm/src/virtual-machine.js | 19 +++++++++++++ 2 files changed, 51 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index aded2ccfb3..4da25f7bd3 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -379,6 +379,22 @@ class Runtime extends EventEmitter { return 'MONITORS_UPDATE'; } + /** + * Event name for block drag update. + * @const {string} + */ + static get BLOCK_DRAG_UPDATE () { + return 'BLOCK_DRAG_UPDATE'; + } + + /** + * Event name for block drag end. + * @const {string} + */ + static get BLOCK_DRAG_END () { + return 'BLOCK_DRAG_END'; + } + /** * Event name for reporting that an extension was added. * @const {string} @@ -1387,6 +1403,22 @@ class Runtime extends EventEmitter { } } + /** + * Emit whether blocks are being dragged over gui + * @param {boolean} areBlocksOverGui True if blocks are dragged out of blocks workspace, false otherwise + */ + emitBlockDragUpdate (areBlocksOverGui) { + this.emit(Runtime.BLOCK_DRAG_UPDATE, areBlocksOverGui); + } + + /** + * Emit event to indicate that the block drag has ended with the blocks outside the blocks workspace + * @param {Array.} blocks The set of blocks dragged to the GUI + */ + emitBlockEndDrag (blocks) { + this.emit(Runtime.BLOCK_DRAG_END, blocks); + } + /** * Emit value for reporter to show in the blocks. * @param {string} blockId ID for the block. diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index f9fa45be38..3d3c16791e 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -74,6 +74,12 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.MONITORS_UPDATE, monitorList => { this.emit(Runtime.MONITORS_UPDATE, monitorList); }); + this.runtime.on(Runtime.BLOCK_DRAG_UPDATE, areBlocksOverGui => { + this.emit(Runtime.BLOCK_DRAG_UPDATE, areBlocksOverGui); + }); + this.runtime.on(Runtime.BLOCK_DRAG_END, blocks => { + this.emit(Runtime.BLOCK_DRAG_END, blocks); + }); this.runtime.on(Runtime.EXTENSION_ADDED, blocksInfo => { this.emit(Runtime.EXTENSION_ADDED, blocksInfo); }); @@ -704,6 +710,19 @@ class VirtualMachine extends EventEmitter { } } + /** + * Called when blocks are dragged from one sprite to another. Adds the blocks to the + * workspace of the given target. + * @param {!Array} blocks Blocks to add. + * @param {!string} targetId Id of target to add blocks to. + */ + shareBlocksToTarget (blocks, targetId) { + const target = this.runtime.getTargetById(targetId); + for (let i = 0; i < blocks.length; i++) { + target.blocks.createBlock(blocks[i]); + } + } + /** * Repopulate the workspace with the blocks of the current editingTarget. This * allows us to get around bugs like gui#413. From 95cea810ca89bfaf68209051dfd585eb4bdb9416 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 1 Mar 2018 13:20:37 -0500 Subject: [PATCH 1607/1971] Merge pull request #953 from ericrosenbaum/bugfix/load-tempo-from-sb2 Load tempo from sb2 --- packages/scratch-vm/src/sprites/rendered-target.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index ae02b72bd4..538cc576ff 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -116,6 +116,12 @@ class RenderedTarget extends Target { * @type {!string} */ this.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND; + + /** + * Current tempo (used by the music extension) + * @type {number} + */ + this.tempo = 60; } /** From 3b89cc128f8673454943817050e4d8d5f087d19c Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 1 Mar 2018 17:34:40 -0500 Subject: [PATCH 1608/1971] Merge pull request #943 from fsih/privateSprite Make sprite.costumes private --- .../scratch-vm/src/sprites/rendered-target.js | 22 ++++++++----------- packages/scratch-vm/src/virtual-machine.js | 6 ++--- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 538cc576ff..8da2d2b5aa 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -381,7 +381,7 @@ class RenderedTarget extends Target { index, 0, this.sprite.costumes.length - 1 ); if (this.renderer) { - const costume = this.sprite.costumes[this.currentCostume]; + const costume = this.getCostumes()[this.currentCostume]; const drawableProperties = { skinId: costume.skinId, costumeResolution: costume.bitmapResolution @@ -410,12 +410,10 @@ class RenderedTarget extends Target { * @param {?int} index Index at which to add costume */ addCostume (costumeObject, index) { - const usedNames = this.sprite.costumes.map(costume => costume.name); - costumeObject.name = StringUtil.unusedName(costumeObject.name, usedNames); if (index) { - this.sprite.costumes.splice(index, 0, costumeObject); + this.sprite.addCostumeAt(costumeObject, index); } else { - this.sprite.costumes.push(costumeObject); + this.sprite.addCostumeAt(costumeObject, this.sprite.costumes.length); } } @@ -428,9 +426,9 @@ class RenderedTarget extends Target { const usedNames = this.sprite.costumes .filter((costume, index) => costumeIndex !== index) .map(costume => costume.name); - const oldName = this.sprite.costumes[costumeIndex].name; + const oldName = this.getCostumes()[costumeIndex].name; const newUnusedName = StringUtil.unusedName(newName, usedNames); - this.sprite.costumes[costumeIndex].name = newUnusedName; + this.getCostumes()[costumeIndex].name = newUnusedName; if (this.isStage) { // Since this is a backdrop, go through all targets and @@ -454,9 +452,7 @@ class RenderedTarget extends Target { const originalCostumeCount = this.sprite.costumes.length; if (originalCostumeCount === 1) return; - this.sprite.costumes = this.sprite.costumes - .slice(0, index) - .concat(this.sprite.costumes.slice(index + 1)); + this.sprite.deleteCostumeAt(index); if (index === this.currentCostume && index === originalCostumeCount - 1) { this.setCostume(index - 1); @@ -542,7 +538,7 @@ class RenderedTarget extends Target { */ getCostumeIndexByName (costumeName) { for (let i = 0; i < this.sprite.costumes.length; i++) { - if (this.sprite.costumes[i].name === costumeName) { + if (this.getCostumes()[i].name === costumeName) { return i; } } @@ -554,7 +550,7 @@ class RenderedTarget extends Target { * @return {object} current costume */ getCurrentCostume () { - return this.sprite.costumes[this.currentCostume]; + return this.getCostumes()[this.currentCostume]; } /** @@ -580,7 +576,7 @@ class RenderedTarget extends Target { updateAllDrawableProperties () { if (this.renderer) { const renderedDirectionScale = this._getRenderedDirectionAndScale(); - const costume = this.sprite.costumes[this.currentCostume]; + const costume = this.getCostumes()[this.currentCostume]; const bitmapResolution = costume.bitmapResolution || 1; const props = { position: [this.x, this.y], diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 3d3c16791e..a0063d5a5e 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -465,7 +465,7 @@ class VirtualMachine extends EventEmitter { * @return {string} the costume's SVG string, or null if it's not an SVG costume. */ getCostumeSvg (costumeIndex) { - const id = this.editingTarget.sprite.costumes[costumeIndex].assetId; + const id = this.editingTarget.getCostumes()[costumeIndex].assetId; if (id && this.runtime && this.runtime.storage && this.runtime.storage.get(id).dataFormat === 'svg') { return this.runtime.storage.get(id).decodeText(); @@ -481,7 +481,7 @@ class VirtualMachine extends EventEmitter { * @param {number} rotationCenterY y of point about which the costume rotates, relative to its upper left corner */ updateSvg (costumeIndex, svg, rotationCenterX, rotationCenterY) { - const costume = this.editingTarget.sprite.costumes[costumeIndex]; + const costume = this.editingTarget.getCostumes()[costumeIndex]; if (costume && this.runtime && this.runtime.renderer) { costume.rotationCenterX = rotationCenterX; costume.rotationCenterY = rotationCenterY; @@ -510,7 +510,7 @@ class VirtualMachine extends EventEmitter { return loadCostume(md5ext, backdropObject, this.runtime).then(() => { const stage = this.runtime.getTargetForStage(); stage.addCostume(backdropObject); - stage.setCostume(stage.sprite.costumes.length - 1); + stage.setCostume(stage.getCostumes().length - 1); }); } From 31fe37ca0c67d6343fba687dd204d64510d83431 Mon Sep 17 00:00:00 2001 From: kchadha Date: Fri, 2 Mar 2018 10:28:53 -0500 Subject: [PATCH 1609/1971] Merge pull request #956 from kchadha/validate-sb2 Validate sb2 --- packages/scratch-vm/src/virtual-machine.js | 28 +++++++++++++++------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index a0063d5a5e..d40a2a28be 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -9,6 +9,8 @@ const sb2 = require('./serialization/sb2'); const sb3 = require('./serialization/sb3'); const StringUtil = require('./util/string-util'); const formatMessage = require('format-message'); +const validate = require('scratch-parser'); + const Variable = require('./engine/variable'); const {loadCostume} = require('./import/load-costume.js'); @@ -226,26 +228,36 @@ class VirtualMachine extends EventEmitter { // Validate & parse if (typeof json !== 'string' && typeof json !== 'object') { - log.error('Failed to parse project. Invalid type supplied to fromJSON.'); - return; + throw new Error('Failed to parse project. Invalid type supplied to fromJSON.'); } - // Attempt to parse JSON if string is supplied - if (typeof json === 'string') json = JSON.parse(json); - // Establish version, deserialize, and load into runtime // @todo Support Scratch 1.4 // @todo This is an extremely naïve / dangerous way of determining version. // See `scratch-parser` for a more sophisticated validation // methodology that should be adapted for use here let deserializer; - if ((typeof json.meta !== 'undefined') && (typeof json.meta.semver !== 'undefined')) { + let validatedProject; + const possibleSb3 = typeof json === 'string' ? JSON.parse(json) : json; + if ((typeof possibleSb3.meta !== 'undefined') && (typeof possibleSb3.meta.semver !== 'undefined')) { deserializer = sb3; + validatedProject = possibleSb3; } else { - deserializer = sb2; + // scratch-parser expects a json string or a buffer + const possibleSb2 = typeof json === 'object' ? JSON.stringify(json) : json; + validate(possibleSb2, (err, project) => { + if (err) { + throw new Error( + `The given project could not be validated, parsing failed with error: ${JSON.stringify(err)}`); + + } else { + deserializer = sb2; + validatedProject = project; + } + }); } - return deserializer.deserialize(json, this.runtime) + return deserializer.deserialize(validatedProject, this.runtime) .then(({targets, extensions}) => this.installTargets(targets, extensions, true)); } From 7a09f8f74ffdc12c2fbbdf2c791ce5179720ff4c Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Tue, 6 Mar 2018 16:33:26 -0500 Subject: [PATCH 1610/1971] Merge pull request #934 from picklesrus/extensions-dynamic-menu Extensions dynamic menu --- packages/scratch-vm/src/engine/runtime.js | 29 +++++++++---------- .../extension-support/extension-manager.js | 26 +++++++++++++++++ 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 4da25f7bd3..aa73c96900 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -569,22 +569,21 @@ class Runtime extends EventEmitter { */ _buildMenuForScratchBlocks (menuName, menuItems, categoryInfo) { const menuId = this._makeExtensionMenuId(menuName, categoryInfo.id); - - /** @TODO: support dynamic menus when 'menuItems' is a method name string (see extension spec) */ - if (typeof menuItems === 'string') { - throw new Error(`Dynamic extension menus are not yet supported. Menu name: ${menuName}`); + let options = null; + if (typeof menuItems === 'function') { + options = menuItems; + } else { + options = menuItems.map(item => { + switch (typeof item) { + case 'string': + return [item, item]; + case 'object': + return [item.text, item.value]; + default: + throw new Error(`Can't interpret menu item: ${item}`); + } + }); } - const options = menuItems.map(item => { - switch (typeof item) { - case 'string': - return [item, item]; - case 'object': - return [item.text, item.value]; - default: - throw new Error(`Can't interpret menu item: ${item}`); - } - }); - return { json: { message0: '%1', diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 3228f5c2a4..d49be91cf8 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -242,8 +242,34 @@ class ExtensionManager { } return result; }, []); + extensionInfo.menus = extensionInfo.menus || []; + extensionInfo.menus = this._prepareMenuInfo(serviceName, extensionInfo.menus); return extensionInfo; } + + /** + * Prepare extension menus. e.g. setup binding for dynamic menu functions. + * @param {string} serviceName - the name of the service hosting this extension block + * @param {Array.} menus - the menu defined by the extension. + * @returns {Array.} - a menuInfo object with all preprocessing done. + * @private + */ + _prepareMenuInfo (serviceName, menus) { + const menuNames = Object.getOwnPropertyNames(menus); + for (let i = 0; i < menuNames.length; i++) { + const item = menuNames[i]; + // If the value is a string, it should be the name of a function in the + // extension object to call to populate the menu whenever it is opened. + // Set up the binding for the function object here so + // we can use it later when converting the menu for Scratch Blocks. + if (typeof menus[item] === 'string') { + const serviceObject = dispatch.services[serviceName]; + // TODO: Fix this to use dispatch.call when extensions are running in workers. + menus[item] = serviceObject[menus[item]].bind(serviceObject); + } + } + return menus; + } /** * Apply defaults for optional block fields. From d4daaf1a38bbfedcddf598a840634838ccc93881 Mon Sep 17 00:00:00 2001 From: kchadha Date: Fri, 9 Mar 2018 09:34:44 -0500 Subject: [PATCH 1611/1971] Merge pull request #964 from kchadha/save-load Preliminary Save and Load Work --- packages/scratch-vm/src/virtual-machine.js | 49 +++++++++++++++++++++- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index d40a2a28be..da98496168 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -1,5 +1,6 @@ const TextEncoder = require('text-encoding').TextEncoder; const EventEmitter = require('events'); +const JSZip = require('jszip'); const centralDispatch = require('./dispatch/central-dispatch'); const ExtensionManager = require('./extension-support/extension-manager'); @@ -15,6 +16,7 @@ const Variable = require('./engine/variable'); const {loadCostume} = require('./import/load-costume.js'); const {loadSound} = require('./import/load-sound.js'); +const {serializeSounds, serializeCostumes} = require('./serialization/serialize-assets'); const RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_']; @@ -184,6 +186,28 @@ class VirtualMachine extends EventEmitter { return this.fromJSON(json); } + /** + * Load a project from a Scratch 3.0 sb3 file containing a project json + * and all of the sound and costume files. + * @param {Buffer} inputBuffer A buffer representing the project to load. + * @return {!Promise} Promise that resolves after targets are installed. + */ + loadProjectLocal (inputBuffer) { + // TODO need to handle sb2 files as well, and will possibly merge w/ + // above function + return JSZip.loadAsync(inputBuffer) + .then(sb3File => { + sb3File.file('project.json').async('string') + .then(json => { + // TODO error handling for unpacking zip/not finding project.json + json = JSON.parse(json); // TODO catch errors here (validation) + return sb3.deserialize(json, this.runtime, sb3File) + .then(({targets, extensions}) => + this.installTargets(targets, extensions, true)); + }); + }); + } + /** * Load a project from the Scratch web site, by ID. * @param {string} id - the ID of the project to download, as a string. @@ -205,8 +229,25 @@ class VirtualMachine extends EventEmitter { * @returns {string} Project in a Scratch 3.0 JSON representation. */ saveProjectSb3 () { - // @todo: Handle other formats, e.g., Scratch 1.4, Scratch 2.0. - return this.toJSON(); + const soundDescs = serializeSounds(this.runtime); + const costumeDescs = serializeCostumes(this.runtime); + const projectJson = this.toJSON(); + + const zip = new JSZip(); + + // Put everything in a zip file + // TODO compression? + zip.file('project.json', projectJson); + for (let i = 0; i < soundDescs.length; i++) { + const currSound = soundDescs[i]; + zip.file(currSound.fileName, currSound.fileContent); + } + for (let i = 0; i < costumeDescs.length; i++) { + const currCostume = costumeDescs[i]; + zip.file(currCostume.fileName, currCostume.fileContent); + } + + return zip.generateAsync({type: 'blob'}); } /** @@ -505,6 +546,10 @@ class VirtualMachine extends EventEmitter { storage.DataFormat.SVG, (new TextEncoder()).encode(svg) ); + // If we're in here, we've edited an svg in the vector editor, + // so the dataFormat should be 'svg' + costume.dataFormat = storage.DataFormat.SVG; + costume.md5 = `${costume.assetId}.${costume.dataFormat}`; this.emitTargetsUpdate(); } From fba5a69819ff16bf2410c1f398b604826d8918b3 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 19 Mar 2018 16:45:25 -0400 Subject: [PATCH 1612/1971] Merge pull request #975 from ericrosenbaum/bugfix/move-volume-to-rendered-target Move volume to rendered target --- packages/scratch-vm/src/sprites/rendered-target.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 8da2d2b5aa..1949641fef 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -122,6 +122,12 @@ class RenderedTarget extends Target { * @type {number} */ this.tempo = 60; + + /** + * Loudness for sound playback for this target, as a percentage. + * @type {number} + */ + this.volume = 100; } /** From 06479c92970a091a31daecc9847dbc64e057e98f Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 22 Mar 2018 16:49:04 -0400 Subject: [PATCH 1613/1971] Merge pull request #991 from ericrosenbaum/bugfix/clones-share-activesoundplayers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clones share their parent’s activeSoundPlayers object --- packages/scratch-vm/src/sprites/rendered-target.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 1949641fef..d7b1e0dde0 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -152,6 +152,13 @@ class RenderedTarget extends Target { this.audioPlayer = null; if (this.runtime && this.runtime.audioEngine) { this.audioPlayer = this.runtime.audioEngine.createPlayer(); + // If this is a clone, it gets a reference to its parent's activeSoundPlayers object. + if (!this.isOriginal) { + const parent = this.sprite.clones[0]; + if (parent && parent.audioPlayer) { + this.audioPlayer.activeSoundPlayers = parent.audioPlayer.activeSoundPlayers; + } + } } } From 38f4e07277eb8cd655f227f1ce8b2523b3cf5415 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Thu, 22 Mar 2018 15:16:21 -0700 Subject: [PATCH 1614/1971] Merge pull request #994 from cwillisf/looks-extension-support Looks extension support --- .../extension-support/extension-manager.js | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index d49be91cf8..bf988adb94 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -246,7 +246,7 @@ class ExtensionManager { extensionInfo.menus = this._prepareMenuInfo(serviceName, extensionInfo.menus); return extensionInfo; } - + /** * Prepare extension menus. e.g. setup binding for dynamic menu functions. * @param {string} serviceName - the name of the service hosting this extension block @@ -264,13 +264,36 @@ class ExtensionManager { // we can use it later when converting the menu for Scratch Blocks. if (typeof menus[item] === 'string') { const serviceObject = dispatch.services[serviceName]; - // TODO: Fix this to use dispatch.call when extensions are running in workers. - menus[item] = serviceObject[menus[item]].bind(serviceObject); + const menuName = menus[item]; + menus[item] = this._getExtensionMenuItems.bind(this, serviceObject, menuName); } } return menus; } + /** + * Fetch the items for a particular extension menu, providing the target ID for context. + * @param {object} extensionObject - the extension object providing the menu. + * @param {string} menuName - the name of the menu function to call. + * @returns {Array} menu items ready for scratch-blocks. + * @private + */ + _getExtensionMenuItems (extensionObject, menuName) { + // Fetch the items appropriate for the target currently being edited. This assumes that menus only + // collect items when opened by the user while editing a particular target. + const editingTarget = this.runtime.getEditingTarget(); + const editingTargetID = editingTarget ? editingTarget.id : null; + + // TODO: Fix this to use dispatch.call when extensions are running in workers. + const menuFunc = extensionObject[menuName]; + const menuItems = menuFunc.call(extensionObject, editingTargetID); + + if (!menuItems || menuItems.length < 1) { + throw new Error(`Extension menu returned no items: ${menuName}`); + } + return menuItems; + } + /** * Apply defaults for optional block fields. * @param {string} serviceName - the name of the service hosting this extension block @@ -297,7 +320,12 @@ class ExtensionManager { blockInfo.func = dispatch.call.bind(dispatch, serviceName, blockInfo.func); } else { const serviceObject = dispatch.services[serviceName]; - blockInfo.func = serviceObject[blockInfo.func].bind(serviceObject); + const func = serviceObject[blockInfo.func]; + if (func) { + blockInfo.func = func.bind(serviceObject); + } else { + throw new Error(`Could not find extension block function called ${blockInfo.func}`); + } } return blockInfo; } From b59ddec1ebec38fb03fb46bc3270e2fbbd94ead1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 23 Mar 2018 09:20:22 -0400 Subject: [PATCH 1615/1971] Merge pull request #992 from paulkaplan/stage-clicked Make "when clicked" blocks target dependent. --- packages/scratch-vm/src/virtual-machine.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index da98496168..e90a548ca2 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -778,6 +778,7 @@ class VirtualMachine extends EventEmitter { for (let i = 0; i < blocks.length; i++) { target.blocks.createBlock(blocks[i]); } + target.blocks.updateTargetSpecificBlocks(target.isStage); } /** From 610d91aeddee09b3b83c1bca242ed431177731d4 Mon Sep 17 00:00:00 2001 From: kchadha Date: Fri, 23 Mar 2018 10:11:49 -0400 Subject: [PATCH 1616/1971] Merge pull request #979 from kchadha/serialization-cleanup SB3 Serialization & Load Project --- packages/scratch-vm/src/virtual-machine.js | 116 +++++++++++---------- 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index e90a548ca2..17ac7ebed4 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -177,34 +177,41 @@ class VirtualMachine extends EventEmitter { } /** - * Load a project from a Scratch 2.0 JSON representation. - * @param {?string} json JSON string representing the project. + * Load a Scratch project from a .sb, .sb2, .sb3 or json string. + * @param {string | object} input A json string, object, or ArrayBuffer representing the project to load. * @return {!Promise} Promise that resolves after targets are installed. */ - loadProject (json) { - // @todo: Handle other formats, e.g., Scratch 1.4, Scratch 3.0. - return this.fromJSON(json); - } + loadProject (input) { + if (typeof input === 'object' && !(input instanceof ArrayBuffer) && + !ArrayBuffer.isView(input)) { + // If the input is an object and not any ArrayBuffer + // or an ArrayBuffer view (this includes all typed arrays and DataViews) + // turn the object into a JSON string, because we suspect + // this is a project.json as an object + // validate expects a string or buffer as input + // TODO not sure if we need to check that it also isn't a data view + input = JSON.stringify(input); + } - /** - * Load a project from a Scratch 3.0 sb3 file containing a project json - * and all of the sound and costume files. - * @param {Buffer} inputBuffer A buffer representing the project to load. - * @return {!Promise} Promise that resolves after targets are installed. - */ - loadProjectLocal (inputBuffer) { - // TODO need to handle sb2 files as well, and will possibly merge w/ - // above function - return JSZip.loadAsync(inputBuffer) - .then(sb3File => { - sb3File.file('project.json').async('string') - .then(json => { - // TODO error handling for unpacking zip/not finding project.json - json = JSON.parse(json); // TODO catch errors here (validation) - return sb3.deserialize(json, this.runtime, sb3File) - .then(({targets, extensions}) => - this.installTargets(targets, extensions, true)); - }); + const validationPromise = new Promise((resolve, reject) => { + validate(input, (error, res) => { + if (error) { + reject(error); + } + if (res) { + resolve(res); + } + }); + }); + + return validationPromise + .then(validatedInput => this.deserializeProject(validatedInput[0], validatedInput[1])) + .catch(error => { + // Intentionally rejecting here (want errors to be handled by caller) + if (error.hasOwnProperty('validationError')) { + return Promise.reject(JSON.stringify(error)); + } + return Promise.reject(error); }); } @@ -233,6 +240,8 @@ class VirtualMachine extends EventEmitter { const costumeDescs = serializeCostumes(this.runtime); const projectJson = this.toJSON(); + // TODO want to eventually move zip creation out of here, and perhaps + // into scratch-storage const zip = new JSZip(); // Put everything in a zip file @@ -258,47 +267,40 @@ class VirtualMachine extends EventEmitter { return JSON.stringify(sb3.serialize(this.runtime)); } + // TODO do we still need this function? Keeping it here so as not to introduce + // a breaking change. /** * Load a project from a Scratch JSON representation. * @param {string} json JSON string representing a project. * @returns {Promise} Promise that resolves after the project has loaded */ fromJSON (json) { + log.warning('fromJSON is now just a wrapper around loadProject, please use that function instead.'); + return this.loadProject(json); + } + + /** + * Load a project from a Scratch JSON representation. + * @param {string} projectJSON JSON string representing a project. + * @param {?JSZip} zip Optional zipped project containing assets to be loaded. + * @returns {Promise} Promise that resolves after the project has loaded + */ + deserializeProject (projectJSON, zip) { // Clear the current runtime this.clear(); - // Validate & parse - if (typeof json !== 'string' && typeof json !== 'object') { - throw new Error('Failed to parse project. Invalid type supplied to fromJSON.'); - } - - // Establish version, deserialize, and load into runtime - // @todo Support Scratch 1.4 - // @todo This is an extremely naïve / dangerous way of determining version. - // See `scratch-parser` for a more sophisticated validation - // methodology that should be adapted for use here - let deserializer; - let validatedProject; - const possibleSb3 = typeof json === 'string' ? JSON.parse(json) : json; - if ((typeof possibleSb3.meta !== 'undefined') && (typeof possibleSb3.meta.semver !== 'undefined')) { - deserializer = sb3; - validatedProject = possibleSb3; - } else { - // scratch-parser expects a json string or a buffer - const possibleSb2 = typeof json === 'object' ? JSON.stringify(json) : json; - validate(possibleSb2, (err, project) => { - if (err) { - throw new Error( - `The given project could not be validated, parsing failed with error: ${JSON.stringify(err)}`); - - } else { - deserializer = sb2; - validatedProject = project; - } - }); - } - - return deserializer.deserialize(validatedProject, this.runtime) + const runtime = this.runtime; + const deserializePromise = function () { + const projectVersion = projectJSON.projectVersion; + if (projectVersion === 2) { + return sb2.deserialize(projectJSON, runtime); + } + if (projectVersion === 3) { + return sb3.deserialize(projectJSON, runtime, zip); + } + return Promise.reject('Unable to verify Scratch Project version.'); + }; + return deserializePromise() .then(({targets, extensions}) => this.installTargets(targets, extensions, true)); } From 68fc68634ee5ad2634f9ef8765a4cacab45d0ef7 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 23 Mar 2018 14:36:52 -0400 Subject: [PATCH 1617/1971] Merge pull request #996 from paulkaplan/fix-bubble-bounds Position the speech bubble more tightly. --- packages/scratch-vm/src/sprites/rendered-target.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index d7b1e0dde0..2cc5b31af8 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -645,6 +645,18 @@ class RenderedTarget extends Target { return null; } + /** + * Return the bounding box around a slice of the top 8px of the rendered target. + * Includes top, left, bottom, right attributes in Scratch coordinates. + * @return {?object} Tight bounding box, or null. + */ + getBoundsForBubble () { + if (this.renderer) { + return this.runtime.renderer.getBoundsForBubble(this.drawableID); + } + return null; + } + /** * Return whether touching a point. * @param {number} x X coordinate of test point. From 50acc4df9bb000c320c441195ecbc3cf42914949 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Tue, 3 Apr 2018 16:19:26 -0400 Subject: [PATCH 1618/1971] Merge pull request #1001 from mzgoddard/motion-detect Motion detect --- .../scratch-vm/src/extension-support/extension-manager.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index bf988adb94..689fcf1285 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -9,10 +9,13 @@ const BlockType = require('./block-type'); const Scratch3PenBlocks = require('../extensions/scratch3_pen'); const Scratch3WeDo2Blocks = require('../extensions/scratch3_wedo2'); const Scratch3MusicBlocks = require('../extensions/scratch3_music'); +const Scratch3VideoSensingBlocks = require('../extensions/scratch3_video_sensing'); + const builtinExtensions = { pen: Scratch3PenBlocks, wedo2: Scratch3WeDo2Blocks, - music: Scratch3MusicBlocks + music: Scratch3MusicBlocks, + videoSensing: Scratch3VideoSensingBlocks }; /** From 29d7faa90b0372a281fdb88faf997451bfb89a4b Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 4 Apr 2018 19:59:14 -0400 Subject: [PATCH 1619/1971] Merge pull request #1014 from ericrosenbaum/bugfix/video-state Import and store video state --- .../scratch-vm/src/sprites/rendered-target.js | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 2cc5b31af8..35204ec933 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -118,16 +118,31 @@ class RenderedTarget extends Target { this.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND; /** - * Current tempo (used by the music extension) + * Loudness for sound playback for this target, as a percentage. + * @type {number} + */ + this.volume = 100; + + /** + * Current tempo (used by the music extension). + * This property is global to the project and stored in the stage. * @type {number} */ this.tempo = 60; /** - * Loudness for sound playback for this target, as a percentage. + * The transparency of the video (used by extensions with camera input). + * This property is global to the project and stored in the stage. * @type {number} */ - this.volume = 100; + this.videoTransparency = 50; + + /** + * The state of the video input (used by extensions with camera input). + * This property is global to the project and stored in the stage. + * @type {string} + */ + this.videoState = RenderedTarget.VIDEO_STATE.OFF; } /** @@ -194,6 +209,18 @@ class RenderedTarget extends Target { return "don't rotate"; } + /** + * Available states for video input. + * @type {object} + */ + static get VIDEO_STATE () { + return { + 'OFF': 'off', + 'ON': 'on', + 'ON-FLIPPED': 'on-flipped' + }; + } + /** * Set the X and Y coordinates. * @param {!number} x New X coordinate, in Scratch coordinates. From 04b8fc5bd0e542721ce5257f38d42772191014a4 Mon Sep 17 00:00:00 2001 From: kchadha Date: Fri, 6 Apr 2018 11:33:00 -0400 Subject: [PATCH 1620/1971] Merge pull request #1000 from kchadha/deserialize-assets-in-sb2 Deserialize sb2 assets while loading local .sb2. --- packages/scratch-vm/src/virtual-machine.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 17ac7ebed4..abe4b69ba7 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -293,7 +293,7 @@ class VirtualMachine extends EventEmitter { const deserializePromise = function () { const projectVersion = projectJSON.projectVersion; if (projectVersion === 2) { - return sb2.deserialize(projectJSON, runtime); + return sb2.deserialize(projectJSON, runtime, false, zip); } if (projectVersion === 3) { return sb3.deserialize(projectJSON, runtime, zip); From aaa54c3123fb3bfc90a089b06ca289c755af93f4 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Tue, 10 Apr 2018 12:17:43 -0700 Subject: [PATCH 1621/1971] Merge pull request #1021 from cwillisf/features-for-control-extension Features for control extension --- packages/scratch-vm/src/engine/runtime.js | 353 +++++++++++------- .../extension-support/extension-manager.js | 89 +++-- packages/scratch-vm/src/virtual-machine.js | 26 +- 3 files changed, 289 insertions(+), 179 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index aa73c96900..677ff36a2b 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -5,9 +5,13 @@ const escapeHtml = require('escape-html'); const ArgumentType = require('../extension-support/argument-type'); const Blocks = require('./blocks'); const BlockType = require('../extension-support/block-type'); +const Profiler = require('./profiler'); const Sequencer = require('./sequencer'); +const ScratchBlocksConstants = require('./scratch-blocks-constants'); +const TargetType = require('../extension-support/target-type'); const Thread = require('./thread'); -const Profiler = require('./profiler'); +const log = require('../util/log'); +const maybeFormatMessage = require('../util/maybe-format-message'); // Virtual I/O devices. const Clock = require('../io/clock'); @@ -56,29 +60,13 @@ const ArgumentTypeMap = (() => { })(); /** - * These constants are copied from scratch-blocks/core/constants.js - * @TODO find a way to require() these... maybe make a scratch-blocks/dist/constants.js or something like that? - * @readonly - * @enum {int} + * Predefined "Converted block info" for a separator between blocks in a block category + * @type {ConvertedBlockInfo} */ -const ScratchBlocksConstants = { - /** - * ENUM for output shape: hexagonal (booleans/predicates). - * @const - */ - OUTPUT_SHAPE_HEXAGONAL: 1, - - /** - * ENUM for output shape: rounded (numbers). - * @const - */ - OUTPUT_SHAPE_ROUND: 2, - - /** - * ENUM for output shape: squared (any/all values; strings). - * @const - */ - OUTPUT_SHAPE_SQUARE: 3 +const ConvertedSeparator = { + info: {}, + json: null, + xml: '' }; /** @@ -484,15 +472,28 @@ class Runtime extends EventEmitter { return `${extensionId}.menu.${escapeHtml(menuName)}`; } + /** + * Create a context ("args") object for use with `formatMessage` on messages which might be target-specific. + * @param {Target} [target] - the target to use as context. If a target is not provided, default to the current + * editing target or the stage. + */ + makeMessageContextForTarget (target) { + const context = {}; + target = target || this.getEditingTarget() || this.getTargetForStage(); + if (target) { + context.targetType = (target.isStage ? TargetType.STAGE : TargetType.SPRITE); + } + } + /** * Register the primitives provided by an extension. - * @param {ExtensionInfo} extensionInfo - information about the extension (id, blocks, etc.) + * @param {ExtensionMetadata} extensionInfo - information about the extension (id, blocks, etc.) * @private */ _registerExtensionPrimitives (extensionInfo) { const categoryInfo = { id: extensionInfo.id, - name: extensionInfo.name, + name: maybeFormatMessage(extensionInfo.name), blockIconURI: extensionInfo.blockIconURI, menuIconURI: extensionInfo.menuIconURI, color1: '#FF6680', @@ -504,30 +505,14 @@ class Runtime extends EventEmitter { this._blockInfo.push(categoryInfo); - for (const menuName in extensionInfo.menus) { - if (extensionInfo.menus.hasOwnProperty(menuName)) { - const menuItems = extensionInfo.menus[menuName]; - const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuItems, categoryInfo); - categoryInfo.menus.push(convertedMenu); - } - } - for (const blockInfo of extensionInfo.blocks) { - const convertedBlock = this._convertForScratchBlocks(blockInfo, categoryInfo); - const opcode = convertedBlock.json.type; - categoryInfo.blocks.push(convertedBlock); - this._primitives[opcode] = convertedBlock.info.func; - if (blockInfo.blockType === BlockType.HAT) { - this._hats[opcode] = {edgeActivated: true}; /** @TODO let extension specify this */ - } - } + this._fillExtensionCategory(categoryInfo, extensionInfo); this.emit(Runtime.EXTENSION_ADDED, categoryInfo.blocks.concat(categoryInfo.menus)); } /** * Reregister the primitives for an extension - * @param {ExtensionInfo} extensionInfo - new info (results of running getInfo) - * for an extension + * @param {ExtensionMetadata} extensionInfo - new info (results of running getInfo) for an extension * @private */ _refreshExtensionPrimitives (extensionInfo) { @@ -536,22 +521,7 @@ class Runtime extends EventEmitter { if (extensionInfo.id === categoryInfo.id) { categoryInfo.blocks = []; categoryInfo.menus = []; - for (const menuName in extensionInfo.menus) { - if (extensionInfo.menus.hasOwnProperty(menuName)) { - const menuItems = extensionInfo.menus[menuName]; - const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuItems, categoryInfo); - categoryInfo.menus.push(convertedMenu); - } - } - for (const blockInfo of extensionInfo.blocks) { - const convertedBlock = this._convertForScratchBlocks(blockInfo, categoryInfo); - const opcode = convertedBlock.json.type; - categoryInfo.blocks.push(convertedBlock); - this._primitives[opcode] = convertedBlock.info.func; - if (blockInfo.blockType === BlockType.HAT) { - this._hats[opcode] = {edgeActivated: true}; /** @TODO let extension specify this */ - } - } + this._fillExtensionCategory(categoryInfo, extensionInfo); extensionBlocks = extensionBlocks.concat(categoryInfo.blocks, categoryInfo.menus); } } @@ -559,6 +529,44 @@ class Runtime extends EventEmitter { this.emit(Runtime.BLOCKSINFO_UPDATE, extensionBlocks); } + /** + * Read extension information, convert menus and blocks, and store the results in the provided category object. + * @param {CategoryInfo} categoryInfo - the category to be filled + * @param {ExtensionMetadata} extensionInfo - the extension metadata to read + * @private + */ + _fillExtensionCategory (categoryInfo, extensionInfo) { + for (const menuName in extensionInfo.menus) { + if (extensionInfo.menus.hasOwnProperty(menuName)) { + const menuItems = extensionInfo.menus[menuName]; + const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuItems, categoryInfo); + categoryInfo.menus.push(convertedMenu); + } + } + for (const blockInfo of extensionInfo.blocks) { + if (blockInfo === '---') { + categoryInfo.blocks.push(ConvertedSeparator); + continue; + } + try { + const convertedBlock = this._convertForScratchBlocks(blockInfo, categoryInfo); + const opcode = convertedBlock.json.type; + categoryInfo.blocks.push(convertedBlock); + if (blockInfo.blockType !== BlockType.EVENT) { + this._primitives[opcode] = convertedBlock.info.func; + } + if (blockInfo.blockType === BlockType.EVENT || blockInfo.blockType === BlockType.HAT) { + this._hats[opcode] = { + edgeActivated: blockInfo.isEdgeActivated, + restartExistingThreads: blockInfo.shouldRestartExistingThreads + }; + } + } catch (e) { + log.error('Error parsing block: ', {block: blockInfo, error: e}); + } + } + } + /** * Build the scratch-blocks JSON for a menu. Note that scratch-blocks treats menus as a special kind of block. * @param {string} menuName - the name of the menu @@ -573,14 +581,16 @@ class Runtime extends EventEmitter { if (typeof menuItems === 'function') { options = menuItems; } else { + const extensionMessageContext = this.makeMessageContextForTarget(); options = menuItems.map(item => { - switch (typeof item) { + const formattedItem = maybeFormatMessage(item, extensionMessageContext); + switch (typeof formattedItem) { case 'string': - return [item, item]; + return [formattedItem, formattedItem]; case 'object': - return [item.text, item.value]; + return [maybeFormatMessage(item.text, extensionMessageContext), item.value]; default: - throw new Error(`Can't interpret menu item: ${item}`); + throw new Error(`Can't interpret menu item: ${JSON.stringify(item)}`); } }); } @@ -606,14 +616,15 @@ class Runtime extends EventEmitter { } /** - * Convert BlockInfo into scratch-blocks JSON & XML, and generate a proxy function. - * @param {BlockInfo} blockInfo - the block to convert + * Convert ExtensionBlockMetadata into scratch-blocks JSON & XML, and generate a proxy function. + * @param {ExtensionBlockMetadata} blockInfo - the block to convert * @param {CategoryInfo} categoryInfo - the category for this block - * @returns {{info: BlockInfo, json: object, xml: string}} - the converted & original block information + * @returns {ConvertedBlockInfo} - the converted & original block information * @private */ _convertForScratchBlocks (blockInfo, categoryInfo) { const extendedOpcode = `${categoryInfo.id}.${blockInfo.opcode}`; + const blockJSON = { type: extendedOpcode, inputsInline: true, @@ -621,19 +632,19 @@ class Runtime extends EventEmitter { colour: categoryInfo.color1, colourSecondary: categoryInfo.color2, colourTertiary: categoryInfo.color3, - args0: [], extensions: ['scratch_extension'] }; - - const inputList = []; - - // TODO: store this somewhere so that we can map args appropriately after translation. - // This maps an arg name to its relative position in the original (usually English) block text. - // When displaying a block in another language we'll need to run a `replace` action similar to the one below, - // but each `[ARG]` will need to be replaced with the number in this map instead of `args0.length`. - const argsMap = {}; - - blockJSON.message0 = ''; + const context = { + // TODO: store this somewhere so that we can map args appropriately after translation. + // This maps an arg name to its relative position in the original (usually English) block text. + // When displaying a block in another language we'll need to run a `replace` action similar to the one + // below, but each `[ARG]` will need to be replaced with the number in this map. + argsMap: {}, + blockJSON, + categoryInfo, + blockInfo, + inputList: [] + }; // If an icon for the extension exists, prepend it to each block, with a vertical separator. if (categoryInfo.blockIconURI) { @@ -647,60 +658,12 @@ class Runtime extends EventEmitter { const separatorJSON = { type: 'field_vertical_separator' }; - blockJSON.args0.push(iconJSON); - blockJSON.args0.push(separatorJSON); + blockJSON.args0 = [ + iconJSON, + separatorJSON + ]; } - blockJSON.message0 += blockInfo.text.replace(/\[(.+?)]/g, (match, placeholder) => { - // Sanitize the placeholder to ensure valid XML - placeholder = placeholder.replace(/[<"&]/, '_'); - - const argJSON = { - type: 'input_value', - name: placeholder - }; - - const argInfo = blockInfo.arguments[placeholder] || {}; - const argTypeInfo = ArgumentTypeMap[argInfo.type] || {}; - const defaultValue = (typeof argInfo.defaultValue === 'undefined' ? - '' : - escapeHtml(argInfo.defaultValue.toString())); - - if (argTypeInfo.check) { - argJSON.check = argTypeInfo.check; - } - - const shadowType = (argInfo.menu ? - this._makeExtensionMenuId(argInfo.menu, categoryInfo.id) : - argTypeInfo.shadowType); - const fieldType = argInfo.menu || argTypeInfo.fieldType; - - // is the ScratchBlocks name for a block input. - inputList.push(``); - - // The is a placeholder for a reporter and is visible when there's no reporter in this input. - // Boolean inputs don't need to specify a shadow in the XML. - if (shadowType) { - inputList.push(``); - - // is a text field that the user can type into. Some shadows, like the color picker, don't allow - // text input and therefore don't need a field element. - if (fieldType) { - inputList.push(`${defaultValue}`); - } - - inputList.push(''); - } - - inputList.push(''); - - // scratch-blocks uses 1-based argument indexing - blockJSON.args0.push(argJSON); - const argNum = blockJSON.args0.length; - argsMap[placeholder] = argNum; - return `%${argNum}`; - }); - switch (blockInfo.blockType) { case BlockType.COMMAND: blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE; @@ -718,37 +681,141 @@ class Runtime extends EventEmitter { blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_HEXAGONAL; break; case BlockType.HAT: + case BlockType.EVENT: + if (!blockInfo.hasOwnProperty('isEdgeActivated')) { + // if absent, this property defaults to true + blockInfo.isEdgeActivated = true; + } blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE; blockJSON.nextStatement = null; // null = available connection; undefined = terminal break; case BlockType.CONDITIONAL: - // Statement inputs get names like 'SUBSTACK', 'SUBSTACK2', 'SUBSTACK3', ... - for (let branchNum = 1; branchNum <= blockInfo.branchCount; ++branchNum) { - blockJSON[`message${branchNum}`] = '%1'; - blockJSON[`args${branchNum}`] = [{ - type: 'input_statement', - name: `SUBSTACK${branchNum > 1 ? branchNum : ''}` - }]; - } + case BlockType.LOOP: + blockInfo.branchCount = blockInfo.branchCount || 1; blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE; blockJSON.previousStatement = null; // null = available connection; undefined = hat - blockJSON.nextStatement = null; // null = available connection; undefined = terminal + if (!blockInfo.isTerminal) { + blockJSON.nextStatement = null; // null = available connection; undefined = terminal + } break; } - if (blockInfo.isTerminal) { - delete blockJSON.nextStatement; + const blockText = Array.isArray(blockInfo.text) ? blockInfo.text : [blockInfo.text]; + let inTextNum = 0; // text for the next block "arm" is blockText[inTextNum] + let inBranchNum = 0; // how many branches have we placed into the JSON so far? + let outLineNum = 0; // used for scratch-blocks `message${outLineNum}` and `args${outLineNum}` + const convertPlaceholders = this._convertPlaceholders.bind(this, context); + const extensionMessageContext = this.makeMessageContextForTarget(); + + // alternate between a block "arm" with text on it and an open slot for a substack + while (inTextNum < blockText.length || inBranchNum < blockInfo.branchCount) { + if (inTextNum < blockText.length) { + context.outLineNum = outLineNum; + const lineText = maybeFormatMessage(blockText[inTextNum], extensionMessageContext); + const convertedText = lineText.replace(/\[(.+?)]/g, convertPlaceholders); + if (blockJSON[`message${outLineNum}`]) { + blockJSON[`message${outLineNum}`] += convertedText; + } else { + blockJSON[`message${outLineNum}`] = convertedText; + } + ++inTextNum; + ++outLineNum; + } + if (inBranchNum < blockInfo.branchCount) { + blockJSON[`message${outLineNum}`] = '%1'; + blockJSON[`args${outLineNum}`] = [{ + type: 'input_statement', + name: `SUBSTACK${inBranchNum > 0 ? inBranchNum + 1 : ''}` + }]; + ++inBranchNum; + ++outLineNum; + } + } + + // Add icon to the bottom right of a loop block + if (blockInfo.blockType === BlockType.LOOP) { + blockJSON[`lastDummyAlign${outLineNum}`] = 'RIGHT'; + blockJSON[`message${outLineNum}`] = '%1'; + blockJSON[`args${outLineNum}`] = [{ + type: 'field_image', + src: './static/blocks-media/repeat.svg', // TODO: use a constant or make this configurable? + width: 24, + height: 24, + alt: '*', + flip_rtl: true + }]; + ++outLineNum; } - const blockXML = `${inputList.join('')}`; + const blockXML = `${context.inputList.join('')}`; return { - info: blockInfo, - json: blockJSON, + info: context.blockInfo, + json: context.blockJSON, xml: blockXML }; } + /** + * Helper for _convertForScratchBlocks which handles linearization of argument placeholders. Called as a callback + * from string#replace. In addition to the return value the JSON and XML items in the context will be filled. + * @param {object} context - information shared with _convertForScratchBlocks about the block, etc. + * @param {string} match - the overall string matched by the placeholder regex, including brackets: '[FOO]'. + * @param {string} placeholder - the name of the placeholder being matched: 'FOO'. + * @return {string} scratch-blocks placeholder for the argument: '%1'. + * @private + */ + _convertPlaceholders (context, match, placeholder) { + // Sanitize the placeholder to ensure valid XML + placeholder = placeholder.replace(/[<"&]/, '_'); + + const argJSON = { + type: 'input_value', + name: placeholder + }; + + const argInfo = context.blockInfo.arguments[placeholder] || {}; + const argTypeInfo = ArgumentTypeMap[argInfo.type] || {}; + const defaultValue = (typeof argInfo.defaultValue === 'undefined' ? + '' : + escapeHtml(maybeFormatMessage(argInfo.defaultValue, this.makeMessageContextForTarget()).toString())); + + if (argTypeInfo.check) { + argJSON.check = argTypeInfo.check; + } + + const shadowType = (argInfo.menu ? + this._makeExtensionMenuId(argInfo.menu, context.categoryInfo.id) : + argTypeInfo.shadowType); + const fieldType = argInfo.menu || argTypeInfo.fieldType; + + // is the ScratchBlocks name for a block input. + context.inputList.push(``); + + // The is a placeholder for a reporter and is visible when there's no reporter in this input. + // Boolean inputs don't need to specify a shadow in the XML. + if (shadowType) { + context.inputList.push(``); + + // is a text field that the user can type into. Some shadows, like the color picker, don't allow + // text input and therefore don't need a field element. + if (fieldType) { + context.inputList.push(`${defaultValue}`); + } + + context.inputList.push(''); + } + + context.inputList.push(''); + + const argsName = `args${context.outLineNum}`; + const blockArgs = (context.blockJSON[argsName] = context.blockJSON[argsName] || []); + blockArgs.push(argJSON); + const argNum = blockArgs.length; + context.argsMap[placeholder] = argNum; + return `%${argNum}`; + } + /** * @returns {string} scratch-blocks XML description for all dynamic blocks, wrapped in elements. */ diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 689fcf1285..21288fbf18 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -1,5 +1,6 @@ const dispatch = require('../dispatch/central-dispatch'); const log = require('../util/log'); +const maybeFormatMessage = require('../util/maybe-format-message'); const BlockType = require('./block-type'); @@ -25,26 +26,22 @@ const builtinExtensions = { */ /** - * @typedef {object} BlockInfo - Information about an extension block - * @property {string} opcode - the block opcode - * @property {string|object} text - the human-readable text on this block - * @property {BlockType|undefined} blockType - the type of block (default: BlockType.COMMAND) - * @property {int|undefined} branchCount - the number of branches this block controls, if conditional (default: 0) - * @property {Boolean|undefined} isTerminal - true if this block ends a stack (default: false) - * @property {Boolean|undefined} blockAllThreads - true if all threads must wait for this block to run (default: false) - * @property {object.|undefined} arguments - information about this block's arguments, if any - * @property {string|Function|undefined} func - the method for this block on the extension service (default: opcode) - * @property {Array.|undefined} filter - the list of targets for which this block should appear (default: all) - * @property {Boolean|undefined} hideFromPalette - true if should not be appear in the palette. (default false) + * @typedef {object} ConvertedBlockInfo - Raw extension block data paired with processed data ready for scratch-blocks + * @property {ExtensionBlockMetadata} info - the raw block info + * @property {object} json - the scratch-blocks JSON definition for this block + * @property {string} xml - the scratch-blocks XML definition for this block */ /** * @typedef {object} CategoryInfo - Information about a block category * @property {string} id - the unique ID of this category + * @property {string} name - the human-readable name of this category + * @property {string|undefined} blockIconURI - optional URI for the block icon image * @property {string} color1 - the primary color for this category, in '#rrggbb' format * @property {string} color2 - the secondary color for this category, in '#rrggbb' format * @property {string} color3 - the tertiary color for this category, in '#rrggbb' format - * @property {Array.} block - the blocks in this category + * @property {Array.} blocks - the blocks, separators, etc. in this category + * @property {Array.} menus - the menus provided by this category */ /** @@ -208,7 +205,7 @@ class ExtensionManager { _registerExtensionInfo (serviceName, extensionInfo) { extensionInfo = this._prepareExtensionInfo(serviceName, extensionInfo); dispatch.call('runtime', '_registerExtensionPrimitives', extensionInfo).catch(e => { - log.error(`Failed to register primitives for extension on service ${serviceName}: ${JSON.stringify(e)}`); + log.error(`Failed to register primitives for extension on service ${serviceName}:`, e); }); } @@ -236,14 +233,23 @@ class ExtensionManager { extensionInfo.name = extensionInfo.name || extensionInfo.id; extensionInfo.blocks = extensionInfo.blocks || []; extensionInfo.targetTypes = extensionInfo.targetTypes || []; - extensionInfo.blocks = extensionInfo.blocks.reduce((result, blockInfo) => { + extensionInfo.blocks = extensionInfo.blocks.reduce((results, blockInfo) => { try { - result.push(this._prepareBlockInfo(serviceName, blockInfo)); + let result; + switch (blockInfo) { + case '---': // separator + result = '---'; + break; + default: // an ExtensionBlockMetadata object + result = this._prepareBlockInfo(serviceName, blockInfo); + break; + } + results.push(result); } catch (e) { // TODO: more meaningful error reporting log.error(`Error processing block: ${e.message}, Block:\n${JSON.stringify(blockInfo)}`); } - return result; + return results; }, []); extensionInfo.menus = extensionInfo.menus || []; extensionInfo.menus = this._prepareMenuInfo(serviceName, extensionInfo.menus); @@ -284,12 +290,23 @@ class ExtensionManager { _getExtensionMenuItems (extensionObject, menuName) { // Fetch the items appropriate for the target currently being edited. This assumes that menus only // collect items when opened by the user while editing a particular target. - const editingTarget = this.runtime.getEditingTarget(); + const editingTarget = this.runtime.getEditingTarget() || this.runtime.getTargetForStage(); const editingTargetID = editingTarget ? editingTarget.id : null; + const extensionMessageContext = this.runtime.makeMessageContextForTarget(editingTarget); // TODO: Fix this to use dispatch.call when extensions are running in workers. const menuFunc = extensionObject[menuName]; - const menuItems = menuFunc.call(extensionObject, editingTargetID); + const menuItems = menuFunc.call(extensionObject, editingTargetID).map( + item => { + item = maybeFormatMessage(item, extensionMessageContext); + if (typeof item === 'object') { + return [ + maybeFormatMessage(item.text, extensionMessageContext), + item.value + ]; + } + return item; + }); if (!menuItems || menuItems.length < 1) { throw new Error(`Extension menu returned no items: ${menuName}`); @@ -300,8 +317,8 @@ class ExtensionManager { /** * Apply defaults for optional block fields. * @param {string} serviceName - the name of the service hosting this extension block - * @param {BlockInfo} blockInfo - the block info from the extension - * @returns {BlockInfo} - a new block info object which has values for all relevant optional fields. + * @param {ExtensionBlockMetadata} blockInfo - the block info from the extension + * @returns {ExtensionBlockMetadata} - a new block info object which has values for all relevant optional fields. * @private */ _prepareBlockInfo (serviceName, blockInfo) { @@ -312,24 +329,30 @@ class ExtensionManager { arguments: {} }, blockInfo); blockInfo.opcode = this._sanitizeID(blockInfo.opcode); - blockInfo.func = blockInfo.func ? this._sanitizeID(blockInfo.func) : blockInfo.opcode; blockInfo.text = blockInfo.text || blockInfo.opcode; - /** - * This is only here because the VM performs poorly when blocks return promises. - * @TODO make it possible for the VM to resolve a promise and continue during the same frame. - */ - if (dispatch._isRemoteService(serviceName)) { - blockInfo.func = dispatch.call.bind(dispatch, serviceName, blockInfo.func); - } else { - const serviceObject = dispatch.services[serviceName]; - const func = serviceObject[blockInfo.func]; - if (func) { - blockInfo.func = func.bind(serviceObject); + if (blockInfo.blockType !== BlockType.EVENT) { + blockInfo.func = blockInfo.func ? this._sanitizeID(blockInfo.func) : blockInfo.opcode; + + /** + * This is only here because the VM performs poorly when blocks return promises. + * @TODO make it possible for the VM to resolve a promise and continue during the same Scratch "tick" + */ + if (dispatch._isRemoteService(serviceName)) { + blockInfo.func = dispatch.call.bind(dispatch, serviceName, blockInfo.func); } else { - throw new Error(`Could not find extension block function called ${blockInfo.func}`); + const serviceObject = dispatch.services[serviceName]; + const func = serviceObject[blockInfo.func]; + if (func) { + blockInfo.func = func.bind(serviceObject); + } else if (blockInfo.blockType !== BlockType.EVENT) { + throw new Error(`Could not find extension block function called ${blockInfo.func}`); + } } + } else if (blockInfo.func) { + log.warn(`Ignoring function "${blockInfo.func}" for event block ${blockInfo.opcode}`); } + return blockInfo; } } diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index abe4b69ba7..aa7cb016dc 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -20,6 +20,18 @@ const {serializeSounds, serializeCostumes} = require('./serialization/serialize- const RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_']; +const CORE_EXTENSIONS = [ + // 'motion', + // 'looks', + // 'sound', + // 'events', + // 'control', + // 'sensing', + // 'operators', + // 'variables', + // 'myBlocks' +]; + /** * Handles connections between blocks, stage, and extensions. * @constructor @@ -314,6 +326,17 @@ class VirtualMachine extends EventEmitter { */ installTargets (targets, extensions, wholeProject) { const extensionPromises = []; + + if (wholeProject) { + this.clear(); + + CORE_EXTENSIONS.forEach(extensionID => { + if (!this.extensionManager.isExtensionLoaded(extensionID)) { + extensionPromises.push(this.extensionManager.loadExtensionURL(extensionID)); + } + }); + } + extensions.extensionIDs.forEach(extensionID => { if (!this.extensionManager.isExtensionLoaded(extensionID)) { const extensionURL = extensions.extensionURLs.get(extensionID) || extensionID; @@ -324,9 +347,6 @@ class VirtualMachine extends EventEmitter { targets = targets.filter(target => !!target); return Promise.all(extensionPromises).then(() => { - if (wholeProject) { - this.clear(); - } targets.forEach(target => { this.runtime.targets.push(target); (/** @type RenderedTarget */ target).updateAllDrawableProperties(); From 9c886f91f7fd12d2ce0f9c6ad1b279067ceeeaed Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 11 Apr 2018 09:17:52 -0400 Subject: [PATCH 1622/1971] Merge pull request #1040 from paulkaplan/filter-dragging-touching Filter out dragging targets from touching sprite check. --- packages/scratch-vm/src/sprites/rendered-target.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 35204ec933..b17f397341 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -734,7 +734,11 @@ class RenderedTarget extends Target { if (!firstClone || !this.renderer) { return false; } - const drawableCandidates = firstClone.sprite.clones.map(clone => clone.drawableID); + // Filter out dragging targets. This means a sprite that is being dragged + // can detect other sprites using touching , but cannot be detected + // by other sprites while it is being dragged. This matches Scratch 2.0 behavior. + const drawableCandidates = firstClone.sprite.clones.filter(clone => !clone.dragging) + .map(clone => clone.drawableID); return this.renderer.isTouchingDrawables( this.drawableID, drawableCandidates); } From 21afb8e0288b52f84495bf33a900d106fb14963a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 11 Apr 2018 09:30:02 -0400 Subject: [PATCH 1623/1971] Merge pull request #1038 from paulkaplan/skin-size Attach the costume size to the costume when loaded and modified --- packages/scratch-vm/src/sprites/rendered-target.js | 2 +- packages/scratch-vm/src/virtual-machine.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index b17f397341..aba3aef64f 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -355,7 +355,7 @@ class RenderedTarget extends Target { if (this.renderer) { // Clamp to scales relative to costume and stage size. // See original ScratchSprite.as:setSize. - const costumeSize = this.renderer.getSkinSize(this.drawableID); + const costumeSize = this.renderer.getCurrentSkinSize(this.drawableID); const origW = costumeSize[0]; const origH = costumeSize[1]; const minScale = Math.min(1, Math.max(5 / origW, 5 / origH)); diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index aa7cb016dc..98f85b808f 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -561,6 +561,7 @@ class VirtualMachine extends EventEmitter { costume.rotationCenterX = rotationCenterX; costume.rotationCenterY = rotationCenterY; this.runtime.renderer.updateSVGSkin(costume.skinId, svg, [rotationCenterX, rotationCenterY]); + costume.size = this.runtime.renderer.getSkinSize(costume.skinId); } const storage = this.runtime.storage; costume.assetId = storage.builtinHelper.cache( From b63d18ba31ac63d1f74464a79ed1262c880e363a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 11 Apr 2018 10:04:25 -0400 Subject: [PATCH 1624/1971] Merge pull request #1041 from paulkaplan/fix-clone-drag Only select the original target after a drag. --- packages/scratch-vm/src/virtual-machine.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 98f85b808f..935ead9ac1 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -921,7 +921,8 @@ class VirtualMachine extends EventEmitter { if (target) { this._dragTarget = null; target.stopDrag(); - this.setEditingTarget(target.id); + this.setEditingTarget(target.sprite && target.sprite.clones[0] ? + target.sprite.clones[0].id : target.id); } } From c734bdd30c5deca4239f01392129fd4bd007c0bd Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Wed, 11 Apr 2018 10:14:56 -0400 Subject: [PATCH 1625/1971] Merge pull request #1016 from gnarf/io-video Video IO Device Implementation --- packages/scratch-vm/src/engine/runtime.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 677ff36a2b..7c400e0e49 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -19,6 +19,7 @@ const DeviceManager = require('../io/deviceManager'); const Keyboard = require('../io/keyboard'); const Mouse = require('../io/mouse'); const MouseWheel = require('../io/mouseWheel'); +const Video = require('../io/video'); const defaultBlockPackages = { scratch3_control: require('../blocks/scratch3_control'), @@ -248,7 +249,8 @@ class Runtime extends EventEmitter { deviceManager: new DeviceManager(), keyboard: new Keyboard(this), mouse: new Mouse(this), - mouseWheel: new MouseWheel(this) + mouseWheel: new MouseWheel(this), + video: new Video(this) }; /** From 4550d0eb3fe67476d5d0c5bb535e61a0a4f4b99a Mon Sep 17 00:00:00 2001 From: kchadha Date: Wed, 11 Apr 2018 19:17:42 -0400 Subject: [PATCH 1626/1971] Merge pull request #1031 from kchadha/block_serialization_compression Block serialization compression --- packages/scratch-vm/src/sprites/rendered-target.js | 13 +++++++++---- packages/scratch-vm/src/virtual-machine.js | 9 +++++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index aba3aef64f..4a5f278be5 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -215,9 +215,9 @@ class RenderedTarget extends Target { */ static get VIDEO_STATE () { return { - 'OFF': 'off', - 'ON': 'on', - 'ON-FLIPPED': 'on-flipped' + OFF: 'off', + ON: 'on', + ON_FLIPPED: 'on-flipped' }; } @@ -1007,7 +1007,12 @@ class RenderedTarget extends Target { variables: this.variables, lists: this.lists, costumes: costumes, - sounds: this.getSounds() + sounds: this.getSounds(), + tempo: this.tempo, + volume: this.volume, + videoTransparency: this.videoTransparency, + videoState: this.videoState + }; } diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 935ead9ac1..696bbae35c 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -257,7 +257,6 @@ class VirtualMachine extends EventEmitter { const zip = new JSZip(); // Put everything in a zip file - // TODO compression? zip.file('project.json', projectJson); for (let i = 0; i < soundDescs.length; i++) { const currSound = soundDescs[i]; @@ -268,7 +267,13 @@ class VirtualMachine extends EventEmitter { zip.file(currCostume.fileName, currCostume.fileContent); } - return zip.generateAsync({type: 'blob'}); + return zip.generateAsync({ + type: 'blob', + compression: 'DEFLATE', + compressionOptions: { + level: 6 // Tradeoff between best speed (1) and best compression (9) + } + }); } /** From 227ebeae50e074dff8e8ac52774b27667f63c811 Mon Sep 17 00:00:00 2001 From: kchadha Date: Fri, 13 Apr 2018 09:12:25 -0400 Subject: [PATCH 1627/1971] Merge pull request #1042 from mzgoddard/target-video-state Configure video device with stage video settings --- packages/scratch-vm/src/sprites/rendered-target.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 4a5f278be5..19489d575f 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -140,9 +140,17 @@ class RenderedTarget extends Target { /** * The state of the video input (used by extensions with camera input). * This property is global to the project and stored in the stage. + * + * Defaults to ON. This setting does not turn the video by itself. A + * video extension once loaded will set the video device to this + * setting. Set to ON when a video extension is added in the editor the + * video will start ON. If the extension is loaded as part of loading a + * saved project the extension will see the value set when the stage + * was loaded from the saved values including the video state. + * * @type {string} */ - this.videoState = RenderedTarget.VIDEO_STATE.OFF; + this.videoState = RenderedTarget.VIDEO_STATE.ON; } /** @@ -211,7 +219,7 @@ class RenderedTarget extends Target { /** * Available states for video input. - * @type {object} + * @enum {string} */ static get VIDEO_STATE () { return { From 535570ee51dbf8c7217c238b575a9c729f8f4594 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 20 Apr 2018 11:43:15 -0400 Subject: [PATCH 1628/1971] Merge pull request #1070 from paulkaplan/fix-bubbles Fix drag and empty costume bugs with say bubbles --- .../scratch-vm/src/sprites/rendered-target.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 19489d575f..c87f3fd72b 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -193,6 +193,14 @@ class RenderedTarget extends Target { return 'TARGET_MOVED'; } + /** + * Event which fires when a target changes visually, for updating say bubbles. + * @type {string} + */ + static get EVENT_TARGET_VISUAL_CHANGE () { + return 'EVENT_TARGET_VISUAL_CHANGE'; + } + /** * Rotation style for "all around"/spinning. * @type {string} @@ -249,6 +257,7 @@ class RenderedTarget extends Target { position: position }); if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); } } else { @@ -299,6 +308,7 @@ class RenderedTarget extends Target { scale: renderedDirectionScale.scale }); if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); } } @@ -346,6 +356,7 @@ class RenderedTarget extends Target { visible: this.visible }); if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); } } @@ -378,6 +389,7 @@ class RenderedTarget extends Target { scale: renderedDirectionScale.scale }); if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); } } @@ -397,6 +409,7 @@ class RenderedTarget extends Target { props[effectName] = this.effects[effectName]; this.renderer.updateDrawableProperties(this.drawableID, props); if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); } } @@ -413,6 +426,7 @@ class RenderedTarget extends Target { if (this.renderer) { this.renderer.updateDrawableProperties(this.drawableID, this.effects); if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); } } @@ -446,6 +460,7 @@ class RenderedTarget extends Target { } this.renderer.updateDrawableProperties(this.drawableID, drawableProperties); if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); } } @@ -573,6 +588,7 @@ class RenderedTarget extends Target { scale: renderedDirectionScale.scale }); if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); } } @@ -645,6 +661,7 @@ class RenderedTarget extends Target { } this.renderer.updateDrawableProperties(this.drawableID, props); if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); } } @@ -1034,6 +1051,7 @@ class RenderedTarget extends Target { if (this.renderer && this.drawableID !== null) { this.renderer.destroyDrawable(this.drawableID); if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); } } From d693e8cd955ca4a7627598a2dfef4c580028c811 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Fri, 20 Apr 2018 11:46:25 -0400 Subject: [PATCH 1629/1971] Merge pull request #1069 from thisandagain/bugfix/underscore Replace extension delimiter with '_' --- packages/scratch-vm/src/engine/runtime.js | 4 ++-- .../scratch-vm/src/extension-support/extension-manager.js | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 7c400e0e49..7e58451b7b 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -471,7 +471,7 @@ class Runtime extends EventEmitter { * @private */ _makeExtensionMenuId (menuName, extensionId) { - return `${extensionId}.menu.${escapeHtml(menuName)}`; + return `${extensionId}_menu_${escapeHtml(menuName)}`; } /** @@ -625,7 +625,7 @@ class Runtime extends EventEmitter { * @private */ _convertForScratchBlocks (blockInfo, categoryInfo) { - const extendedOpcode = `${categoryInfo.id}.${blockInfo.opcode}`; + const extendedOpcode = `${categoryInfo.id}_${blockInfo.opcode}`; const blockJSON = { type: extendedOpcode, diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 21288fbf18..30e0f26cda 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -188,7 +188,7 @@ class ExtensionManager { _registerInternalExtension (extensionObject) { const extensionInfo = extensionObject.getInfo(); const fakeWorkerId = this.nextExtensionWorker++; - const serviceName = `extension.${fakeWorkerId}.${extensionInfo.id}`; + const serviceName = `extension_${fakeWorkerId}_${extensionInfo.id}`; return dispatch.setService(serviceName, extensionObject) .then(() => { dispatch.call('extensions', 'registerExtensionService', serviceName); @@ -229,7 +229,9 @@ class ExtensionManager { */ _prepareExtensionInfo (serviceName, extensionInfo) { extensionInfo = Object.assign({}, extensionInfo); - extensionInfo.id = this._sanitizeID(extensionInfo.id); + if (!/^[a-z0-9]+$/i.test(extensionInfo.id)) { + throw new Error('Invalid extension id'); + } extensionInfo.name = extensionInfo.name || extensionInfo.id; extensionInfo.blocks = extensionInfo.blocks || []; extensionInfo.targetTypes = extensionInfo.targetTypes || []; From 550dbf2b60ba363772052bdecfa2fe518a605875 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 20 Apr 2018 16:01:04 -0400 Subject: [PATCH 1630/1971] Merge pull request #1071 from kchadha/fix-mp3-edit-bug Fix issue with sound upload and save/load --- packages/scratch-vm/src/virtual-machine.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 696bbae35c..1e7a0b9e16 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -522,6 +522,7 @@ class VirtualMachine extends EventEmitter { storage.DataFormat.WAV, soundEncoding ); + sound.dataFormat = storage.DataFormat.WAV; sound.md5 = `${sound.assetId}.${sound.dataFormat}`; } // If soundEncoding is null, it's because gui had a problem From 2bd29ddccf419f25ba0dec8f77e1a61e85972937 Mon Sep 17 00:00:00 2001 From: kchadha Date: Mon, 23 Apr 2018 17:52:04 -0400 Subject: [PATCH 1631/1971] Merge pull request #1075 from kchadha/load-costume-no-metadata Load costume without metadata --- packages/scratch-vm/src/sprites/rendered-target.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index c87f3fd72b..c55042e67e 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -452,7 +452,7 @@ class RenderedTarget extends Target { typeof costume.rotationCenterX !== 'undefined' && typeof costume.rotationCenterY !== 'undefined' ) { - const scale = costume.bitmapResolution || 1; + const scale = costume.bitmapResolution || 2; drawableProperties.rotationCenter = [ costume.rotationCenterX / scale, costume.rotationCenterY / scale @@ -641,7 +641,7 @@ class RenderedTarget extends Target { if (this.renderer) { const renderedDirectionScale = this._getRenderedDirectionAndScale(); const costume = this.getCostumes()[this.currentCostume]; - const bitmapResolution = costume.bitmapResolution || 1; + const bitmapResolution = costume.bitmapResolution || 2; const props = { position: [this.x, this.y], direction: renderedDirectionScale.direction, From 6afedc38a9217f45473da2445f598eca5459f2be Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 26 Apr 2018 13:51:31 -0400 Subject: [PATCH 1632/1971] Merge pull request #1090 from fsih/renameGetCostume Rename getCostumeSvg to getCostume --- packages/scratch-vm/src/virtual-machine.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 1e7a0b9e16..8d04455986 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -545,7 +545,7 @@ class VirtualMachine extends EventEmitter { * @param {int} costumeIndex - the index of the costume to be got. * @return {string} the costume's SVG string, or null if it's not an SVG costume. */ - getCostumeSvg (costumeIndex) { + getCostume (costumeIndex) { const id = this.editingTarget.getCostumes()[costumeIndex].assetId; if (id && this.runtime && this.runtime.storage && this.runtime.storage.get(id).dataFormat === 'svg') { From 90fe9b4187f2083dd0a8eb69ff817a7938630b17 Mon Sep 17 00:00:00 2001 From: kchadha Date: Mon, 30 Apr 2018 09:27:10 -0400 Subject: [PATCH 1633/1971] Merge pull request #1092 from kchadha/refactor-video-io Refactor Video IO --- packages/scratch-vm/src/virtual-machine.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 8d04455986..e80a0bbda8 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -188,6 +188,10 @@ class VirtualMachine extends EventEmitter { } } + setVideoProvider (videoProvider) { + this.runtime.ioDevices.video.setProvider(videoProvider); + } + /** * Load a Scratch project from a .sb, .sb2, .sb3 or json string. * @param {string | object} input A json string, object, or ArrayBuffer representing the project to load. From 3fe8d0d918b99de56ab976226605903ce158618e Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 30 Apr 2018 09:36:32 -0400 Subject: [PATCH 1634/1971] Merge pull request #1065 from mzgoddard/push-reported-again Push reported again. Now with empty boolean support! --- packages/scratch-vm/src/engine/runtime.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 7e58451b7b..c3d68657da 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -947,6 +947,9 @@ class Runtime extends EventEmitter { thread.target = target; thread.stackClick = opts.stackClick; thread.updateMonitor = opts.updateMonitor; + thread.blockContainer = opts.updateMonitor ? + this.monitorBlocks : + target.blocks; thread.pushStack(id); this.threads.push(thread); @@ -976,6 +979,7 @@ class Runtime extends EventEmitter { newThread.target = thread.target; newThread.stackClick = thread.stackClick; newThread.updateMonitor = thread.updateMonitor; + newThread.blockContainer = thread.blockContainer; newThread.pushStack(thread.topBlock); const i = this.threads.indexOf(thread); if (i > -1) { From fc8e2d855b45058972148031444eb1716dd4ed3f Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Tue, 1 May 2018 08:38:08 -0400 Subject: [PATCH 1635/1971] Merge pull request #1086 from mortendybdal/feature/add-sprite-3 Included a function to add sprite 3 to vm --- packages/scratch-vm/src/virtual-machine.js | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index e80a0bbda8..a2d50c6537 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -398,6 +398,32 @@ class VirtualMachine extends EventEmitter { this.installTargets(targets, extensions, false)); } + /** + * Add a single sb3 sprite. + * @param {string} target JSON string representing the sprite/target. + * @returns {Promise} Promise that resolves after the sprite is added + */ + addSprite3 (target) { + // Validate & parse + if (typeof target !== 'string') { + log.error('Failed to parse sprite. Non-string supplied to addSprite3.'); + return; + } + target = JSON.parse(target); + if (typeof target !== 'object') { + log.error('Failed to parse sprite. JSON supplied to addSprite3 is not an object.'); + return; + } + + const jsonFormatted = { + targets: [target] + }; + + return sb3 + .deserialize(jsonFormatted, this.runtime, null) + .then(({targets, extensions}) => this.installTargets(targets, extensions, false)); + } + /** * Add a costume to the current editing target. * @param {string} md5ext - the MD5 and extension of the costume to be loaded. From 1248cc185f5148c6b6df1d0c44db6cb8bd356b2d Mon Sep 17 00:00:00 2001 From: Mx Corey Frang Date: Tue, 1 May 2018 08:56:22 -0400 Subject: [PATCH 1636/1971] Fix downloadProjectId method (#1093) * Fix downloadProjectId method * use .data not .data.buffer --- packages/scratch-vm/src/virtual-machine.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index a2d50c6537..8282a4d352 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -244,7 +244,7 @@ class VirtualMachine extends EventEmitter { const vm = this; const promise = storage.load(storage.AssetType.Project, id); promise.then(projectAsset => { - vm.loadProject(projectAsset.decodeText()); + vm.loadProject(projectAsset.data); }); } From 92b8b9e47baaa89648f56acfd00d968d5fd72987 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 1 May 2018 17:05:34 -0400 Subject: [PATCH 1637/1971] Merge pull request #1082 from fsih/updateBitmap Add updateBitmap function. getCostume can now serve bitmaps as well as svgs --- packages/scratch-vm/src/virtual-machine.js | 69 ++++++++++++++++++++-- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 8282a4d352..cf82327f51 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -2,6 +2,7 @@ const TextEncoder = require('text-encoding').TextEncoder; const EventEmitter = require('events'); const JSZip = require('jszip'); +const Buffer = require('buffer').Buffer; const centralDispatch = require('./dispatch/central-dispatch'); const ExtensionManager = require('./extension-support/extension-manager'); const log = require('./util/log'); @@ -17,6 +18,7 @@ const Variable = require('./engine/variable'); const {loadCostume} = require('./import/load-costume.js'); const {loadSound} = require('./import/load-sound.js'); const {serializeSounds, serializeCostumes} = require('./serialization/serialize-assets'); +require('canvas-toBlob'); const RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_']; @@ -571,19 +573,77 @@ class VirtualMachine extends EventEmitter { } /** - * Get an SVG string from storage. + * Get a string representation of the image from storage. * @param {int} costumeIndex - the index of the costume to be got. - * @return {string} the costume's SVG string, or null if it's not an SVG costume. + * @return {string} the costume's SVG string if it's SVG, + * a dataURI if it's a PNG or JPG, or null if it couldn't be found or decoded. */ getCostume (costumeIndex) { const id = this.editingTarget.getCostumes()[costumeIndex].assetId; - if (id && this.runtime && this.runtime.storage && - this.runtime.storage.get(id).dataFormat === 'svg') { + if (!id || !this.runtime || !this.runtime.storage) return null; + const format = this.runtime.storage.get(id).dataFormat; + if (format === this.runtime.storage.DataFormat.SVG) { return this.runtime.storage.get(id).decodeText(); + } else if (format === this.runtime.storage.DataFormat.PNG || + format === this.runtime.storage.DataFormat.JPG) { + return this.runtime.storage.get(id).encodeDataURI(); } + log.error(`Unhandled format: ${this.runtime.storage.get(id).dataFormat}`); return null; } + /** + * Update a costume with the given bitmap + * @param {!int} costumeIndex - the index of the costume to be updated. + * @param {!ImageData} bitmap - new bitmap for the renderer. + * @param {!number} rotationCenterX x of point about which the costume rotates, relative to its upper left corner + * @param {!number} rotationCenterY y of point about which the costume rotates, relative to its upper left corner + * @param {!number} bitmapResolution 1 for bitmaps that have 1 pixel per unit of stage, + * 2 for double-resolution bitmaps + */ + updateBitmap (costumeIndex, bitmap, rotationCenterX, rotationCenterY, bitmapResolution) { + const costume = this.editingTarget.getCostumes()[costumeIndex]; + if (!(costume && this.runtime && this.runtime.renderer)) return; + + costume.rotationCenterX = rotationCenterX; + costume.rotationCenterY = rotationCenterY; + + // @todo: updateBitmapSkin does not take ImageData + const canvas = document.createElement('canvas'); + canvas.width = bitmap.width; + canvas.height = bitmap.height; + const context = canvas.getContext('2d'); + context.putImageData(bitmap, 0, 0); + + // Divide by resolution because the renderer's definition of the rotation center + // is the rotation center divided by the bitmap resolution + this.runtime.renderer.updateBitmapSkin( + costume.skinId, + canvas, + bitmapResolution, + [rotationCenterX / bitmapResolution, rotationCenterY / bitmapResolution] + ); + + // @todo there should be a better way to get from ImageData to a decodable storage format + canvas.toBlob(blob => { + const reader = new FileReader(); + reader.addEventListener('loadend', () => { + const storage = this.runtime.storage; + costume.assetId = storage.builtinHelper.cache( + storage.AssetType.ImageBitmap, + storage.DataFormat.PNG, + Buffer.from(reader.result) + ); + costume.dataFormat = storage.DataFormat.PNG; + costume.bitmapResolution = bitmapResolution; + costume.size = [bitmap.width, bitmap.height]; + costume.md5 = `${costume.assetId}.${costume.dataFormat}`; + this.emitTargetsUpdate(); + }); + reader.readAsArrayBuffer(blob); + }); + } + /** * Update a costume with the given SVG * @param {int} costumeIndex - the index of the costume to be updated. @@ -609,6 +669,7 @@ class VirtualMachine extends EventEmitter { // so the dataFormat should be 'svg' costume.dataFormat = storage.DataFormat.SVG; costume.md5 = `${costume.assetId}.${costume.dataFormat}`; + delete costume.bitmapResolution; this.emitTargetsUpdate(); } From 0d14da484168c84e2ffaeff37049863c42891d46 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 4 May 2018 14:01:55 -0400 Subject: [PATCH 1638/1971] Merge pull request #1114 from kchadha/fix-broken-bitmap-res Fix broken rotation center on vector sprites --- packages/scratch-vm/src/virtual-machine.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index cf82327f51..475272e225 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -614,7 +614,7 @@ class VirtualMachine extends EventEmitter { canvas.height = bitmap.height; const context = canvas.getContext('2d'); context.putImageData(bitmap, 0, 0); - + // Divide by resolution because the renderer's definition of the rotation center // is the rotation center divided by the bitmap resolution this.runtime.renderer.updateBitmapSkin( @@ -669,7 +669,6 @@ class VirtualMachine extends EventEmitter { // so the dataFormat should be 'svg' costume.dataFormat = storage.DataFormat.SVG; costume.md5 = `${costume.assetId}.${costume.dataFormat}`; - delete costume.bitmapResolution; this.emitTargetsUpdate(); } From 7ef562600a112051669eb389cf3ff96b897a38fe Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 4 May 2018 14:40:55 -0400 Subject: [PATCH 1639/1971] Merge pull request #1117 from kchadha/update-svg-bitmapRes Updating vector should make bitmap resolution 1. --- packages/scratch-vm/src/virtual-machine.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 475272e225..6ce9de918e 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -669,6 +669,7 @@ class VirtualMachine extends EventEmitter { // so the dataFormat should be 'svg' costume.dataFormat = storage.DataFormat.SVG; costume.md5 = `${costume.assetId}.${costume.dataFormat}`; + costume.bitmapResolution = 1; this.emitTargetsUpdate(); } From 5dd67941528304536d3bb2dd6f8d91078d2fb61f Mon Sep 17 00:00:00 2001 From: kchadha Date: Fri, 4 May 2018 14:56:31 -0400 Subject: [PATCH 1640/1971] Merge pull request #1110 from kchadha/sprite-upload Support for Sprite Upload --- packages/scratch-vm/src/virtual-machine.js | 97 ++++++++++++++-------- 1 file changed, 62 insertions(+), 35 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 6ce9de918e..2758c35efe 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -212,13 +212,11 @@ class VirtualMachine extends EventEmitter { } const validationPromise = new Promise((resolve, reject) => { - validate(input, (error, res) => { - if (error) { - reject(error); - } - if (res) { - resolve(res); - } + // The second argument of false below indicates to the validator that the + // input should be parsed/validated as an entire project (and not a single sprite) + validate(input, false, (error, res) => { + if (error) return reject(error); + resolve(res); }); }); @@ -378,51 +376,80 @@ class VirtualMachine extends EventEmitter { }); } + /** + * Add a sprite, this could be .sprite2 or .sprite3. Unpack and validate + * such a file first. + * @param {string | object} input A json string, object, or ArrayBuffer representing the project to load. + * @return {!Promise} Promise that resolves after targets are installed. + */ + addSprite (input) { + const errorPrefix = 'Sprite Upload Error:'; + if (typeof input === 'object' && !(input instanceof ArrayBuffer) && + !ArrayBuffer.isView(input)) { + // If the input is an object and not any ArrayBuffer + // or an ArrayBuffer view (this includes all typed arrays and DataViews) + // turn the object into a JSON string, because we suspect + // this is a project.json as an object + // validate expects a string or buffer as input + // TODO not sure if we need to check that it also isn't a data view + input = JSON.stringify(input); + } + + const validationPromise = new Promise((resolve, reject) => { + // The second argument of true below indicates to the parser/validator + // that the given input should be treated as a single sprite and not + // an entire project + validate(input, true, (error, res) => { + if (error) return reject(error); + resolve(res); + }); + }); + + return validationPromise + .then(validatedInput => { + const projectVersion = validatedInput[0].projectVersion; + if (projectVersion === 2) { + return this._addSprite2(validatedInput[0], validatedInput[1]); + } + if (projectVersion === 3) { + return this._addSprite3(validatedInput[0], validatedInput[1]); + } + return Promise.reject(`${errorPrefix} Unable to verify sprite version.`); + }) + .catch(error => { + // Intentionally rejecting here (want errors to be handled by caller) + if (error.hasOwnProperty('validationError')) { + return Promise.reject(JSON.stringify(error)); + } + return Promise.reject(`${errorPrefix} ${error}`); + }); + } + /** * Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format. - * @param {string} json JSON string representing the sprite. + * @param {object} sprite Object representing 2.0 sprite to be added. + * @param {?ArrayBuffer} zip Optional zip of assets being referenced by json * @returns {Promise} Promise that resolves after the sprite is added */ - addSprite2 (json) { + _addSprite2 (sprite, zip) { // Validate & parse - if (typeof json !== 'string') { - log.error('Failed to parse sprite. Non-string supplied to addSprite2.'); - return; - } - json = JSON.parse(json); - if (typeof json !== 'object') { - log.error('Failed to parse sprite. JSON supplied to addSprite2 is not an object.'); - return; - } - return sb2.deserialize(json, this.runtime, true) + return sb2.deserialize(sprite, this.runtime, true, zip) .then(({targets, extensions}) => this.installTargets(targets, extensions, false)); } /** * Add a single sb3 sprite. - * @param {string} target JSON string representing the sprite/target. + * @param {object} sprite Object rperesenting 3.0 sprite to be added. + * @param {?ArrayBuffer} zip Optional zip of assets being referenced by target json * @returns {Promise} Promise that resolves after the sprite is added */ - addSprite3 (target) { + _addSprite3 (sprite, zip) { // Validate & parse - if (typeof target !== 'string') { - log.error('Failed to parse sprite. Non-string supplied to addSprite3.'); - return; - } - target = JSON.parse(target); - if (typeof target !== 'object') { - log.error('Failed to parse sprite. JSON supplied to addSprite3 is not an object.'); - return; - } - - const jsonFormatted = { - targets: [target] - }; return sb3 - .deserialize(jsonFormatted, this.runtime, null) + .deserialize(sprite, this.runtime, zip, true) .then(({targets, extensions}) => this.installTargets(targets, extensions, false)); } From d8314acb67d8b92db8957dcd175dd6a5097c43ea Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 9 May 2018 13:52:29 -0400 Subject: [PATCH 1641/1971] Merge pull request #1128 from paulkaplan/sb2-monitors Import monitors from sb2 files. --- packages/scratch-vm/src/engine/runtime.js | 2 ++ packages/scratch-vm/src/virtual-machine.js | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index c3d68657da..aef800133e 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1176,6 +1176,8 @@ class Runtime extends EventEmitter { dispose () { this.stopAll(); this.targets.map(this.disposeTarget, this); + this._monitorState = OrderedMap({}); + // @todo clear out extensions? turboMode? etc. } /** diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 2758c35efe..fdb1b845ec 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -337,8 +337,6 @@ class VirtualMachine extends EventEmitter { const extensionPromises = []; if (wholeProject) { - this.clear(); - CORE_EXTENSIONS.forEach(extensionID => { if (!this.extensionManager.isExtensionLoaded(extensionID)) { extensionPromises.push(this.extensionManager.loadExtensionURL(extensionID)); From 50379330d2bc6e8b7da7a742300a77f82db60946 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 10 May 2018 13:42:48 -0400 Subject: [PATCH 1642/1971] Merge pull request #1126 from fsih/fixQuirksOnLoadCostume Run quirks mode fixes on costumes loaded from sb2s --- packages/scratch-vm/src/engine/runtime.js | 8 ++++++++ packages/scratch-vm/src/virtual-machine.js | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index aef800133e..82ecca9cbf 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -917,6 +917,14 @@ class Runtime extends EventEmitter { this.renderer = renderer; } + /** + * Set the svg adapter, which converts scratch 2 svgs to scratch 3 svgs + * @param {!SvgRenderer} svgAdapter The adapter to attach + */ + attachV2SVGAdapter (svgAdapter) { + this.v2SvgAdapter = svgAdapter; + } + /** * Attach the storage module * @param {!ScratchStorage} storage The storage module to attach diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index fdb1b845ec..d236650ff0 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -826,6 +826,14 @@ class VirtualMachine extends EventEmitter { this.runtime.attachRenderer(renderer); } + /** + * Set the svg adapter for the VM/runtime, which converts scratch 2 svgs to scratch 3 svgs + * @param {!SvgRenderer} svgAdapter The adapter to attach + */ + attachV2SVGAdapter (svgAdapter) { + this.runtime.attachV2SVGAdapter(svgAdapter); + } + /** * Set the storage module for the VM/runtime * @param {!ScratchStorage} storage The storage module to attach From 376231b69b8cdcd6409db68ec5d3761dbe6d45f5 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 11 May 2018 09:30:05 -0400 Subject: [PATCH 1643/1971] Merge pull request #1141 from ericrosenbaum/feature/extension-id-in-toolbox Add extension id to toolbox category XML --- packages/scratch-vm/src/engine/runtime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 82ecca9cbf..e55f93ec86 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -839,7 +839,7 @@ class Runtime extends EventEmitter { const menuIconXML = menuIconURI ? `iconURI="${menuIconURI}"` : ''; - xmlParts.push(``); + xmlParts.push(``); xmlParts.push.apply(xmlParts, paletteBlocks.map(block => block.xml)); xmlParts.push(''); } From c7045e54c50d4f8354c895862d436f7f1e73b32a Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 11 May 2018 17:11:57 -0400 Subject: [PATCH 1644/1971] Merge pull request #1140 from picklesrus/extension-manager-translate Add translate extension to the extension manager. --- packages/scratch-vm/src/extension-support/extension-manager.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 30e0f26cda..5ea965b211 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -10,12 +10,14 @@ const BlockType = require('./block-type'); const Scratch3PenBlocks = require('../extensions/scratch3_pen'); const Scratch3WeDo2Blocks = require('../extensions/scratch3_wedo2'); const Scratch3MusicBlocks = require('../extensions/scratch3_music'); +const Scratch3TranslateBlocks = require('../extensions/scratch3_translate'); const Scratch3VideoSensingBlocks = require('../extensions/scratch3_video_sensing'); const builtinExtensions = { pen: Scratch3PenBlocks, wedo2: Scratch3WeDo2Blocks, music: Scratch3MusicBlocks, + translate: Scratch3TranslateBlocks, videoSensing: Scratch3VideoSensingBlocks }; From 07af1ec3fd00af2d3a5a80c056240fb4dacf6a5b Mon Sep 17 00:00:00 2001 From: Kreg Hanning Date: Mon, 14 May 2018 13:52:49 -0400 Subject: [PATCH 1645/1971] Add micro:bit Scratch 3.0 extension (#1113) * Add micro:bit Scratch 3.0 extension * Fix lint errors in micro:bit extension * Fix doc. Incorrect return type * Check for valid pin in when pin connected block * Drop mapping function * Drop question mark from tilt hat block * Generate list of symbols from object keys * Trim display text block to max 20 characters --- packages/scratch-vm/src/extension-support/extension-manager.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 5ea965b211..0f7cc2a84c 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -10,6 +10,7 @@ const BlockType = require('./block-type'); const Scratch3PenBlocks = require('../extensions/scratch3_pen'); const Scratch3WeDo2Blocks = require('../extensions/scratch3_wedo2'); const Scratch3MusicBlocks = require('../extensions/scratch3_music'); +const Scratch3MicroBitBlocks = require('../extensions/scratch3_microbit'); const Scratch3TranslateBlocks = require('../extensions/scratch3_translate'); const Scratch3VideoSensingBlocks = require('../extensions/scratch3_video_sensing'); @@ -17,6 +18,7 @@ const builtinExtensions = { pen: Scratch3PenBlocks, wedo2: Scratch3WeDo2Blocks, music: Scratch3MusicBlocks, + microbit: Scratch3MicroBitBlocks, translate: Scratch3TranslateBlocks, videoSensing: Scratch3VideoSensingBlocks }; From 1e19a08625101adc7693437e8e8777077009bbf7 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 24 May 2018 13:58:16 -0400 Subject: [PATCH 1646/1971] Merge pull request #1160 from Kenny2github/patch-1 Round (x position) and (y position) to 8 places --- .../scratch-vm/src/sprites/rendered-target.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index c55042e67e..9fee08afa6 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -237,6 +237,17 @@ class RenderedTarget extends Target { }; } + /** + * Round a number to n digits + * @param {number} value The number to be rounded + * @param {number} places The number of decimal places to round to + * @return {number} The rounded number + */ + _roundCoord (value, places) { + const power = Math.pow(10, places); + return Math.round(value * power) / power; + } + /** * Set the X and Y coordinates. * @param {!number} x New X coordinate, in Scratch coordinates. @@ -250,6 +261,8 @@ class RenderedTarget extends Target { const oldY = this.y; if (this.renderer) { const position = this.renderer.getFencedPositionOfDrawable(this.drawableID, [x, y]); + position[0] = this._roundCoord(position[0], 8); + position[1] = this._roundCoord(position[1], 8); this.x = position[0]; this.y = position[1]; @@ -261,8 +274,8 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } else { - this.x = x; - this.y = y; + this.x = this._roundCoord(x, 8); + this.y = this._roundCoord(y, 8); } this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY, force); this.runtime.requestTargetsUpdate(this); From e4f636e5467d5a98f2366376a7524a92ad047fc6 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Fri, 25 May 2018 01:07:20 -0700 Subject: [PATCH 1647/1971] Merge pull request #1153 from vincentbriglia/issue1551 Add option for Extensions to have different colour --- packages/scratch-vm/src/engine/runtime.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index e55f93ec86..c4e147a6fc 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -498,9 +498,9 @@ class Runtime extends EventEmitter { name: maybeFormatMessage(extensionInfo.name), blockIconURI: extensionInfo.blockIconURI, menuIconURI: extensionInfo.menuIconURI, - color1: '#FF6680', - color2: '#FF4D6A', - color3: '#FF3355', + color1: extensionInfo.colour || '#FF6680', + color2: extensionInfo.colourSecondary || '#FF4D6A', + color3: extensionInfo.colourTertiary || '#FF3355', blocks: [], menus: [] }; From 92d3f4eea25208a16e22dbe8459776681eb65d88 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Fri, 25 May 2018 01:10:57 -0700 Subject: [PATCH 1648/1971] Merge pull request #1154 from vincentbriglia/issue1552 Allow override of icons per block --- packages/scratch-vm/src/engine/runtime.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index c4e147a6fc..fdef792e34 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -649,11 +649,15 @@ class Runtime extends EventEmitter { }; // If an icon for the extension exists, prepend it to each block, with a vertical separator. - if (categoryInfo.blockIconURI) { + // We can overspecify an icon for each block, but if no icon exists on a block, fall back to + // the category block icon. + const iconURI = blockInfo.blockIconURI || categoryInfo.blockIconURI; + + if (iconURI) { blockJSON.message0 = '%1 %2'; const iconJSON = { type: 'field_image', - src: categoryInfo.blockIconURI, + src: iconURI, width: 40, height: 40 }; From 774870e543bb92ba899dc7f0f56819828ba0a421 Mon Sep 17 00:00:00 2001 From: kchadha Date: Fri, 25 May 2018 09:10:05 -0400 Subject: [PATCH 1649/1971] Merge pull request #1148 from kchadha/stage-layering Layer group ordering. --- packages/scratch-vm/src/engine/runtime.js | 2 ++ .../scratch-vm/src/sprites/rendered-target.js | 32 ++++++++++++------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index fdef792e34..ab38955e48 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -12,6 +12,7 @@ const TargetType = require('../extension-support/target-type'); const Thread = require('./thread'); const log = require('../util/log'); const maybeFormatMessage = require('../util/maybe-format-message'); +const StageLayering = require('./stage-layering'); // Virtual I/O devices. const Clock = require('../io/clock'); @@ -919,6 +920,7 @@ class Runtime extends EventEmitter { */ attachRenderer (renderer) { this.renderer = renderer; + this.renderer.setLayerGroupOrdering(StageLayering.LAYER_GROUPS); } /** diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 9fee08afa6..f8a4bb1ebb 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -2,6 +2,7 @@ const log = require('../util/log'); const MathUtil = require('../util/math-util'); const StringUtil = require('../util/string-util'); const Target = require('../engine/target'); +const StageLayering = require('../engine/stage-layering'); /** * Rendered target: instance of a sprite (clone), or the stage. @@ -155,10 +156,11 @@ class RenderedTarget extends Target { /** * Create a drawable with the this.renderer. + * @param {boolean} layerGroup The layer group this drawable should be added to */ - initDrawable () { + initDrawable (layerGroup) { if (this.renderer) { - this.drawableID = this.renderer.createDrawable(); + this.drawableID = this.renderer.createDrawable(layerGroup); } // If we're a clone, start the hats. if (!this.isOriginal) { @@ -813,18 +815,22 @@ class RenderedTarget extends Target { /** * Move to the front layer. */ - goToFront () { + goToFront () { // This should only ever be used for sprites if (this.renderer) { - this.renderer.setDrawableOrder(this.drawableID, Infinity); + // Let the renderer re-order the sprite based on its knowledge + // of what layers are present + this.renderer.setDrawableOrder(this.drawableID, Infinity, StageLayering.SPRITE_LAYER); } } /** * Move to the back layer. */ - goToBack () { + goToBack () { // This should only ever be used for sprites if (this.renderer) { - this.renderer.setDrawableOrder(this.drawableID, -Infinity, false, 1); + // Let the renderer re-order the sprite based on its knowledge + // of what layers are present + this.renderer.setDrawableOrder(this.drawableID, -Infinity, StageLayering.SPRITE_LAYER, false); } } @@ -834,7 +840,7 @@ class RenderedTarget extends Target { */ goForwardLayers (nLayers) { if (this.renderer) { - this.renderer.setDrawableOrder(this.drawableID, nLayers, true, 1); + this.renderer.setDrawableOrder(this.drawableID, nLayers, StageLayering.SPRITE_LAYER, true); } } @@ -844,7 +850,7 @@ class RenderedTarget extends Target { */ goBackwardLayers (nLayers) { if (this.renderer) { - this.renderer.setDrawableOrder(this.drawableID, -nLayers, true, 1); + this.renderer.setDrawableOrder(this.drawableID, -nLayers, StageLayering.SPRITE_LAYER, true); } } @@ -855,8 +861,8 @@ class RenderedTarget extends Target { goBehindOther (other) { if (this.renderer) { const otherLayer = this.renderer.setDrawableOrder( - other.drawableID, 0, true); - this.renderer.setDrawableOrder(this.drawableID, otherLayer); + other.drawableID, 0, StageLayering.SPRITE_LAYER, true); + this.renderer.setDrawableOrder(this.drawableID, otherLayer, StageLayering.SPRITE_LAYER); } } @@ -925,7 +931,7 @@ class RenderedTarget extends Target { newClone.effects = JSON.parse(JSON.stringify(this.effects)); newClone.variables = JSON.parse(JSON.stringify(this.variables)); newClone.lists = JSON.parse(JSON.stringify(this.lists)); - newClone.initDrawable(); + newClone.initDrawable(StageLayering.SPRITE_LAYER); newClone.updateAllDrawableProperties(); // Place behind the current target. newClone.goBehindOther(this); @@ -1062,7 +1068,9 @@ class RenderedTarget extends Target { this.runtime.stopForTarget(this); this.sprite.removeClone(this); if (this.renderer && this.drawableID !== null) { - this.renderer.destroyDrawable(this.drawableID); + this.renderer.destroyDrawable(this.drawableID, this.isStage ? + StageLayering.BACKGROUND_LAYER : + StageLayering.SPRITE_LAYER); if (this.visible) { this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); From 9872511b99f50338aebedb1d6357d1155f761bac Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 31 May 2018 15:35:09 -0400 Subject: [PATCH 1650/1971] Merge pull request #1121 from picklesrus/speech-extension-checkin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initial version of the speech extension. There is certainly still so… --- .../scratch-vm/src/extension-support/extension-manager.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 0f7cc2a84c..147d573057 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -13,6 +13,7 @@ const Scratch3MusicBlocks = require('../extensions/scratch3_music'); const Scratch3MicroBitBlocks = require('../extensions/scratch3_microbit'); const Scratch3TranslateBlocks = require('../extensions/scratch3_translate'); const Scratch3VideoSensingBlocks = require('../extensions/scratch3_video_sensing'); +const Scratch3SpeechBlocks = require('../extensions/scratch3_speech'); const builtinExtensions = { pen: Scratch3PenBlocks, @@ -20,7 +21,8 @@ const builtinExtensions = { music: Scratch3MusicBlocks, microbit: Scratch3MicroBitBlocks, translate: Scratch3TranslateBlocks, - videoSensing: Scratch3VideoSensingBlocks + videoSensing: Scratch3VideoSensingBlocks, + speech: Scratch3SpeechBlocks }; /** From 8ef4b9a165dd0a65a2137d76658eedd9601070e4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 31 May 2018 16:51:22 -0400 Subject: [PATCH 1651/1971] Merge pull request #1180 from kchadha/parse-sb2-comments Import Comments from 2.0 Projects --- packages/scratch-vm/src/sprites/rendered-target.js | 3 --- packages/scratch-vm/src/virtual-machine.js | 6 +++++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index f8a4bb1ebb..b6df05e6c0 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -930,7 +930,6 @@ class RenderedTarget extends Target { newClone.rotationStyle = this.rotationStyle; newClone.effects = JSON.parse(JSON.stringify(this.effects)); newClone.variables = JSON.parse(JSON.stringify(this.variables)); - newClone.lists = JSON.parse(JSON.stringify(this.lists)); newClone.initDrawable(StageLayering.SPRITE_LAYER); newClone.updateAllDrawableProperties(); // Place behind the current target. @@ -957,7 +956,6 @@ class RenderedTarget extends Target { newTarget.rotationStyle = this.rotationStyle; newTarget.effects = JSON.parse(JSON.stringify(this.effects)); newTarget.variables = JSON.parse(JSON.stringify(this.variables)); - newTarget.lists = JSON.parse(JSON.stringify(this.lists)); newTarget.updateAllDrawableProperties(); newTarget.goBehindOther(this); return newTarget; @@ -1049,7 +1047,6 @@ class RenderedTarget extends Target { rotationStyle: this.rotationStyle, blocks: this.blocks._blocks, variables: this.variables, - lists: this.lists, costumes: costumes, sounds: this.getSounds(), tempo: this.tempo, diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index d236650ff0..ca6d8d173d 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -1005,12 +1005,16 @@ class VirtualMachine extends EventEmitter { ); const variables = Object.keys(variableMap).map(k => variableMap[k]); + const workspaceComments = Object.keys(this.editingTarget.comments) + .map(k => this.editingTarget.comments[k]) + .filter(c => c.blockId === null); const xmlString = ` ${variables.map(v => v.toXML()).join()} - ${this.editingTarget.blocks.toXML()} + ${workspaceComments.map(c => c.toXML()).join()} + ${this.editingTarget.blocks.toXML(this.editingTarget.comments)} `; this.emit('workspaceUpdate', {xml: xmlString}); From cbfc2039995cf966e90c440e397749711823f386 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 5 Jun 2018 16:12:46 -0400 Subject: [PATCH 1652/1971] Merge pull request #1203 from paulkaplan/var-apis APIs for getting and setting variable values --- packages/scratch-vm/src/virtual-machine.js | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index ca6d8d173d..1c9c2883d5 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -1071,6 +1071,42 @@ class VirtualMachine extends EventEmitter { this.editingTarget.postSpriteInfo(data); } } + + /** + * Set a target's variable's value. Return whether it succeeded. + * @param {!string} targetId ID of the target which owns the variable. + * @param {!string} variableId ID of the variable to set. + * @param {!*} value The new value of that variable. + * @returns {boolean} whether the target and variable were found and updated. + */ + setVariableValue (targetId, variableId, value) { + const target = this.runtime.getTargetById(targetId); + if (target) { + const variable = target.lookupVariableById(variableId); + if (variable) { + variable.value = value; + return true; + } + } + return false; + } + + /** + * Get a target's variable's value. Return null if the target or variable does not exist. + * @param {!string} targetId ID of the target which owns the variable. + * @param {!string} variableId ID of the variable to set. + * @returns {?*} The value of the variable, or null if it could not be looked up. + */ + getVariableValue (targetId, variableId) { + const target = this.runtime.getTargetById(targetId); + if (target) { + const variable = target.lookupVariableById(variableId); + if (variable) { + return variable.value; + } + } + return null; + } } module.exports = VirtualMachine; From 78f2034223858e44bc6494edd6c9a90320dffe2d Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Tue, 5 Jun 2018 18:35:38 -0400 Subject: [PATCH 1653/1971] Merge pull request #1210 from thisandagain/feature/polly Add initial working version of the Amazon Polly extension --- packages/scratch-vm/src/extension-support/extension-manager.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 147d573057..cff9f4f0ef 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -11,6 +11,7 @@ const Scratch3PenBlocks = require('../extensions/scratch3_pen'); const Scratch3WeDo2Blocks = require('../extensions/scratch3_wedo2'); const Scratch3MusicBlocks = require('../extensions/scratch3_music'); const Scratch3MicroBitBlocks = require('../extensions/scratch3_microbit'); +const Scratch3SpeakBlocks = require('../extensions/scratch3_speak'); const Scratch3TranslateBlocks = require('../extensions/scratch3_translate'); const Scratch3VideoSensingBlocks = require('../extensions/scratch3_video_sensing'); const Scratch3SpeechBlocks = require('../extensions/scratch3_speech'); @@ -20,6 +21,7 @@ const builtinExtensions = { wedo2: Scratch3WeDo2Blocks, music: Scratch3MusicBlocks, microbit: Scratch3MicroBitBlocks, + speak: Scratch3SpeakBlocks, translate: Scratch3TranslateBlocks, videoSensing: Scratch3VideoSensingBlocks, speech: Scratch3SpeechBlocks From 17421abfa08e8c08fd028a174d7250447947b299 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 7 Jun 2018 10:54:15 -0400 Subject: [PATCH 1654/1971] Merge pull request #1204 from paulkaplan/reorder-apis Add methods for reordering costumes and sounds --- .../scratch-vm/src/sprites/rendered-target.js | 45 ++++++++++++++++++- packages/scratch-vm/src/virtual-machine.js | 30 +++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index b6df05e6c0..d6fa706517 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -488,7 +488,7 @@ class RenderedTarget extends Target { * @param {?int} index Index at which to add costume */ addCostume (costumeObject, index) { - if (index) { + if (typeof index === 'number' && !isNaN(index)) { this.sprite.addCostumeAt(costumeObject, index); } else { this.sprite.addCostumeAt(costumeObject, this.sprite.costumes.length); @@ -551,7 +551,7 @@ class RenderedTarget extends Target { addSound (soundObject, index) { const usedNames = this.sprite.sounds.map(sound => sound.name); soundObject.name = StringUtil.unusedName(soundObject.name, usedNames); - if (index) { + if (typeof index === 'number' && !isNaN(index)) { this.sprite.sounds.splice(index, 0, soundObject); } else { this.sprite.sounds.push(soundObject); @@ -640,6 +640,47 @@ class RenderedTarget extends Target { return this.sprite.costumes; } + /** + * Reorder costume list by moving costume at costumeIndex to newIndex. + * @param {!number} costumeIndex Index of the costume to move. + * @param {!number} newIndex New index for that costume. + * @returns {boolean} If a change occurred (i.e. if the indices do not match) + */ + reorderCostume (costumeIndex, newIndex) { + newIndex = MathUtil.clamp(newIndex, 0, this.sprite.costumes.length - 1); + costumeIndex = MathUtil.clamp(costumeIndex, 0, this.sprite.costumes.length - 1); + + if (newIndex === costumeIndex) return false; + + const currentCostume = this.getCurrentCostume(); + const costume = this.sprite.costumes[costumeIndex]; + + // Use the sprite method for deleting costumes because setCostume is handled manually + this.sprite.deleteCostumeAt(costumeIndex); + + this.addCostume(costume, newIndex); + this.currentCostume = this.getCostumeIndexByName(currentCostume.name); + return true; + } + + /** + * Reorder sound list by moving sound at soundIndex to newIndex. + * @param {!number} soundIndex Index of the sound to move. + * @param {!number} newIndex New index for that sound. + * @returns {boolean} If a change occurred (i.e. if the indices do not match) + */ + reorderSound (soundIndex, newIndex) { + newIndex = MathUtil.clamp(newIndex, 0, this.sprite.sounds.length - 1); + soundIndex = MathUtil.clamp(soundIndex, 0, this.sprite.sounds.length - 1); + + if (newIndex === soundIndex) return false; + + const sound = this.sprite.sounds[soundIndex]; + this.deleteSound(soundIndex); + this.addSound(sound, newIndex); + return true; + } + /** * Get full sound list * @return {object[]} list of sounds diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 1c9c2883d5..893f411fc2 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -1033,6 +1033,36 @@ class VirtualMachine extends EventEmitter { return null; } + /** + * Reorder the costumes of a target if it exists. Return whether it succeeded. + * @param {!string} targetId ID of the target which owns the costumes. + * @param {!number} costumeIndex index of the costume to move. + * @param {!number} newIndex index that the costume should be moved to. + * @returns {boolean} Whether a costume was reordered. + */ + reorderCostume (targetId, costumeIndex, newIndex) { + const target = this.runtime.getTargetById(targetId); + if (target) { + return target.reorderCostume(costumeIndex, newIndex); + } + return false; + } + + /** + * Reorder the sounds of a target if it exists. Return whether it occured. + * @param {!string} targetId ID of the target which owns the sounds. + * @param {!number} soundIndex index of the sound to move. + * @param {!number} newIndex index that the sound should be moved to. + * @returns {boolean} Whether a sound was reordered. + */ + reorderSound (targetId, soundIndex, newIndex) { + const target = this.runtime.getTargetById(targetId); + if (target) { + return target.reorderSound(soundIndex, newIndex); + } + return false; + } + /** * Put a target into a "drag" state, during which its X/Y positions will be unaffected * by blocks. From b6b3787c9f44192b704943c94b6d83d09f17b684 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 7 Jun 2018 15:26:09 -0400 Subject: [PATCH 1655/1971] Merge pull request #1214 from ericrosenbaum/feature/when-touching Add when touching hat --- .../scratch-vm/src/sprites/rendered-target.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index d6fa706517..37c1b56151 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -765,6 +765,23 @@ class RenderedTarget extends Target { return null; } + /** + * Return whether this target is touching the mouse, an edge, or a sprite. + * @param {string} requestedObject an id for mouse or edge, or a sprite name. + * @return {boolean} True if the sprite is touching the object. + */ + isTouchingObject (requestedObject) { + if (requestedObject === '_mouse_') { + if (!this.runtime.ioDevices.mouse) return false; + const mouseX = this.runtime.ioDevices.mouse.getClientX(); + const mouseY = this.runtime.ioDevices.mouse.getClientY(); + return this.isTouchingPoint(mouseX, mouseY); + } else if (requestedObject === '_edge_') { + return this.isTouchingEdge(); + } + return this.isTouchingSprite(requestedObject); + } + /** * Return whether touching a point. * @param {number} x X coordinate of test point. From 4f420c5ccca4d42af4db060682d19db9a80f7778 Mon Sep 17 00:00:00 2001 From: Connor Hudson Date: Mon, 11 Jun 2018 10:27:45 -0400 Subject: [PATCH 1656/1971] Merge pull request #1218 from technoboy10/bugfix/gh-1165-monitor-import Fix importing hidden monitors from Scratch 2 projects --- packages/scratch-vm/src/engine/runtime.js | 47 ++++++++++++++++++++--- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index ab38955e48..cf6d90a675 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1518,26 +1518,38 @@ class Runtime extends EventEmitter { /** * Add a monitor to the state. If the monitor already exists in the state, - * overwrites it. + * updates those properties that are defined in the given monitor record. * @param {!MonitorRecord} monitor Monitor to add. */ requestAddMonitor (monitor) { - this._monitorState = this._monitorState.set(monitor.get('id'), monitor); + const id = monitor.get('id'); + if (!this.requestUpdateMonitor(monitor)) { // update monitor if it exists in the state + // if the monitor did not exist in the state, add it + this._monitorState = this._monitorState.set(id, monitor); + } } /** - * Update a monitor in the state. Does nothing if the monitor does not already - * exist in the state. + * Update a monitor in the state and report success/failure of update. * @param {!Map} monitor Monitor values to update. Values on the monitor with overwrite * values on the old monitor with the same ID. If a value isn't defined on the new monitor, * the old monitor will keep its old value. + * @return {boolean} true if monitor exists in the state and was updated, false if it did not exist. */ requestUpdateMonitor (monitor) { const id = monitor.get('id'); if (this._monitorState.has(id)) { this._monitorState = - this._monitorState.set(id, this._monitorState.get(id).merge(monitor)); + // Use mergeWith here to prevent undefined values from overwriting existing ones + this._monitorState.set(id, this._monitorState.get(id).mergeWith((prev, next) => { + if (typeof next === 'undefined' || next === null) { + return prev; + } + return next; + }, monitor)); + return true; } + return false; } /** @@ -1549,6 +1561,31 @@ class Runtime extends EventEmitter { this._monitorState = this._monitorState.delete(monitorId); } + /** + * Hides a monitor and returns success/failure of action. + * @param {!string} monitorId ID of the monitor to hide. + * @return {boolean} true if monitor exists and was updated, false otherwise + */ + requestHideMonitor (monitorId) { + return this.requestUpdateMonitor(new Map([ + ['id', monitorId], + ['visible', false] + ])); + } + + /** + * Shows a monitor and returns success/failure of action. + * not exist in the state. + * @param {!string} monitorId ID of the monitor to show. + * @return {boolean} true if monitor exists and was updated, false otherwise + */ + requestShowMonitor (monitorId) { + return this.requestUpdateMonitor(new Map([ + ['id', monitorId], + ['visible', true] + ])); + } + /** * Removes all monitors with the given target ID from the state. Does nothing if * the monitor already does not exist in the state. From d44b2c8da634522b2aae0ae48454753d341ccf7a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 13 Jun 2018 11:07:52 -0400 Subject: [PATCH 1657/1971] Merge pull request #1231 from paulkaplan/reorder-tarrgets Add top-level `reorderTarget` API to VM --- packages/scratch-vm/src/virtual-machine.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 893f411fc2..4ff368faaf 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -6,6 +6,7 @@ const Buffer = require('buffer').Buffer; const centralDispatch = require('./dispatch/central-dispatch'); const ExtensionManager = require('./extension-support/extension-manager'); const log = require('./util/log'); +const MathUtil = require('./util/math-util'); const Runtime = require('./engine/runtime'); const sb2 = require('./serialization/sb2'); const sb3 = require('./serialization/sb3'); @@ -1033,6 +1034,25 @@ class VirtualMachine extends EventEmitter { return null; } + /** + * Reorder target by index. Return whether a change was made. + * @param {!string} targetIndex Index of the target. + * @param {!number} newIndex index that the target should be moved to. + * @returns {boolean} Whether a target was reordered. + */ + reorderTarget (targetIndex, newIndex) { + let targets = this.runtime.targets; + targetIndex = MathUtil.clamp(targetIndex, 0, targets.length - 1); + newIndex = MathUtil.clamp(newIndex, 0, targets.length - 1); + if (targetIndex === newIndex) return false; + const target = targets[targetIndex]; + targets = targets.slice(0, targetIndex).concat(targets.slice(targetIndex + 1)); + targets.splice(newIndex, 0, target); + this.runtime.targets = targets; + this.emitTargetsUpdate(); + return true; + } + /** * Reorder the costumes of a target if it exists. Return whether it succeeded. * @param {!string} targetId ID of the target which owns the costumes. From 408ddfab454f8e4bba1a8dc7134928958b42522d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 13 Jun 2018 15:58:37 -0400 Subject: [PATCH 1658/1971] Merge pull request #1211 from mzgoddard/broadcast-and-wait-promise yield a tick if broadcastandwait is waiting on threads waiting for a future tick --- packages/scratch-vm/src/engine/runtime.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index cf6d90a675..5edb792330 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1017,6 +1017,19 @@ class Runtime extends EventEmitter { this.threads.indexOf(thread) > -1); } + /** + * Return whether a thread is waiting for more information or done. + * @param {?Thread} thread Thread object to check. + * @return {boolean} True if the thread is waiting + */ + isWaitingThread (thread) { + return ( + thread.status === Thread.STATUS_PROMISE_WAIT || + thread.status === Thread.STATUS_YIELD_TICK || + !this.isActiveThread(thread) + ); + } + /** * Toggle a script. * @param {!string} topBlockId ID of block that starts the script. From 28917fffcd5b9a4b9a89bfc8693e24e0a6e0cc3b Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 14 Jun 2018 11:51:32 -0400 Subject: [PATCH 1659/1971] Merge pull request #1233 from ericrosenbaum/feature/extension-status-button Add extension status button --- packages/scratch-vm/src/engine/runtime.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 5edb792330..266e2ce87c 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -546,6 +546,16 @@ class Runtime extends EventEmitter { categoryInfo.menus.push(convertedMenu); } } + + // Add extension status button + if (extensionInfo.showStatusButton) { + categoryInfo.blocks.push({ + info: {}, + json: null, + xml: `` + }); + } + for (const blockInfo of extensionInfo.blocks) { if (blockInfo === '---') { categoryInfo.blocks.push(ConvertedSeparator); From 7a2a390145642c8f41a796c59dced5e8acd4f8e2 Mon Sep 17 00:00:00 2001 From: kchadha Date: Thu, 14 Jun 2018 18:42:19 -0400 Subject: [PATCH 1660/1971] Merge pull request #1236 from kchadha/comment-save-load Comment Save & Load --- packages/scratch-vm/src/sprites/rendered-target.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 37c1b56151..6323743849 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -1103,6 +1103,7 @@ class RenderedTarget extends Target { costumeCount: costumes.length, visible: this.visible, rotationStyle: this.rotationStyle, + comments: this.comments, blocks: this.blocks._blocks, variables: this.variables, costumes: costumes, From f093be1100e281775753616dfa9c31f6c5912ac2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 19 Jun 2018 11:31:00 -0400 Subject: [PATCH 1661/1971] Merge pull request #1244 from paulkaplan/sharing-apis Add shareSound and shareCostume APIs with unit tests to the VM --- packages/scratch-vm/src/virtual-machine.js | 40 ++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 4ff368faaf..767641eece 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -935,6 +935,46 @@ class VirtualMachine extends EventEmitter { target.blocks.updateTargetSpecificBlocks(target.isStage); } + /** + * Called when costumes are dragged from editing target to another target. + * Sets the newly added costume as the current costume. + * @param {!number} costumeIndex Index of the costume of the editing target to share. + * @param {!string} targetId Id of target to add the costume. + * @return {Promise} Promise that resolves when the new costume has been loaded. + */ + shareCostumeToTarget (costumeIndex, targetId) { + const originalCostume = this.editingTarget.getCostumes()[costumeIndex]; + const clone = Object.assign({}, originalCostume); + const md5ext = `${clone.assetId}.${clone.dataFormat}`; + return loadCostume(md5ext, clone, this.runtime).then(() => { + const target = this.runtime.getTargetById(targetId); + if (target) { + target.addCostume(clone); + target.setCostume( + target.getCostumes().length - 1 + ); + } + }); + } + + /** + * Called when sounds are dragged from editing target to another target. + * @param {!number} soundIndex Index of the sound of the editing target to share. + * @param {!string} targetId Id of target to add the sound. + * @return {Promise} Promise that resolves when the new sound has been loaded. + */ + shareSoundToTarget (soundIndex, targetId) { + const originalSound = this.editingTarget.getSounds()[soundIndex]; + const clone = Object.assign({}, originalSound); + return loadSound(clone, this.runtime).then(() => { + const target = this.runtime.getTargetById(targetId); + if (target) { + target.addSound(clone); + this.emitTargetsUpdate(); + } + }); + } + /** * Repopulate the workspace with the blocks of the current editingTarget. This * allows us to get around bugs like gui#413. From 8b0ce0a786d3724b70744ad8db1ef65ced88cbb4 Mon Sep 17 00:00:00 2001 From: kchadha Date: Wed, 20 Jun 2018 14:40:11 -0400 Subject: [PATCH 1662/1971] Merge pull request #1248 from towerofnix/sprite-quad-fix Fix new sprites only showing up in the top-right quadrant --- packages/scratch-vm/src/sprites/rendered-target.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 6323743849..aac0b059ae 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -1004,8 +1004,8 @@ class RenderedTarget extends Target { const newTarget = newSprite.createClone(); // Copy all properties. // @todo refactor with clone methods - newTarget.x = Math.random() * 400 / 2; - newTarget.y = Math.random() * 300 / 2; + newTarget.x = (Math.random() - 0.5) * 400 / 2; + newTarget.y = (Math.random() - 0.5) * 300 / 2; newTarget.direction = this.direction; newTarget.draggable = this.draggable; newTarget.visible = this.visible; From 35d34b0fcc8c61bf7c9fb58e19b4e281b8befcdf Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Thu, 21 Jun 2018 17:14:27 -0400 Subject: [PATCH 1663/1971] Merge pull request #1239 from gnarf/soundbank Use new scratch-audio SoundBank in vm / sounds --- .../scratch-vm/src/sprites/rendered-target.js | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index aac0b059ae..93e7d8c7f5 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -170,21 +170,30 @@ class RenderedTarget extends Target { } } + get audioPlayer () { + /* eslint-disable no-console */ + console.warn('get audioPlayer deprecated, please update to use .sprite.soundBank methods'); + console.warn(new Error('stack for debug').stack); + /* eslint-enable no-console */ + const bank = this.sprite.soundBank; + const audioPlayerProxy = { + playSound: soundId => bank.play(this, soundId) + }; + + Object.defineProperty(this, 'audioPlayer', { + configurable: false, + enumerable: true, + writable: false, + value: audioPlayerProxy + }); + + return audioPlayerProxy; + } + /** * Initialize the audio player for this sprite or clone. */ initAudio () { - this.audioPlayer = null; - if (this.runtime && this.runtime.audioEngine) { - this.audioPlayer = this.runtime.audioEngine.createPlayer(); - // If this is a clone, it gets a reference to its parent's activeSoundPlayers object. - if (!this.isOriginal) { - const parent = this.sprite.clones[0]; - if (parent && parent.audioPlayer) { - this.audioPlayer.activeSoundPlayers = parent.audioPlayer.activeSoundPlayers; - } - } - } } /** @@ -1034,9 +1043,8 @@ class RenderedTarget extends Target { */ onStopAll () { this.clearEffects(); - if (this.audioPlayer) { - this.audioPlayer.stopAllSounds(); - this.audioPlayer.clearEffects(); + if (this.sprite.soundBank) { + this.sprite.soundBank.stopAllSounds(this); } } @@ -1122,6 +1130,9 @@ class RenderedTarget extends Target { dispose () { this.runtime.changeCloneCounter(-1); this.runtime.stopForTarget(this); + if (this.sprite.soundBank) { + this.sprite.soundBank.stopAllSounds(this); + } this.sprite.removeClone(this); if (this.renderer && this.drawableID !== null) { this.renderer.destroyDrawable(this.drawableID, this.isStage ? @@ -1132,10 +1143,6 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } - if (this.audioPlayer) { - this.audioPlayer.stopAllSounds(); - this.audioPlayer.dispose(); - } } } From 55329d58c5f6044d5e83abda2d2bba2f63ed46d5 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Thu, 21 Jun 2018 17:23:33 -0400 Subject: [PATCH 1664/1971] add decoded SoundPlayer's to a Sprite's SoundBank (#1260) --- packages/scratch-vm/src/virtual-machine.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 767641eece..9e93b79d2a 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -525,7 +525,7 @@ class VirtualMachine extends EventEmitter { * @returns {?Promise} - a promise that resolves when the sound has been decoded and added */ addSound (soundObject) { - return loadSound(soundObject, this.runtime).then(() => { + return loadSound(soundObject, this.runtime, this.editingTarget.sprite).then(() => { this.editingTarget.addSound(soundObject); this.emitTargetsUpdate(); }); @@ -549,7 +549,7 @@ class VirtualMachine extends EventEmitter { getSoundBuffer (soundIndex) { const id = this.editingTarget.sprite.sounds[soundIndex].soundId; if (id && this.runtime && this.runtime.audioEngine) { - return this.runtime.audioEngine.getSoundBuffer(id); + return this.editingTarget.sprite.soundBank.getSoundPlayer(id).buffer; } return null; } @@ -564,7 +564,7 @@ class VirtualMachine extends EventEmitter { const sound = this.editingTarget.sprite.sounds[soundIndex]; const id = sound ? sound.soundId : null; if (id && this.runtime && this.runtime.audioEngine) { - this.runtime.audioEngine.updateSoundBuffer(id, newBuffer); + this.editingTarget.sprite.soundBank.getSoundPlayer(id).buffer = newBuffer; } // Update sound in runtime if (soundEncoding) { From 51cd9b2999c30441f91884f08c29f2b5208dff04 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 22 Jun 2018 09:45:16 -0400 Subject: [PATCH 1665/1971] Merge pull request #1262 from rschamp/revert-audio Revert #1260, #1258, #1239 --- .../scratch-vm/src/sprites/rendered-target.js | 43 ++++++++----------- packages/scratch-vm/src/virtual-machine.js | 6 +-- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 93e7d8c7f5..aac0b059ae 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -170,30 +170,21 @@ class RenderedTarget extends Target { } } - get audioPlayer () { - /* eslint-disable no-console */ - console.warn('get audioPlayer deprecated, please update to use .sprite.soundBank methods'); - console.warn(new Error('stack for debug').stack); - /* eslint-enable no-console */ - const bank = this.sprite.soundBank; - const audioPlayerProxy = { - playSound: soundId => bank.play(this, soundId) - }; - - Object.defineProperty(this, 'audioPlayer', { - configurable: false, - enumerable: true, - writable: false, - value: audioPlayerProxy - }); - - return audioPlayerProxy; - } - /** * Initialize the audio player for this sprite or clone. */ initAudio () { + this.audioPlayer = null; + if (this.runtime && this.runtime.audioEngine) { + this.audioPlayer = this.runtime.audioEngine.createPlayer(); + // If this is a clone, it gets a reference to its parent's activeSoundPlayers object. + if (!this.isOriginal) { + const parent = this.sprite.clones[0]; + if (parent && parent.audioPlayer) { + this.audioPlayer.activeSoundPlayers = parent.audioPlayer.activeSoundPlayers; + } + } + } } /** @@ -1043,8 +1034,9 @@ class RenderedTarget extends Target { */ onStopAll () { this.clearEffects(); - if (this.sprite.soundBank) { - this.sprite.soundBank.stopAllSounds(this); + if (this.audioPlayer) { + this.audioPlayer.stopAllSounds(); + this.audioPlayer.clearEffects(); } } @@ -1130,9 +1122,6 @@ class RenderedTarget extends Target { dispose () { this.runtime.changeCloneCounter(-1); this.runtime.stopForTarget(this); - if (this.sprite.soundBank) { - this.sprite.soundBank.stopAllSounds(this); - } this.sprite.removeClone(this); if (this.renderer && this.drawableID !== null) { this.renderer.destroyDrawable(this.drawableID, this.isStage ? @@ -1143,6 +1132,10 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } + if (this.audioPlayer) { + this.audioPlayer.stopAllSounds(); + this.audioPlayer.dispose(); + } } } diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 9e93b79d2a..767641eece 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -525,7 +525,7 @@ class VirtualMachine extends EventEmitter { * @returns {?Promise} - a promise that resolves when the sound has been decoded and added */ addSound (soundObject) { - return loadSound(soundObject, this.runtime, this.editingTarget.sprite).then(() => { + return loadSound(soundObject, this.runtime).then(() => { this.editingTarget.addSound(soundObject); this.emitTargetsUpdate(); }); @@ -549,7 +549,7 @@ class VirtualMachine extends EventEmitter { getSoundBuffer (soundIndex) { const id = this.editingTarget.sprite.sounds[soundIndex].soundId; if (id && this.runtime && this.runtime.audioEngine) { - return this.editingTarget.sprite.soundBank.getSoundPlayer(id).buffer; + return this.runtime.audioEngine.getSoundBuffer(id); } return null; } @@ -564,7 +564,7 @@ class VirtualMachine extends EventEmitter { const sound = this.editingTarget.sprite.sounds[soundIndex]; const id = sound ? sound.soundId : null; if (id && this.runtime && this.runtime.audioEngine) { - this.editingTarget.sprite.soundBank.getSoundPlayer(id).buffer = newBuffer; + this.runtime.audioEngine.updateSoundBuffer(id, newBuffer); } // Update sound in runtime if (soundEncoding) { From ad6d67980a1abc4ece11bb71a7f063845c7b1564 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 22 Jun 2018 09:53:24 -0400 Subject: [PATCH 1666/1971] Merge pull request #1264 from LLK/unrevert-audio Re-apply audio engine updates --- .../scratch-vm/src/sprites/rendered-target.js | 43 +++++++++++-------- packages/scratch-vm/src/virtual-machine.js | 12 +++--- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index aac0b059ae..93e7d8c7f5 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -170,21 +170,30 @@ class RenderedTarget extends Target { } } + get audioPlayer () { + /* eslint-disable no-console */ + console.warn('get audioPlayer deprecated, please update to use .sprite.soundBank methods'); + console.warn(new Error('stack for debug').stack); + /* eslint-enable no-console */ + const bank = this.sprite.soundBank; + const audioPlayerProxy = { + playSound: soundId => bank.play(this, soundId) + }; + + Object.defineProperty(this, 'audioPlayer', { + configurable: false, + enumerable: true, + writable: false, + value: audioPlayerProxy + }); + + return audioPlayerProxy; + } + /** * Initialize the audio player for this sprite or clone. */ initAudio () { - this.audioPlayer = null; - if (this.runtime && this.runtime.audioEngine) { - this.audioPlayer = this.runtime.audioEngine.createPlayer(); - // If this is a clone, it gets a reference to its parent's activeSoundPlayers object. - if (!this.isOriginal) { - const parent = this.sprite.clones[0]; - if (parent && parent.audioPlayer) { - this.audioPlayer.activeSoundPlayers = parent.audioPlayer.activeSoundPlayers; - } - } - } } /** @@ -1034,9 +1043,8 @@ class RenderedTarget extends Target { */ onStopAll () { this.clearEffects(); - if (this.audioPlayer) { - this.audioPlayer.stopAllSounds(); - this.audioPlayer.clearEffects(); + if (this.sprite.soundBank) { + this.sprite.soundBank.stopAllSounds(this); } } @@ -1122,6 +1130,9 @@ class RenderedTarget extends Target { dispose () { this.runtime.changeCloneCounter(-1); this.runtime.stopForTarget(this); + if (this.sprite.soundBank) { + this.sprite.soundBank.stopAllSounds(this); + } this.sprite.removeClone(this); if (this.renderer && this.drawableID !== null) { this.renderer.destroyDrawable(this.drawableID, this.isStage ? @@ -1132,10 +1143,6 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } - if (this.audioPlayer) { - this.audioPlayer.stopAllSounds(); - this.audioPlayer.dispose(); - } } } diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 767641eece..57ae97e76f 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -495,7 +495,7 @@ class VirtualMachine extends EventEmitter { duplicateSound (soundIndex) { const originalSound = this.editingTarget.getSounds()[soundIndex]; const clone = Object.assign({}, originalSound); - return loadSound(clone, this.runtime).then(() => { + return loadSound(clone, this.runtime, this.editingTarget.sprite).then(() => { this.editingTarget.addSound(clone, soundIndex + 1); this.emitTargetsUpdate(); }); @@ -525,7 +525,7 @@ class VirtualMachine extends EventEmitter { * @returns {?Promise} - a promise that resolves when the sound has been decoded and added */ addSound (soundObject) { - return loadSound(soundObject, this.runtime).then(() => { + return loadSound(soundObject, this.runtime, this.editingTarget.sprite).then(() => { this.editingTarget.addSound(soundObject); this.emitTargetsUpdate(); }); @@ -549,7 +549,7 @@ class VirtualMachine extends EventEmitter { getSoundBuffer (soundIndex) { const id = this.editingTarget.sprite.sounds[soundIndex].soundId; if (id && this.runtime && this.runtime.audioEngine) { - return this.runtime.audioEngine.getSoundBuffer(id); + return this.editingTarget.sprite.soundBank.getSoundPlayer(id).buffer; } return null; } @@ -564,7 +564,7 @@ class VirtualMachine extends EventEmitter { const sound = this.editingTarget.sprite.sounds[soundIndex]; const id = sound ? sound.soundId : null; if (id && this.runtime && this.runtime.audioEngine) { - this.runtime.audioEngine.updateSoundBuffer(id, newBuffer); + this.editingTarget.sprite.soundBank.getSoundPlayer(id).buffer = newBuffer; } // Update sound in runtime if (soundEncoding) { @@ -966,8 +966,8 @@ class VirtualMachine extends EventEmitter { shareSoundToTarget (soundIndex, targetId) { const originalSound = this.editingTarget.getSounds()[soundIndex]; const clone = Object.assign({}, originalSound); - return loadSound(clone, this.runtime).then(() => { - const target = this.runtime.getTargetById(targetId); + const target = this.runtime.getTargetById(targetId); + return loadSound(clone, this.runtime, target.sprite).then(() => { if (target) { target.addSound(clone); this.emitTargetsUpdate(); From b919a34ad1671af1c4268ac7e400e1363ecca7bf Mon Sep 17 00:00:00 2001 From: kchadha Date: Mon, 25 Jun 2018 11:21:01 -0400 Subject: [PATCH 1667/1971] Merge pull request #1256 from kchadha/sprite-save-load Sprite save load --- packages/scratch-vm/src/virtual-machine.js | 50 ++++++++++++++++++---- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 57ae97e76f..940991e49b 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -263,14 +263,7 @@ class VirtualMachine extends EventEmitter { // Put everything in a zip file zip.file('project.json', projectJson); - for (let i = 0; i < soundDescs.length; i++) { - const currSound = soundDescs[i]; - zip.file(currSound.fileName, currSound.fileContent); - } - for (let i = 0; i < costumeDescs.length; i++) { - const currCostume = costumeDescs[i]; - zip.file(currCostume.fileName, currCostume.fileContent); - } + this._addFileDescsToZip(soundDescs.concat(costumeDescs), zip); return zip.generateAsync({ type: 'blob', @@ -281,6 +274,43 @@ class VirtualMachine extends EventEmitter { }); } + _addFileDescsToZip (fileDescs, zip) { + for (let i = 0; i < fileDescs.length; i++) { + const currFileDesc = fileDescs[i]; + zip.file(currFileDesc.fileName, currFileDesc.fileContent); + } + } + + /** + * Exports a sprite in the sprite3 format. + * @param {string} targetId ID of the target to export + * @param {string=} optZipType Optional type that the resulting + * zip should be outputted in. Options are: base64, binarystring, + * array, uint8array, arraybuffer, blob, or nodebuffer. Defaults to + * blob if argument not provided. + * See https://stuk.github.io/jszip/documentation/api_jszip/generate_async.html#type-option + * for more information about these options. + * @return {object} A generated zip of the sprite and its assets in the format + * specified by optZipType or blob by default. + */ + exportSprite (targetId, optZipType) { + const soundDescs = serializeSounds(this.runtime, targetId); + const costumeDescs = serializeCostumes(this.runtime, targetId); + const spriteJson = JSON.stringify(sb3.serialize(this.runtime, targetId)); + + const zip = new JSZip(); + zip.file('sprite.json', spriteJson); + this._addFileDescsToZip(soundDescs.concat(costumeDescs), zip); + + return zip.generateAsync({ + type: typeof optZipType === 'string' ? optZipType : 'blob', + compression: 'DEFLATE', + compressionOptions: { + level: 6 + } + }); + } + /** * Export project as a Scratch 3.0 JSON representation. * @return {string} Serialized state of the runtime. @@ -368,6 +398,10 @@ class VirtualMachine extends EventEmitter { this.editingTarget = targets[0]; } + if (!wholeProject) { + this.editingTarget.fixUpVariableReferences(); + } + // Update the VM user's knowledge of targets and blocks on the workspace. this.emitTargetsUpdate(); this.emitWorkspaceUpdate(); From 53e6803d28ca055de617b902cd160b94ff9bc308 Mon Sep 17 00:00:00 2001 From: Mx Corey Frang Date: Mon, 25 Jun 2018 15:44:39 -0400 Subject: [PATCH 1668/1971] delete clone should not stop sound (#1269) --- packages/scratch-vm/src/sprites/rendered-target.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 93e7d8c7f5..eb996786be 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -1044,7 +1044,7 @@ class RenderedTarget extends Target { onStopAll () { this.clearEffects(); if (this.sprite.soundBank) { - this.sprite.soundBank.stopAllSounds(this); + this.sprite.soundBank.stopAllSounds(); } } @@ -1130,9 +1130,6 @@ class RenderedTarget extends Target { dispose () { this.runtime.changeCloneCounter(-1); this.runtime.stopForTarget(this); - if (this.sprite.soundBank) { - this.sprite.soundBank.stopAllSounds(this); - } this.sprite.removeClone(this); if (this.renderer && this.drawableID !== null) { this.renderer.destroyDrawable(this.drawableID, this.isStage ? From 6a8521e2f439f309088f063ed8fc1c32745b75db Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 27 Jun 2018 15:53:18 -0400 Subject: [PATCH 1669/1971] Show extension status button via XML attr not button (#1270) --- packages/scratch-vm/src/engine/runtime.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 266e2ce87c..f34ccb6047 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -497,6 +497,7 @@ class Runtime extends EventEmitter { const categoryInfo = { id: extensionInfo.id, name: maybeFormatMessage(extensionInfo.name), + showStatusButton: extensionInfo.showStatusButton, blockIconURI: extensionInfo.blockIconURI, menuIconURI: extensionInfo.menuIconURI, color1: extensionInfo.colour || '#FF6680', @@ -547,15 +548,6 @@ class Runtime extends EventEmitter { } } - // Add extension status button - if (extensionInfo.showStatusButton) { - categoryInfo.blocks.push({ - info: {}, - json: null, - xml: `` - }); - } - for (const blockInfo of extensionInfo.blocks) { if (blockInfo === '---') { categoryInfo.blocks.push(ConvertedSeparator); @@ -854,7 +846,13 @@ class Runtime extends EventEmitter { const menuIconXML = menuIconURI ? `iconURI="${menuIconURI}"` : ''; - xmlParts.push(``); + let statusButtonXML = ''; + if (categoryInfo.showStatusButton) { + statusButtonXML = 'showStatusButton="true"'; + } + + xmlParts.push(``); xmlParts.push.apply(xmlParts, paletteBlocks.map(block => block.xml)); xmlParts.push(''); } From 8643e87f19342705da359eee2615dbbf200d3788 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 28 Jun 2018 10:26:18 -0400 Subject: [PATCH 1670/1971] Merge pull request #1276 from thisandagain/bugfix/1275 Update colors for extensions --- packages/scratch-vm/src/engine/runtime.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index f34ccb6047..517ae48d91 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -500,9 +500,9 @@ class Runtime extends EventEmitter { showStatusButton: extensionInfo.showStatusButton, blockIconURI: extensionInfo.blockIconURI, menuIconURI: extensionInfo.menuIconURI, - color1: extensionInfo.colour || '#FF6680', - color2: extensionInfo.colourSecondary || '#FF4D6A', - color3: extensionInfo.colourTertiary || '#FF3355', + color1: extensionInfo.colour || '#0FBD8C', + color2: extensionInfo.colourSecondary || '#0DA57A', + color3: extensionInfo.colourTertiary || '#0B8E69', blocks: [], menus: [] }; From 571b43f7814087fca285997265ceb76285b84548 Mon Sep 17 00:00:00 2001 From: kchadha Date: Fri, 6 Jul 2018 14:39:56 -0400 Subject: [PATCH 1671/1971] Merge pull request #1301 from kchadha/variable-scope Variable scope --- packages/scratch-vm/src/engine/runtime.js | 9 +++++++++ packages/scratch-vm/src/virtual-machine.js | 14 ++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 517ae48d91..1ba19570bc 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1715,6 +1715,15 @@ class Runtime extends EventEmitter { return this._editingTarget; } + getAllVarNamesOfType (varType) { + let varNames = []; + for (const target of this.targets) { + const targetVarNames = target.getAllVariableNamesInScopeByType(varType, true); + varNames = varNames.concat(targetVarNames); + } + return varNames; + } + /** * Tell the runtime to request a redraw. * Use after a clone/sprite has completed some visible operation on the stage. diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 940991e49b..922c2fc202 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -1074,19 +1074,21 @@ class VirtualMachine extends EventEmitter { const id = messageIds[i]; delete this.runtime.getTargetForStage().variables[id]; } - const variableMap = Object.assign({}, - this.runtime.getTargetForStage().variables, - this.editingTarget.variables - ); + const globalVarMap = Object.assign({}, this.runtime.getTargetForStage().variables); + const localVarMap = this.editingTarget.isStage ? + Object.create(null) : + Object.assign({}, this.editingTarget.variables); - const variables = Object.keys(variableMap).map(k => variableMap[k]); + const globalVariables = Object.keys(globalVarMap).map(k => globalVarMap[k]); + const localVariables = Object.keys(localVarMap).map(k => localVarMap[k]); const workspaceComments = Object.keys(this.editingTarget.comments) .map(k => this.editingTarget.comments[k]) .filter(c => c.blockId === null); const xmlString = ` - ${variables.map(v => v.toXML()).join()} + ${globalVariables.map(v => v.toXML()).join()} + ${localVariables.map(v => v.toXML(true)).join()} ${workspaceComments.map(c => c.toXML()).join()} ${this.editingTarget.blocks.toXML(this.editingTarget.comments)} From 0296df02b38d28dda72ed71d7eb6bc79d4b654f5 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 9 Jul 2018 14:26:06 -0400 Subject: [PATCH 1672/1971] Merge pull request #1285 from LLK/feature/device-connection Device connection update for micro:bit and EV3 extensions --- packages/scratch-vm/src/engine/runtime.js | 56 +++++++++++++++++++ .../extension-support/extension-manager.js | 4 +- packages/scratch-vm/src/virtual-machine.js | 26 +++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 1ba19570bc..530db3b40a 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -254,6 +254,8 @@ class Runtime extends EventEmitter { video: new Video(this) }; + this.extensionDevices = {}; + /** * A runtime profiler that records timed events for later playback to * diagnose Scratch performance. @@ -394,6 +396,30 @@ class Runtime extends EventEmitter { return 'EXTENSION_ADDED'; } + /** + * Event name for updating the available set of peripheral devices. + * @const {string} + */ + static get PERIPHERAL_LIST_UPDATE () { + return 'PERIPHERAL_LIST_UPDATE'; + } + + /** + * Event name for reporting that a peripheral has connected. + * @const {string} + */ + static get PERIPHERAL_CONNECTED () { + return 'PERIPHERAL_CONNECTED'; + } + + /** + * Event name for reporting that a peripheral has encountered an error. + * @const {string} + */ + static get PERIPHERAL_ERROR () { + return 'PERIPHERAL_ERROR'; + } + /** * Event name for reporting that blocksInfo was updated. * @const {string} @@ -867,6 +893,36 @@ class Runtime extends EventEmitter { (result, categoryInfo) => result.concat(categoryInfo.blocks.map(blockInfo => blockInfo.json)), []); } + registerExtensionDevice (extensionId, device) { + this.extensionDevices[extensionId] = device; + } + + startDeviceScan (extensionId) { + if (this.extensionDevices[extensionId]) { + this.extensionDevices[extensionId].startDeviceScan(); + } + } + + connectToPeripheral (extensionId, peripheralId) { + if (this.extensionDevices[extensionId]) { + this.extensionDevices[extensionId].connectDevice(peripheralId); + } + } + + disconnectExtensionSession (extensionId) { + if (this.extensionDevices[extensionId]) { + this.extensionDevices[extensionId].disconnectSession(); + } + } + + getPeripheralIsConnected (extensionId) { + let isConnected = false; + if (this.extensionDevices[extensionId]) { + isConnected = this.extensionDevices[extensionId].getPeripheralIsConnected(); + } + return isConnected; + } + /** * Retrieve the function associated with the given opcode. * @param {!string} opcode The opcode to look up. diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index cff9f4f0ef..7a004104f9 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -15,6 +15,7 @@ const Scratch3SpeakBlocks = require('../extensions/scratch3_speak'); const Scratch3TranslateBlocks = require('../extensions/scratch3_translate'); const Scratch3VideoSensingBlocks = require('../extensions/scratch3_video_sensing'); const Scratch3SpeechBlocks = require('../extensions/scratch3_speech'); +const Scratch3Ev3Blocks = require('../extensions/scratch3_ev3'); const builtinExtensions = { pen: Scratch3PenBlocks, @@ -24,7 +25,8 @@ const builtinExtensions = { speak: Scratch3SpeakBlocks, translate: Scratch3TranslateBlocks, videoSensing: Scratch3VideoSensingBlocks, - speech: Scratch3SpeechBlocks + speech: Scratch3SpeechBlocks, + ev3: Scratch3Ev3Blocks }; /** diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 922c2fc202..e95258fb41 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -106,6 +106,16 @@ class VirtualMachine extends EventEmitter { this.emit(Runtime.BLOCKSINFO_UPDATE, blocksInfo); }); + this.runtime.on(Runtime.PERIPHERAL_LIST_UPDATE, info => { + this.emit(Runtime.PERIPHERAL_LIST_UPDATE, info); + }); + this.runtime.on(Runtime.PERIPHERAL_CONNECTED, () => + this.emit(Runtime.PERIPHERAL_CONNECTED) + ); + this.runtime.on(Runtime.PERIPHERAL_ERROR, () => + this.emit(Runtime.PERIPHERAL_ERROR) + ); + this.extensionManager = new ExtensionManager(this.runtime); this.blockListener = this.blockListener.bind(this); @@ -195,6 +205,22 @@ class VirtualMachine extends EventEmitter { this.runtime.ioDevices.video.setProvider(videoProvider); } + startDeviceScan (extensionId) { + this.runtime.startDeviceScan(extensionId); + } + + connectToPeripheral (extensionId, peripheralId) { + this.runtime.connectToPeripheral(extensionId, peripheralId); + } + + disconnectExtensionSession (extensionId) { + this.runtime.disconnectExtensionSession(extensionId); + } + + getPeripheralIsConnected (extensionId) { + return this.runtime.getPeripheralIsConnected(extensionId); + } + /** * Load a Scratch project from a .sb, .sb2, .sb3 or json string. * @param {string | object} input A json string, object, or ArrayBuffer representing the project to load. From 272a5544a5fbbe539dfb5808e1beb9385396c144 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Tue, 10 Jul 2018 08:30:52 -0400 Subject: [PATCH 1673/1971] Merge pull request #1311 from thisandagain/feature/polly Update text-to-speech extension to use new audio engine --- .../scratch-vm/src/extension-support/extension-manager.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 7a004104f9..66235b24a3 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -11,7 +11,7 @@ const Scratch3PenBlocks = require('../extensions/scratch3_pen'); const Scratch3WeDo2Blocks = require('../extensions/scratch3_wedo2'); const Scratch3MusicBlocks = require('../extensions/scratch3_music'); const Scratch3MicroBitBlocks = require('../extensions/scratch3_microbit'); -const Scratch3SpeakBlocks = require('../extensions/scratch3_speak'); +const Scratch3Text2SpeechBlocks = require('../extensions/scratch3_text2speech'); const Scratch3TranslateBlocks = require('../extensions/scratch3_translate'); const Scratch3VideoSensingBlocks = require('../extensions/scratch3_video_sensing'); const Scratch3SpeechBlocks = require('../extensions/scratch3_speech'); @@ -22,7 +22,7 @@ const builtinExtensions = { wedo2: Scratch3WeDo2Blocks, music: Scratch3MusicBlocks, microbit: Scratch3MicroBitBlocks, - speak: Scratch3SpeakBlocks, + text2speech: Scratch3Text2SpeechBlocks, translate: Scratch3TranslateBlocks, videoSensing: Scratch3VideoSensingBlocks, speech: Scratch3SpeechBlocks, From 6e03f2c6de23a60c75914204b546cb122ca69d8f Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 11 Jul 2018 16:03:10 -0400 Subject: [PATCH 1674/1971] Merge pull request #1321 from chrisgarrity/issue/1299-switch-language Issue/1299 switch language --- .../src/extension-support/extension-manager.js | 13 ++++++++----- packages/scratch-vm/src/virtual-machine.js | 9 ++++++--- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 66235b24a3..edbf7ceac7 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -144,18 +144,21 @@ class ExtensionManager { } /** - * regenerate blockinfo for any loaded extensions - */ + * Regenerate blockinfo for any loaded extensions + * @returns {Promise} resolved once all the extensions have been reinitialized + */ refreshBlocks () { - this._loadedExtensions.forEach(serviceName => { + const allPromises = Array.from(this._loadedExtensions.values()).map(serviceName => dispatch.call(serviceName, 'getInfo') .then(info => { + info = this._prepareExtensionInfo(serviceName, info); dispatch.call('runtime', '_refreshExtensionPrimitives', info); }) .catch(e => { log.error(`Failed to refresh buildtin extension primitives: ${JSON.stringify(e)}`); - }); - }); + }) + ); + return Promise.all(allPromises); } allocateWorker () { diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index e95258fb41..822f06ceda 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -907,12 +907,15 @@ class VirtualMachine extends EventEmitter { * set the current locale and builtin messages for the VM * @param {[type]} locale current locale * @param {[type]} messages builtin messages map for current locale + * @returns {Promise} Promise that resolves when all the blocks have been + * updated for a new locale (or empty if locale hasn't changed.) */ setLocale (locale, messages) { - if (locale !== formatMessage.setup().locale) { - formatMessage.setup({locale: locale, translations: {[locale]: messages}}); - this.extensionManager.refreshBlocks(); + if (locale === formatMessage.setup().locale) { + return Promise.resolve(); } + formatMessage.setup({locale: locale, translations: {[locale]: messages}}); + return this.extensionManager.refreshBlocks(); } /** From 6503750d9acf014d930c32bb27bbf270ee7af84f Mon Sep 17 00:00:00 2001 From: Kreg Hanning Date: Wed, 11 Jul 2018 16:13:49 -0400 Subject: [PATCH 1675/1971] Add micro:bit matrix field block (#1314) * Add micro:bit matrix field block * Remove debug logs * Remove rowcol menu * Fix lint errors --- packages/scratch-vm/src/engine/runtime.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 530db3b40a..956611ad81 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -58,6 +58,10 @@ const ArgumentTypeMap = (() => { map[ArgumentType.BOOLEAN] = { check: 'Boolean' }; + map[ArgumentType.MATRIX] = { + shadowType: 'matrix', + fieldType: 'MATRIX' + }; return map; })(); From bf873740b01f7692b139a18dabf6ff1e7090c123 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 12 Jul 2018 18:50:14 -0400 Subject: [PATCH 1676/1971] Merge pull request #1315 from fsih/loadCostume Don't depend on this until https://github.com/LLK/scratch-gui/pull/2575 is in Break out loadVector and loadBitmap from loadCostumeAsset, and handle bitmap resolution 1 assets in loadBitmap --- packages/scratch-vm/src/engine/runtime.js | 9 +++++++++ packages/scratch-vm/src/virtual-machine.js | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 956611ad81..8908fdaadd 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -999,6 +999,15 @@ class Runtime extends EventEmitter { this.v2SvgAdapter = svgAdapter; } + /** + * Set the bitmap adapter for the VM/runtime, which converts scratch 2 + * bitmaps to scratch 3 bitmaps. (Scratch 3 bitmaps are all bitmap resolution 2) + * @param {!function} bitmapAdapter The adapter to attach + */ + attachV2BitmapAdapter (bitmapAdapter) { + this.v2BitmapAdapter = bitmapAdapter; + } + /** * Attach the storage module * @param {!ScratchStorage} storage The storage module to attach diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 822f06ceda..d46e1e68da 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -895,6 +895,15 @@ class VirtualMachine extends EventEmitter { this.runtime.attachV2SVGAdapter(svgAdapter); } + /** + * Set the bitmap adapter for the VM/runtime, which converts scratch 2 + * bitmaps to scratch 3 bitmaps. (Scratch 3 bitmaps are all bitmap resolution 2) + * @param {!function} bitmapAdapter The adapter to attach + */ + attachV2BitmapAdapter (bitmapAdapter) { + this.runtime.attachV2BitmapAdapter(bitmapAdapter); + } + /** * Set the storage module for the VM/runtime * @param {!ScratchStorage} storage The storage module to attach From 08b04b39945b7fd4a6862a8dedb974ca354ea7cb Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 13 Jul 2018 07:53:05 -0400 Subject: [PATCH 1677/1971] Merge pull request #1330 from paulkaplan/glow-on-stack-click Always glow threads that come from stack clicks --- packages/scratch-vm/src/engine/runtime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 8908fdaadd..5e0a0cf5bd 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1490,7 +1490,7 @@ class Runtime extends EventEmitter { const target = thread.target; if (target === this._editingTarget) { const blockForThread = thread.blockGlowInFrame; - if (thread.requestScriptGlowInFrame) { + if (thread.requestScriptGlowInFrame || thread.stackClick) { let script = target.blocks.getTopLevelScript(blockForThread); if (!script) { // Attempt to find in flyout blocks. From cc08b178ce318e5c81dc2472431fd9d02aaddf63 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 13 Jul 2018 13:24:49 -0400 Subject: [PATCH 1678/1971] Merge pull request #1326 from kchadha/share-local-var-fix Fix variable conflicts that arise when sharing the love with the stage. --- packages/scratch-vm/src/engine/runtime.js | 23 ++++++++++++++++++++++ packages/scratch-vm/src/virtual-machine.js | 17 +++++++++++++--- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 5e0a0cf5bd..f5843b3ad5 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -13,6 +13,7 @@ const Thread = require('./thread'); const log = require('../util/log'); const maybeFormatMessage = require('../util/maybe-format-message'); const StageLayering = require('./stage-layering'); +const Variable = require('./variable'); // Virtual I/O devices. const Clock = require('../io/clock'); @@ -22,6 +23,9 @@ const Mouse = require('../io/mouse'); const MouseWheel = require('../io/mouseWheel'); const Video = require('../io/video'); +const StringUtil = require('../util/string-util'); +const uid = require('../util/uid'); + const defaultBlockPackages = { scratch3_control: require('../blocks/scratch3_control'), scratch3_event: require('../blocks/scratch3_event'), @@ -1793,6 +1797,25 @@ class Runtime extends EventEmitter { return varNames; } + /** + * Create a new global variable avoiding conflicts with other variable names. + * @param {string} variableName The desired variable name for the new global variable. + * This can be turned into a fresh name as necessary. + * @param {string} optVarId An optional ID to use for the variable. A new one will be generated + * if a falsey value for this parameter is provided. + * @param {string} optVarType The type of the variable to create. Defaults to Variable.SCALAR_TYPE. + * @return {Variable} The new variable that was created. + */ + createNewGlobalVariable (variableName, optVarId, optVarType) { + const varType = (typeof optVarType === 'string') ? optVarType : Variable.SCALAR_TYPE; + const allVariableNames = this.getAllVarNamesOfType(varType); + const newName = StringUtil.unusedName(variableName, allVariableNames); + const variable = new Variable(optVarId || uid(), newName, varType); + const stage = this.getTargetForStage(); + stage.variables[variable.id] = variable; + return variable; + } + /** * Tell the runtime to request a redraw. * Use after a clone/sprite has completed some visible operation on the stage. diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index d46e1e68da..d73d8ce7ee 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -998,11 +998,22 @@ class VirtualMachine extends EventEmitter { * workspace of the given target. * @param {!Array} blocks Blocks to add. * @param {!string} targetId Id of target to add blocks to. + * @param {?string} optFromTargetId Optional target id indicating that blocks are being + * shared from that target. This is needed for resolving any potential variable conflicts. */ - shareBlocksToTarget (blocks, targetId) { + shareBlocksToTarget (blocks, targetId, optFromTargetId) { + const copiedBlocks = JSON.parse(JSON.stringify(blocks)); const target = this.runtime.getTargetById(targetId); - for (let i = 0; i < blocks.length; i++) { - target.blocks.createBlock(blocks[i]); + + if (optFromTargetId) { + // If the blocks are being shared from another target, + // resolve any possible variable conflicts that may arise. + const fromTarget = this.runtime.getTargetById(optFromTargetId); + fromTarget.resolveVariableSharingConflictsWithTarget(copiedBlocks, target); + } + + for (let i = 0; i < copiedBlocks.length; i++) { + target.blocks.createBlock(copiedBlocks[i]); } target.blocks.updateTargetSpecificBlocks(target.isStage); } From d3bf759c3065b22efe8aff6c6a924b15d4137cd2 Mon Sep 17 00:00:00 2001 From: Evelyn Eastmond Date: Tue, 17 Jul 2018 16:03:06 -0400 Subject: [PATCH 1679/1971] EV3/Microbit critical fixes for code freeze (#1354) * Resolves - BLESession and BTSession should emit PERIPHERAL_SCAN_TIMEOUT #1348. * Resolves - BLESession should handle 'could not find service' error #1350. * Resolves - BTSession should handle 'no peripheral connected' error #1351. * Fixing a typo that caused device scan timeout bugs. * Resolves - Add casting and clamping throughout the EV3 extension #1352. * Fixing a linting error. * Further fixes for issue #1351. --- packages/scratch-vm/src/engine/runtime.js | 8 ++++++++ packages/scratch-vm/src/virtual-machine.js | 3 +++ 2 files changed, 11 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index f5843b3ad5..9a6e519455 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -428,6 +428,14 @@ class Runtime extends EventEmitter { return 'PERIPHERAL_ERROR'; } + /** + * Event name for reporting that a peripheral has not been discovered. + * @const {string} + */ + static get PERIPHERAL_SCAN_TIMEOUT () { + return 'PERIPHERAL_SCAN_TIMEOUT'; + } + /** * Event name for reporting that blocksInfo was updated. * @const {string} diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index d73d8ce7ee..992d84bf84 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -115,6 +115,9 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.PERIPHERAL_ERROR, () => this.emit(Runtime.PERIPHERAL_ERROR) ); + this.runtime.on(Runtime.PERIPHERAL_SCAN_TIMEOUT, () => + this.emit(Runtime.PERIPHERAL_SCAN_TIMEOUT) + ); this.extensionManager = new ExtensionManager(this.runtime); From 0ee8923dbf0e43cc4b4ebe6ea211e8768c21f1ba Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Wed, 18 Jul 2018 10:57:24 -0400 Subject: [PATCH 1680/1971] Merge pull request #1367 from chrisgarrity/issue/gui2530-blockwidth-locale Add function to return the current VM/blocks locale --- packages/scratch-vm/src/virtual-machine.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 992d84bf84..e390f9d88a 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -930,6 +930,14 @@ class VirtualMachine extends EventEmitter { return this.extensionManager.refreshBlocks(); } + /** + * get the current locale for the VM + * @returns {string} the current locale in the VM + */ + getLocale () { + return formatMessage.setup().locale; + } + /** * Handle a Blockly event for the current editing target. * @param {!Blockly.Event} e Any Blockly event. From 0b8d6681184fdb13db68d66ff68089a24597571e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 18 Jul 2018 15:18:27 -0400 Subject: [PATCH 1681/1971] Merge pull request #1377 from paulkaplan/update-extension-names Update the category info name when the extension is refreshed. --- packages/scratch-vm/src/engine/runtime.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 9a6e519455..69b801c02b 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -565,6 +565,7 @@ class Runtime extends EventEmitter { let extensionBlocks = []; for (const categoryInfo of this._blockInfo) { if (extensionInfo.id === categoryInfo.id) { + categoryInfo.name = maybeFormatMessage(extensionInfo.name); categoryInfo.blocks = []; categoryInfo.menus = []; this._fillExtensionCategory(categoryInfo, extensionInfo); From 11086ce107c1e5375ffec4aff6e703879796190a Mon Sep 17 00:00:00 2001 From: kchadha Date: Tue, 24 Jul 2018 13:53:29 -0400 Subject: [PATCH 1682/1971] Merge pull request #1392 from kchadha/sprite-layer-save-load Sprite Layer Ordering Save/Load --- packages/scratch-vm/src/sprites/rendered-target.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index eb996786be..93ee086615 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -879,6 +879,13 @@ class RenderedTarget extends Target { return false; } + getLayerOrder () { + if (this.renderer) { + return this.renderer.getDrawableOrder(this.drawableID); + } + return null; + } + /** * Move to the front layer. */ From e19fe6a931da4d07cb1d0572e63d62e182785ef8 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Jul 2018 15:13:15 -0400 Subject: [PATCH 1683/1971] Merge pull request #1387 from paulkaplan/userdata-io-device Add UserData ioDevice for setting and getting username from blocks --- packages/scratch-vm/src/engine/runtime.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 69b801c02b..403768e0c1 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -21,6 +21,7 @@ const DeviceManager = require('../io/deviceManager'); const Keyboard = require('../io/keyboard'); const Mouse = require('../io/mouse'); const MouseWheel = require('../io/mouseWheel'); +const UserData = require('../io/userData'); const Video = require('../io/video'); const StringUtil = require('../util/string-util'); @@ -259,6 +260,7 @@ class Runtime extends EventEmitter { keyboard: new Keyboard(this), mouse: new Mouse(this), mouseWheel: new MouseWheel(this), + userData: new UserData(), video: new Video(this) }; From 7b7e21eb91a487f28c594dcca65691dd885b3aad Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 31 Jul 2018 11:09:37 -0400 Subject: [PATCH 1684/1971] Merge pull request #1399 from paulkaplan/add-to-target Add an extra param for adding sounds and costumes to specific targets --- packages/scratch-vm/src/virtual-machine.js | 38 +++++++++++++++------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index e390f9d88a..a42bee1add 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -523,15 +523,22 @@ class VirtualMachine extends EventEmitter { * @property {number} rotationCenterX - the X component of the costume's origin. * @property {number} rotationCenterY - the Y component of the costume's origin. * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume. + * @param {string} optTargetId - the id of the target to add to, if not the editing target. * @returns {?Promise} - a promise that resolves when the costume has been added */ - addCostume (md5ext, costumeObject) { - return loadCostume(md5ext, costumeObject, this.runtime).then(() => { - this.editingTarget.addCostume(costumeObject); - this.editingTarget.setCostume( - this.editingTarget.getCostumes().length - 1 - ); - }); + addCostume (md5ext, costumeObject, optTargetId) { + const target = optTargetId ? this.runtime.getTargetById(optTargetId) : + this.editingTarget; + if (target) { + return loadCostume(md5ext, costumeObject, this.runtime).then(() => { + target.addCostume(costumeObject); + target.setCostume( + target.getCostumes().length - 1 + ); + }); + } + // If the target cannot be found by id, return a rejected promise + return new Promise.reject(); } /** @@ -585,13 +592,20 @@ class VirtualMachine extends EventEmitter { /** * Add a sound to the current editing target. * @param {!object} soundObject Object representing the costume. + * @param {string} optTargetId - the id of the target to add to, if not the editing target. * @returns {?Promise} - a promise that resolves when the sound has been decoded and added */ - addSound (soundObject) { - return loadSound(soundObject, this.runtime, this.editingTarget.sprite).then(() => { - this.editingTarget.addSound(soundObject); - this.emitTargetsUpdate(); - }); + addSound (soundObject, optTargetId) { + const target = optTargetId ? this.runtime.getTargetById(optTargetId) : + this.editingTarget; + if (target) { + return loadSound(soundObject, this.runtime, target.sprite).then(() => { + target.addSound(soundObject); + this.emitTargetsUpdate(); + }); + } + // If the target cannot be found by id, return a rejected promise + return new Promise.reject(); } /** From 2081a0a9374f366d1279f8cab6376432d109d29d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 2 Aug 2018 10:15:40 -0400 Subject: [PATCH 1685/1971] Merge pull request #1413 from paulkaplan/turbo-mode Add turbo mode events --- packages/scratch-vm/src/engine/runtime.js | 16 ++++++++++++++++ packages/scratch-vm/src/virtual-machine.js | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 403768e0c1..9a4ed3972f 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -322,6 +322,22 @@ class Runtime extends EventEmitter { return 'BLOCK_GLOW_OFF'; } + /** + * Event name for turning on turbo mode. + * @const {string} + */ + static get TURBO_MODE_ON () { + return 'TURBO_MODE_ON'; + } + + /** + * Event name for turning off turbo mode. + * @const {string} + */ + static get TURBO_MODE_OFF () { + return 'TURBO_MODE_OFF'; + } + /** * Event name when the project is started (threads may not necessarily be * running). diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index a42bee1add..b1b1869721 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -148,6 +148,11 @@ class VirtualMachine extends EventEmitter { */ setTurboMode (turboModeOn) { this.runtime.turboMode = !!turboModeOn; + if (this.runtime.turboMode) { + this.emit(Runtime.TURBO_MODE_ON); + } else { + this.emit(Runtime.TURBO_MODE_OFF); + } } /** From c66d039f0633f973b918b2189c72b02267e72702 Mon Sep 17 00:00:00 2001 From: kchadha Date: Wed, 8 Aug 2018 15:32:09 -0400 Subject: [PATCH 1686/1971] Merge pull request #1434 from kchadha/fix-variable-xml Fix variable xml --- packages/scratch-vm/src/sprites/rendered-target.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 93ee086615..daca65139f 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -1003,7 +1003,7 @@ class RenderedTarget extends Target { newClone.currentCostume = this.currentCostume; newClone.rotationStyle = this.rotationStyle; newClone.effects = JSON.parse(JSON.stringify(this.effects)); - newClone.variables = JSON.parse(JSON.stringify(this.variables)); + newClone.variables = this.duplicateVariables(); newClone.initDrawable(StageLayering.SPRITE_LAYER); newClone.updateAllDrawableProperties(); // Place behind the current target. @@ -1029,7 +1029,7 @@ class RenderedTarget extends Target { newTarget.currentCostume = this.currentCostume; newTarget.rotationStyle = this.rotationStyle; newTarget.effects = JSON.parse(JSON.stringify(this.effects)); - newTarget.variables = JSON.parse(JSON.stringify(this.variables)); + newTarget.variables = this.duplicateVariables(newTarget.blocks); newTarget.updateAllDrawableProperties(); newTarget.goBehindOther(this); return newTarget; From 960053356539e003446db535f0e7eae86ea67cba Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 8 Aug 2018 16:46:48 -0400 Subject: [PATCH 1687/1971] Merge pull request #1398 from paulkaplan/publish-docs Publish jsdoc to gh-pages --- packages/scratch-vm/src/engine/runtime.js | 2 +- packages/scratch-vm/src/virtual-machine.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 9a4ed3972f..5847f6ebc1 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -41,7 +41,7 @@ const defaultBlockPackages = { /** * Information used for converting Scratch argument types into scratch-blocks data. - * @type {object.}} + * @type {object.} */ const ArgumentTypeMap = (() => { const map = {}; diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index b1b1869721..1a1d7c0361 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -936,8 +936,8 @@ class VirtualMachine extends EventEmitter { /** * set the current locale and builtin messages for the VM - * @param {[type]} locale current locale - * @param {[type]} messages builtin messages map for current locale + * @param {!string} locale current locale + * @param {!object} messages builtin messages map for current locale * @returns {Promise} Promise that resolves when all the blocks have been * updated for a new locale (or empty if locale hasn't changed.) */ From 9f6086fe6662d2985843d0d947f2341c2afe9ae8 Mon Sep 17 00:00:00 2001 From: Mx Corey Frang Date: Thu, 9 Aug 2018 10:10:23 -0400 Subject: [PATCH 1688/1971] Use proposed drawableTouching interface from render (#1419) * use new drawableTouching interface from render * Fix mock renderer for new API call * with lint fix --- packages/scratch-vm/src/sprites/rendered-target.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index daca65139f..d21280831b 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -799,15 +799,7 @@ class RenderedTarget extends Target { */ isTouchingPoint (x, y) { if (this.renderer) { - // @todo: Update once pick is in Scratch coordinates. - // Limits test to this Drawable, so this will return true - // even if the clone is obscured by another Drawable. - const pickResult = this.runtime.renderer.pick( - x, y, - null, null, - [this.drawableID] - ); - return pickResult === this.drawableID; + return this.renderer.drawableTouching(this.drawableID, x, y); } return false; } From 70f09c77bede5c99e34f41593f2019524d4a413f Mon Sep 17 00:00:00 2001 From: kchadha Date: Thu, 9 Aug 2018 15:25:45 -0400 Subject: [PATCH 1689/1971] Merge pull request #1438 from kchadha/return-add-sprite-function-when-deleting Export the sprite and return an add function when deleting the sprite. --- packages/scratch-vm/src/virtual-machine.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 1a1d7c0361..79513103f2 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -836,6 +836,7 @@ class VirtualMachine extends EventEmitter { /** * Delete a sprite and all its clones. * @param {string} targetId ID of a target whose sprite to delete. + * @return {Function} Returns a function to restore the sprite that was deleted */ deleteSprite (targetId) { const target = this.runtime.getTargetById(targetId); @@ -849,6 +850,8 @@ class VirtualMachine extends EventEmitter { if (!sprite) { throw new Error('No sprite associated with this target.'); } + const spritePromise = this.exportSprite(targetId, 'uint8array'); + const restoreSprite = () => spritePromise.then(spriteBuffer => this.addSprite(spriteBuffer)); this.runtime.requestRemoveMonitorByTargetId(targetId); const currentEditingTarget = this.editingTarget; for (let i = 0; i < sprite.clones.length; i++) { @@ -867,9 +870,10 @@ class VirtualMachine extends EventEmitter { } // Sprite object should be deleted by GC. this.emitTargetsUpdate(); - } else { - throw new Error('No target with the provided id.'); + return restoreSprite; } + + throw new Error('No target with the provided id.'); } /** From fd64a8809c9e18f879808f688d639299aa9f3e2e Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 16 Aug 2018 17:28:43 -0400 Subject: [PATCH 1690/1971] Merge pull request #1486 from kchadha/fix-delete-sprite-local-vars Fix issue with deleting sprite with local variables --- packages/scratch-vm/src/virtual-machine.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 79513103f2..ed9c340dac 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -852,7 +852,14 @@ class VirtualMachine extends EventEmitter { } const spritePromise = this.exportSprite(targetId, 'uint8array'); const restoreSprite = () => spritePromise.then(spriteBuffer => this.addSprite(spriteBuffer)); + // Remove monitors from the runtime state and remove the + // target-specific monitored blocks (e.g. local variables) this.runtime.requestRemoveMonitorByTargetId(targetId); + const targetSpecificMonitorBlockIds = Object.keys(this.runtime.monitorBlocks._blocks) + .filter(key => this.runtime.monitorBlocks._blocks[key].targetId === targetId); + for (const blockId of targetSpecificMonitorBlockIds) { + this.runtime.monitorBlocks.deleteBlock(blockId); + } const currentEditingTarget = this.editingTarget; for (let i = 0; i < sprite.clones.length; i++) { const clone = sprite.clones[i]; From d87f7b3c84ae78ee9721131489ce8fc277a528e2 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 17 Aug 2018 10:26:15 -0400 Subject: [PATCH 1691/1971] Merge pull request #1453 from mzgoddard/round-setxy round x and y like Scratch 2 does --- packages/scratch-vm/src/sprites/rendered-target.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index d21280831b..443b9cdc6b 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -272,8 +272,8 @@ class RenderedTarget extends Target { const oldY = this.y; if (this.renderer) { const position = this.renderer.getFencedPositionOfDrawable(this.drawableID, [x, y]); - position[0] = this._roundCoord(position[0], 8); - position[1] = this._roundCoord(position[1], 8); + position[0] = Math.round(position[0]); + position[1] = Math.round(position[1]); this.x = position[0]; this.y = position[1]; @@ -285,8 +285,8 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } else { - this.x = this._roundCoord(x, 8); - this.y = this._roundCoord(y, 8); + this.x = Math.round(x); + this.y = Math.round(y); } this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY, force); this.runtime.requestTargetsUpdate(this); From dbb4c2aa7206d61f76aad72404079f2575d70b08 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 20 Aug 2018 10:36:46 -0400 Subject: [PATCH 1692/1971] Merge pull request #1497 from amazinigmech2418/revert-1453-round-setxy Revert "round x and y like Scratch 2 does" --- packages/scratch-vm/src/sprites/rendered-target.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 443b9cdc6b..d21280831b 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -272,8 +272,8 @@ class RenderedTarget extends Target { const oldY = this.y; if (this.renderer) { const position = this.renderer.getFencedPositionOfDrawable(this.drawableID, [x, y]); - position[0] = Math.round(position[0]); - position[1] = Math.round(position[1]); + position[0] = this._roundCoord(position[0], 8); + position[1] = this._roundCoord(position[1], 8); this.x = position[0]; this.y = position[1]; @@ -285,8 +285,8 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } else { - this.x = Math.round(x); - this.y = Math.round(y); + this.x = this._roundCoord(x, 8); + this.y = this._roundCoord(y, 8); } this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY, force); this.runtime.requestTargetsUpdate(this); From b152b40081bd57775c1a1b64bb33fb1e52302ecf Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Tue, 21 Aug 2018 15:28:39 -0400 Subject: [PATCH 1693/1971] Merge pull request #1503 from kchadha/restore-sound Restore sound --- packages/scratch-vm/src/sprites/rendered-target.js | 11 ++++++++--- packages/scratch-vm/src/virtual-machine.js | 13 ++++++++++++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index d21280831b..370dacd638 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -585,12 +585,17 @@ class RenderedTarget extends Target { /** * Delete a sound by index. * @param {number} index Sound index to be deleted + * @return {object} The deleted sound object, or null if no sound was deleted. */ deleteSound (index) { - this.sprite.sounds = this.sprite.sounds - .slice(0, index) - .concat(this.sprite.sounds.slice(index + 1)); + // Make sure the sound index is not out of bounds + if (index < 0 || index >= this.sprite.sounds.length) { + return null; + } + // Delete the sound at the given index + const deletedSound = this.sprite.sounds.splice(index, 1)[0]; this.runtime.requestTargetsUpdate(this); + return deletedSound; } /** diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index ed9c340dac..d23c85773e 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -675,9 +675,20 @@ class VirtualMachine extends EventEmitter { /** * Delete a sound from the current editing target. * @param {int} soundIndex - the index of the sound to be removed. + * @return {?Function} A function to restore the sound that was deleted, + * or null, if no sound was deleted. */ deleteSound (soundIndex) { - this.editingTarget.deleteSound(soundIndex); + const target = this.editingTarget; + const deletedSound = this.editingTarget.deleteSound(soundIndex); + if (deletedSound) { + const restoreFun = () => { + target.addSound(deletedSound); + this.emitTargetsUpdate(); + }; + return restoreFun; + } + return null; } /** From d779fdaaa532b969eaf304193ea84a2fb438d9c7 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Tue, 21 Aug 2018 19:53:22 -0400 Subject: [PATCH 1694/1971] Merge pull request #1511 from kchadha/restore-costume Restore costume --- packages/scratch-vm/src/sprites/rendered-target.js | 12 ++++++++++-- packages/scratch-vm/src/virtual-machine.js | 12 +++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 370dacd638..ebfe80a6df 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -534,12 +534,19 @@ class RenderedTarget extends Target { /** * Delete a costume by index. * @param {number} index Costume index to be deleted + * @return {?object} The costume that was deleted or null + * if the index was out of bounds of the costumes list or + * this target only has one costume. */ deleteCostume (index) { const originalCostumeCount = this.sprite.costumes.length; - if (originalCostumeCount === 1) return; + if (originalCostumeCount === 1) return null; - this.sprite.deleteCostumeAt(index); + if (index < 0 || index >= originalCostumeCount) { + return null; + } + + const deletedCostume = this.sprite.deleteCostumeAt(index); if (index === this.currentCostume && index === originalCostumeCount - 1) { this.setCostume(index - 1); @@ -550,6 +557,7 @@ class RenderedTarget extends Target { } this.runtime.requestTargetsUpdate(this); + return deletedCostume; } /** diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index d23c85773e..6187111215 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -589,9 +589,19 @@ class VirtualMachine extends EventEmitter { /** * Delete a costume from the current editing target. * @param {int} costumeIndex - the index of the costume to be removed. + * @return {?function} A function to restore the deleted costume, or null, + * if no costume was deleted. */ deleteCostume (costumeIndex) { - this.editingTarget.deleteCostume(costumeIndex); + const deletedCostume = this.editingTarget.deleteCostume(costumeIndex); + if (deletedCostume) { + const target = this.editingTarget; + return () => { + target.addCostume(deletedCostume); + this.emitTargetsUpdate(); + }; + } + return null; } /** From 7d43ef29eec73e772e5d7828f73db87a643a3cc4 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 22 Aug 2018 17:30:14 -0400 Subject: [PATCH 1695/1971] Merge pull request #1515 from kchadha/fix-stack-glow Fix stack glow --- packages/scratch-vm/src/engine/runtime.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 5847f6ebc1..c12809c64b 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -127,14 +127,14 @@ class Runtime extends EventEmitter { * These will execute on `_editingTarget.` * @type {!Blocks} */ - this.flyoutBlocks = new Blocks(); + this.flyoutBlocks = new Blocks(true /* force no glow */); /** * Storage container for monitor blocks. * These will execute on a target maybe * @type {!Blocks} */ - this.monitorBlocks = new Blocks(); + this.monitorBlocks = new Blocks(true /* force no glow */); /** * Currently known editing target for the VM. From 2d56079989189e4b01b4ced1ec87633e7edb28ff Mon Sep 17 00:00:00 2001 From: Evelyn Eastmond Date: Fri, 7 Sep 2018 17:01:23 -0400 Subject: [PATCH 1696/1971] Refactor for hardware extensions (#1555) * Beginning refactor: renaming 'device' to 'peripheral', shortening function names, reordering functions, etc. * Continuing refactoring: renaming some functions to be more verbose in the runtime, adding JSDocs, etc. * Changing 'device' to 'peripheral', etc. * Changing 'session' to 'socket'. * Fixing EV3 menus and menu arg validation, reordering functions, etc. * Add _send, add some references to documentation, etc. * Factored out _outputCommand and _inputCommand, renamed some enums, etc. * Fixed _outputCommand, some other minor cleanup. * Make _outputCommand and _inputCommand public. * Added TODO. * Renamed BLE UUID enums to be clearer. * Change WeDo2 in comments to WeDo 2.0, etc. * Changed some WeDo2Motor command names, cleaned up some JSDocs. * Beginning a major EV3 refactor. * WeDo2 formatting and comment changes. * Motor refactoring in EV3: motorTurnClockwise and motorTurnCounterClockwise initial working state. * Add reminders to possibly cast motor menu args in WeDo2. * Continue to move motor commands in EV3 to EV3Motor class, don't create new EV3Motor on every poll cycle, etc. * Factoring EV3 polling value commands, etc. * Fixing EV3 motor power, position and button pressed, and some commenting, etc. * Move EV3 motor position parsing to EV3Motor class, move directCommand and directCompoundCommand functions, some commenting, etc. * Changed WeDo2 motor label enum name. * Removed some EV3 motor functions that aren't needed, changed menu label enum names, moved some opcodes up to enums. * Fixing comments and documentation. * Some commenting. * Adding further documentation and references to PDFs, changed reply check to be safer, etc. * Some comment changes. * Moving some functions around in EV3 and WeDo2 to match. * Commenting, etc. * Some renaming of session, etc. * Fix stopAllMotors in EV3. * Fixing clearing of motors in EV3. * Some comment changes. * Change runtime .extensions/registerExtension to .peripheralExtensions/registerPeripheralExtension. * Renaming outputCommand/inputCommand to generateOutputCommand/generateInputCommand, etc. * Moved motorCommandIDs to EV3Motor class, renamed directCommand to generateCommand, etc. * Adding a reminder to rename something. * JSDoc fix in EV3Motor class. * Fixing microbit function name. * Adding a todo item. * Changing Ev3 menu formats to be backwards compatible, moving a BLE function up. * Fixing EV3 ports again, and button pressed returning a boolean. * Fixing menu value to be a string in EV3. --- packages/scratch-vm/src/engine/runtime.js | 55 ++++++++++++++++------ packages/scratch-vm/src/virtual-machine.js | 31 +++++++++--- 2 files changed, 65 insertions(+), 21 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index c12809c64b..f453936164 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -264,7 +264,10 @@ class Runtime extends EventEmitter { video: new Video(this) }; - this.extensionDevices = {}; + /** + * A list of extensions, used to manage hardware connection. + */ + this.peripheralExtensions = {}; /** * A runtime profiler that records timed events for later playback to @@ -928,32 +931,56 @@ class Runtime extends EventEmitter { (result, categoryInfo) => result.concat(categoryInfo.blocks.map(blockInfo => blockInfo.json)), []); } - registerExtensionDevice (extensionId, device) { - this.extensionDevices[extensionId] = device; + /** + * Register an extension that communications with a hardware peripheral by id, + * to have access to it and its peripheral functions in the future. + * @param {string} extensionId - the id of the extension. + * @param {object} extension - the extension to register. + */ + registerPeripheralExtension (extensionId, extension) { + this.peripheralExtensions[extensionId] = extension; } - startDeviceScan (extensionId) { - if (this.extensionDevices[extensionId]) { - this.extensionDevices[extensionId].startDeviceScan(); + /** + * Tell the specified extension to scan for a peripheral. + * @param {string} extensionId - the id of the extension. + */ + scanForPeripheral (extensionId) { + if (this.peripheralExtensions[extensionId]) { + this.peripheralExtensions[extensionId].scan(); } } - connectToPeripheral (extensionId, peripheralId) { - if (this.extensionDevices[extensionId]) { - this.extensionDevices[extensionId].connectDevice(peripheralId); + /** + * Connect to the extension's specified peripheral. + * @param {string} extensionId - the id of the extension. + * @param {number} peripheralId - the id of the peripheral. + */ + connectPeripheral (extensionId, peripheralId) { + if (this.peripheralExtensions[extensionId]) { + this.peripheralExtensions[extensionId].connect(peripheralId); } } - disconnectExtensionSession (extensionId) { - if (this.extensionDevices[extensionId]) { - this.extensionDevices[extensionId].disconnectSession(); + /** + * Disconnect from the extension's connected peripheral. + * @param {string} extensionId - the id of the extension. + */ + disconnectPeripheral (extensionId) { + if (this.peripheralExtensions[extensionId]) { + this.peripheralExtensions[extensionId].disconnect(); } } + /** + * Returns whether the extension has a currently connected peripheral. + * @param {string} extensionId - the id of the extension. + * @return {boolean} - whether the extension has a connected peripheral. + */ getPeripheralIsConnected (extensionId) { let isConnected = false; - if (this.extensionDevices[extensionId]) { - isConnected = this.extensionDevices[extensionId].getPeripheralIsConnected(); + if (this.peripheralExtensions[extensionId]) { + isConnected = this.peripheralExtensions[extensionId].isConnected(); } return isConnected; } diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 6187111215..90f2bd5133 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -105,7 +105,6 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.BLOCKSINFO_UPDATE, blocksInfo => { this.emit(Runtime.BLOCKSINFO_UPDATE, blocksInfo); }); - this.runtime.on(Runtime.PERIPHERAL_LIST_UPDATE, info => { this.emit(Runtime.PERIPHERAL_LIST_UPDATE, info); }); @@ -213,18 +212,36 @@ class VirtualMachine extends EventEmitter { this.runtime.ioDevices.video.setProvider(videoProvider); } - startDeviceScan (extensionId) { - this.runtime.startDeviceScan(extensionId); + /** + * Tell the specified extension to scan for a peripheral. + * @param {string} extensionId - the id of the extension. + */ + scanForPeripheral (extensionId) { + this.runtime.scanForPeripheral(extensionId); } - connectToPeripheral (extensionId, peripheralId) { - this.runtime.connectToPeripheral(extensionId, peripheralId); + /** + * Connect to the extension's specified peripheral. + * @param {string} extensionId - the id of the extension. + * @param {number} peripheralId - the id of the peripheral. + */ + connectPeripheral (extensionId, peripheralId) { + this.runtime.connectPeripheral(extensionId, peripheralId); } - disconnectExtensionSession (extensionId) { - this.runtime.disconnectExtensionSession(extensionId); + /** + * Disconnect from the extension's connected peripheral. + * @param {string} extensionId - the id of the extension. + */ + disconnectPeripheral (extensionId) { + this.runtime.disconnectPeripheral(extensionId); } + /** + * Returns whether the extension has a currently connected peripheral. + * @param {string} extensionId - the id of the extension. + * @return {boolean} - whether the extension has a connected peripheral. + */ getPeripheralIsConnected (extensionId) { return this.runtime.getPeripheralIsConnected(extensionId); } From 5679fd5d17161072b37d4da35a43a1910d0978a0 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 12 Sep 2018 18:10:12 -0400 Subject: [PATCH 1697/1971] Merge pull request #1565 from cwillisf/extension-monitors Extension monitors --- packages/scratch-vm/src/engine/runtime.js | 33 +++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index f453936164..daca604c75 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -805,8 +805,12 @@ class Runtime extends EventEmitter { } } - // Add icon to the bottom right of a loop block - if (blockInfo.blockType === BlockType.LOOP) { + if (blockInfo.blockType === BlockType.REPORTER) { + if (!blockInfo.disableMonitor && context.inputList.length === 0) { + blockJSON.checkboxInFlyout = true; + } + } else if (blockInfo.blockType === BlockType.LOOP) { + // Add icon to the bottom right of a loop block blockJSON[`lastDummyAlign${outLineNum}`] = 'RIGHT'; blockJSON[`message${outLineNum}`] = '%1'; blockJSON[`args${outLineNum}`] = [{ @@ -1851,6 +1855,31 @@ class Runtime extends EventEmitter { return varNames; } + /** + * Get the label or label function for an opcode + * @param {string} extendedOpcode - the opcode you want a label for + * @return {object} - object with label and category + * @property {string} category - the category for this opcode + * @property {Function} [labelFn] - function to generate the label for this opcode + * @property {string} [label] - the label for this opcode if `labelFn` is absent + */ + getLabelForOpcode (extendedOpcode) { + const [category, opcode] = StringUtil.splitFirst(extendedOpcode, '_'); + if (!(category && opcode)) return; + + const categoryInfo = this._blockInfo.find(ci => ci.id === category); + if (!categoryInfo) return; + + const block = categoryInfo.blocks.find(b => b.info.opcode === opcode); + if (!block) return; + + // TODO: should this use some other category? Also, we may want to format the label in a locale-specific way. + return { + category: 'data', + label: `${categoryInfo.name}: ${block.info.text}` + }; + } + /** * Create a new global variable avoiding conflicts with other variable names. * @param {string} variableName The desired variable name for the new global variable. From ee7452c99ee2ba8bca26ea3e3f202ba30b4d4509 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 13 Sep 2018 15:44:18 -0400 Subject: [PATCH 1698/1971] fix ids and names for text2speech and speech2text extensions (#1583) --- .../scratch-vm/src/extension-support/extension-manager.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index edbf7ceac7..ce6b1df2a3 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -14,7 +14,7 @@ const Scratch3MicroBitBlocks = require('../extensions/scratch3_microbit'); const Scratch3Text2SpeechBlocks = require('../extensions/scratch3_text2speech'); const Scratch3TranslateBlocks = require('../extensions/scratch3_translate'); const Scratch3VideoSensingBlocks = require('../extensions/scratch3_video_sensing'); -const Scratch3SpeechBlocks = require('../extensions/scratch3_speech'); +const Scratch3Speech2TextBlocks = require('../extensions/scratch3_speech2text'); const Scratch3Ev3Blocks = require('../extensions/scratch3_ev3'); const builtinExtensions = { @@ -25,7 +25,7 @@ const builtinExtensions = { text2speech: Scratch3Text2SpeechBlocks, translate: Scratch3TranslateBlocks, videoSensing: Scratch3VideoSensingBlocks, - speech: Scratch3SpeechBlocks, + speech2text: Scratch3Speech2TextBlocks, ev3: Scratch3Ev3Blocks }; From feb3e6460e8d05b0972816bfeb272bf457ea8709 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 18 Sep 2018 16:11:53 -0400 Subject: [PATCH 1699/1971] Merge pull request #1594 from rschamp/renderer-getter Add API for the attached renderer --- packages/scratch-vm/src/virtual-machine.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 90f2bd5133..6ad09deee4 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -958,6 +958,13 @@ class VirtualMachine extends EventEmitter { this.runtime.attachRenderer(renderer); } + /** + * @returns {RenderWebGL} The renderer attached to the vm + */ + get renderer () { + return this.runtime && this.runtime.renderer; + } + /** * Set the svg adapter for the VM/runtime, which converts scratch 2 svgs to scratch 3 svgs * @param {!SvgRenderer} svgAdapter The adapter to attach From 4d43ccd7e78716ca7a1b07bbb3008ac6cb050e1c Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 21 Sep 2018 09:24:59 -0400 Subject: [PATCH 1700/1971] Merge pull request #1604 from ericrosenbaum/feature/listening-indicator Add event to show and hide a mic indicator for Speech to Text --- packages/scratch-vm/src/engine/runtime.js | 16 ++++++++++++++++ packages/scratch-vm/src/virtual-machine.js | 3 +++ 2 files changed, 19 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index daca604c75..d634dbc836 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -457,6 +457,14 @@ class Runtime extends EventEmitter { return 'PERIPHERAL_SCAN_TIMEOUT'; } + /** + * Event name to indicate that the microphone is being used to stream audio. + * @const {string} + */ + static get MIC_LISTENING () { + return 'MIC_LISTENING'; + } + /** * Event name for reporting that blocksInfo was updated. * @const {string} @@ -989,6 +997,14 @@ class Runtime extends EventEmitter { return isConnected; } + /** + * Emit an event to indicate that the microphone is being used to stream audio. + * @param {boolean} listening - true if the microphone is currently listening. + */ + emitMicListening (listening) { + this.emit(Runtime.MIC_LISTENING, listening); + } + /** * Retrieve the function associated with the given opcode. * @param {!string} opcode The opcode to look up. diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 6ad09deee4..a118ab47cd 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -117,6 +117,9 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.PERIPHERAL_SCAN_TIMEOUT, () => this.emit(Runtime.PERIPHERAL_SCAN_TIMEOUT) ); + this.runtime.on(Runtime.MIC_LISTENING, listening => { + this.emit(Runtime.MIC_LISTENING, listening); + }); this.extensionManager = new ExtensionManager(this.runtime); From 05fe88d64c41568aef2c71048fee65f86ecaf1a6 Mon Sep 17 00:00:00 2001 From: Evelyn Eastmond Date: Tue, 25 Sep 2018 14:44:29 -0400 Subject: [PATCH 1701/1971] Merge pull request #1607 from evhan55/multiple-alerts Show customized alerts on hardware extension peripheral errors. --- packages/scratch-vm/src/virtual-machine.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index a118ab47cd..5be8a553ca 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -111,8 +111,8 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.PERIPHERAL_CONNECTED, () => this.emit(Runtime.PERIPHERAL_CONNECTED) ); - this.runtime.on(Runtime.PERIPHERAL_ERROR, () => - this.emit(Runtime.PERIPHERAL_ERROR) + this.runtime.on(Runtime.PERIPHERAL_ERROR, data => + this.emit(Runtime.PERIPHERAL_ERROR, data) ); this.runtime.on(Runtime.PERIPHERAL_SCAN_TIMEOUT, () => this.emit(Runtime.PERIPHERAL_SCAN_TIMEOUT) From b44fb11815e0ee8e7e4dddfba8ac4af904160844 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Fri, 5 Oct 2018 13:25:22 -0400 Subject: [PATCH 1702/1971] Merge pull request #1617 from mzgoddard/execution-order Sprite clone and initial draw order execution order --- packages/scratch-vm/src/engine/runtime.js | 70 ++++++++++++++++++- .../scratch-vm/src/sprites/rendered-target.js | 4 ++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index d634dbc836..ad295dfecc 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -112,6 +112,12 @@ class Runtime extends EventEmitter { */ this.targets = []; + /** + * Targets in reverse order of execution. Shares its order with drawables. + * @type {Array.} + */ + this.executableTargets = []; + /** * A list of threads that are currently running in the VM. * Threads are added when execution starts and pruned when execution ends. @@ -1245,7 +1251,7 @@ class Runtime extends EventEmitter { * @param {Target=} optTarget Optionally, a target to restrict to. */ allScriptsDo (f, optTarget) { - let targets = this.targets; + let targets = this.executableTargets; if (optTarget) { targets = [optTarget]; } @@ -1362,6 +1368,68 @@ class Runtime extends EventEmitter { // @todo clear out extensions? turboMode? etc. } + /** + * Add a target to the execution order. + * @param {Target} executableTarget target to add + */ + addExecutable (executableTarget) { + this.executableTargets.push(executableTarget); + } + + /** + * Move a target in the execution order by a relative amount. + * + * A positve number will make the target execute earlier. A negative number + * will make the target execute later in the order. + * + * @param {Target} executableTarget target to move + * @param {number} delta number of positions to move target by + * @returns {number} new position in execution order + */ + moveExecutable (executableTarget, delta) { + const oldIndex = this.executableTargets.indexOf(executableTarget); + this.executableTargets.splice(oldIndex, 1); + let newIndex = oldIndex + delta; + if (newIndex > this.executableTargets.length) { + newIndex = this.executableTargets.length; + } + if (newIndex <= 0) { + if (this.executableTargets.length > 0 && this.executableTargets[0].isStage) { + newIndex = 1; + } else { + newIndex = 0; + } + } + this.executableTargets.splice(newIndex, 0, executableTarget); + return newIndex; + } + + /** + * Set a target to execute at a specific position in the execution order. + * + * Infinity will set the target to execute first. 0 will set the target to + * execute last (before the stage). + * + * @param {Target} executableTarget target to move + * @param {number} newIndex position in execution order to place the target + * @returns {number} new position in the execution order + */ + setExecutablePosition (executableTarget, newIndex) { + const oldIndex = this.executableTargets.indexOf(executableTarget); + return this.moveExecutable(executableTarget, newIndex - oldIndex); + } + + /** + * Remove a target from the execution set. + * @param {Target} executableTarget target to remove + */ + removeExecutable (executableTarget) { + const oldIndex = this.executableTargets.indexOf(executableTarget); + if (oldIndex > -1) { + this.executableTargets.splice(oldIndex, 1); + } + } + /** * Dispose of a target. * @param {!Target} disposingTarget Target to dispose of. diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index ebfe80a6df..88588eae05 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -943,6 +943,9 @@ class RenderedTarget extends Target { other.drawableID, 0, StageLayering.SPRITE_LAYER, true); this.renderer.setDrawableOrder(this.drawableID, otherLayer, StageLayering.SPRITE_LAYER); } + + const executionPosition = this.runtime.executableTargets.indexOf(other); + this.runtime.setExecutablePosition(this, executionPosition); } /** @@ -1142,6 +1145,7 @@ class RenderedTarget extends Target { dispose () { this.runtime.changeCloneCounter(-1); this.runtime.stopForTarget(this); + this.runtime.removeExecutable(this); this.sprite.removeClone(this); if (this.renderer && this.drawableID !== null) { this.renderer.destroyDrawable(this.drawableID, this.isStage ? From 8b4104b91454323c306ff74c9d79f88555508f56 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Tue, 9 Oct 2018 23:00:33 -0400 Subject: [PATCH 1703/1971] Merge pull request #1619 from mzgoddard/execution-order-2 Execution order 2 --- packages/scratch-vm/src/sprites/rendered-target.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 88588eae05..a0d75223ac 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -900,6 +900,8 @@ class RenderedTarget extends Target { // of what layers are present this.renderer.setDrawableOrder(this.drawableID, Infinity, StageLayering.SPRITE_LAYER); } + + this.runtime.setExecutablePosition(this, Infinity); } /** @@ -911,6 +913,8 @@ class RenderedTarget extends Target { // of what layers are present this.renderer.setDrawableOrder(this.drawableID, -Infinity, StageLayering.SPRITE_LAYER, false); } + + this.runtime.setExecutablePosition(this, -Infinity); } /** @@ -921,6 +925,8 @@ class RenderedTarget extends Target { if (this.renderer) { this.renderer.setDrawableOrder(this.drawableID, nLayers, StageLayering.SPRITE_LAYER, true); } + + this.runtime.moveExecutable(this, nLayers); } /** @@ -931,6 +937,8 @@ class RenderedTarget extends Target { if (this.renderer) { this.renderer.setDrawableOrder(this.drawableID, -nLayers, StageLayering.SPRITE_LAYER, true); } + + this.runtime.moveExecutable(this, -nLayers); } /** From b820df9e9df8674e5ea773c0ff15c7a7b41d85e5 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 15 Oct 2018 13:16:49 -0400 Subject: [PATCH 1704/1971] Merge pull request #1631 from mzgoddard/stop-other-asks Stop other asks --- packages/scratch-vm/src/engine/runtime.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index ad295dfecc..56a87c8feb 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -383,6 +383,15 @@ class Runtime extends EventEmitter { return 'PROJECT_STOP_ALL'; } + /** + * Event name for target being stopped by a stop for target call. + * Used by blocks that need to stop individual targets. + * @const {string} + */ + static get STOP_FOR_TARGET () { + return 'STOP_FOR_TARGET'; + } + /** * Event name for visual value report. * @const {string} @@ -1450,6 +1459,9 @@ class Runtime extends EventEmitter { * @param {Thread=} optThreadException Optional thread to skip. */ stopForTarget (target, optThreadException) { + // Emit stop event to allow blocks to clean up any state. + this.emit(Runtime.STOP_FOR_TARGET, target, optThreadException); + // Stop any threads on the target. for (let i = 0; i < this.threads.length; i++) { if (this.threads[i] === optThreadException) { From e80fa3b8d51806cb353fcfe118186dbc3f7c124a Mon Sep 17 00:00:00 2001 From: Katie Broida Date: Wed, 17 Oct 2018 10:49:08 -0400 Subject: [PATCH 1705/1971] Confirm extension in use in sb2 serialization (#1643) Add a check when serializing sb2 projects to see whether an extension is actually in use in a block or a visible monitor. --- packages/scratch-vm/src/engine/runtime.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 56a87c8feb..35156b93c5 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1596,6 +1596,14 @@ class Runtime extends EventEmitter { return count; } + /** + * + * @return {OrderedMap} The current state of monitor blocks. + */ + getMonitorState () { + return this._monitorState; + } + /** * Queue monitor blocks to sequencer to be run. */ From ce269527e9c74b168626248e2d43c1e0f862e7f2 Mon Sep 17 00:00:00 2001 From: Evelyn Eastmond Date: Wed, 17 Oct 2018 15:48:07 -0400 Subject: [PATCH 1706/1971] Differentiate peripheral errors: request vs. disconnect (#1654) * Beginning to add differentiation for hardware disconnect alerts. * Set connected status after error is sent. --- packages/scratch-vm/src/engine/runtime.js | 14 +++++++++++--- packages/scratch-vm/src/virtual-machine.js | 7 +++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 35156b93c5..50e4c43e49 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -457,11 +457,19 @@ class Runtime extends EventEmitter { } /** - * Event name for reporting that a peripheral has encountered an error. + * Event name for reporting that a peripheral has encountered a request error. * @const {string} */ - static get PERIPHERAL_ERROR () { - return 'PERIPHERAL_ERROR'; + static get PERIPHERAL_REQUEST_ERROR () { + return 'PERIPHERAL_REQUEST_ERROR'; + } + + /** + * Event name for reporting that a peripheral has encountered a disconnect error. + * @const {string} + */ + static get PERIPHERAL_DISCONNECT_ERROR () { + return 'PERIPHERAL_DISCONNECT_ERROR'; } /** diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 5be8a553ca..43a5e34a92 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -111,8 +111,11 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.PERIPHERAL_CONNECTED, () => this.emit(Runtime.PERIPHERAL_CONNECTED) ); - this.runtime.on(Runtime.PERIPHERAL_ERROR, data => - this.emit(Runtime.PERIPHERAL_ERROR, data) + this.runtime.on(Runtime.PERIPHERAL_REQUEST_ERROR, () => + this.emit(Runtime.PERIPHERAL_REQUEST_ERROR) + ); + this.runtime.on(Runtime.PERIPHERAL_DISCONNECT_ERROR, data => + this.emit(Runtime.PERIPHERAL_DISCONNECT_ERROR, data) ); this.runtime.on(Runtime.PERIPHERAL_SCAN_TIMEOUT, () => this.emit(Runtime.PERIPHERAL_SCAN_TIMEOUT) From cc2aab05cd4ba08405cbf371a27d7bdc5b48a172 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 17 Oct 2018 17:34:12 -0400 Subject: [PATCH 1707/1971] Save and load the language setting for Text to Speech (#1670) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use scratch locales internally, with adapter for polly * Save and load text2pseech language * Only localize default input to “speak” if in a supported language --- packages/scratch-vm/src/sprites/rendered-target.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index a0d75223ac..3736588ca6 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -152,6 +152,14 @@ class RenderedTarget extends Target { * @type {string} */ this.videoState = RenderedTarget.VIDEO_STATE.ON; + + /** + * The language to use for speech synthesis, in the text2speech extension. + * It is initialized to null so that on extension load, we can check for + * this and try setting it using the editor locale. + * @type {string} + */ + this.textToSpeechLanguage = null; } /** @@ -1139,6 +1147,7 @@ class RenderedTarget extends Target { variables: this.variables, costumes: costumes, sounds: this.getSounds(), + textToSpeechLanguage: this.textToSpeechLanguage, tempo: this.tempo, volume: this.volume, videoTransparency: this.videoTransparency, From 941877097f4ece5586bb56f0d375e29a705cc992 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 18 Oct 2018 12:02:27 -0400 Subject: [PATCH 1708/1971] Merge pull request #1675 from LLK/revert-1643-load-video-state Revert "Confirm extension in use in sb2 serialization" --- packages/scratch-vm/src/engine/runtime.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 50e4c43e49..2e17500cd3 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1604,14 +1604,6 @@ class Runtime extends EventEmitter { return count; } - /** - * - * @return {OrderedMap} The current state of monitor blocks. - */ - getMonitorState () { - return this._monitorState; - } - /** * Queue monitor blocks to sequencer to be run. */ From f4f6d419e54421d60219c668f008b4860efb1b16 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Tue, 23 Oct 2018 15:11:08 -0400 Subject: [PATCH 1709/1971] Merge pull request #1632 from mzgoddard/stop-other-sounds Stop other sounds --- packages/scratch-vm/src/sprites/rendered-target.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 3736588ca6..0d47295169 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -1074,9 +1074,6 @@ class RenderedTarget extends Target { */ onStopAll () { this.clearEffects(); - if (this.sprite.soundBank) { - this.sprite.soundBank.stopAllSounds(); - } } /** From 7e678a9ed1866066e405e80443359fb3ac175a83 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 25 Oct 2018 10:15:47 +0100 Subject: [PATCH 1710/1971] Merge pull request #1691 from rschamp/storage-no-cache Store asset objects on costumes and sounds --- packages/scratch-vm/src/virtual-machine.js | 47 +++++++++++++--------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 43a5e34a92..96ab30411f 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -690,11 +690,14 @@ class VirtualMachine extends EventEmitter { // is updated as below. sound.format = ''; const storage = this.runtime.storage; - sound.assetId = storage.builtinHelper.cache( + sound.asset = storage.createAsset( storage.AssetType.Sound, storage.DataFormat.WAV, - soundEncoding + soundEncoding, + null, + true // generate md5 ); + sound.assetId = sound.asset.assetId; sound.dataFormat = storage.DataFormat.WAV; sound.md5 = `${sound.assetId}.${sound.dataFormat}`; } @@ -731,16 +734,16 @@ class VirtualMachine extends EventEmitter { * a dataURI if it's a PNG or JPG, or null if it couldn't be found or decoded. */ getCostume (costumeIndex) { - const id = this.editingTarget.getCostumes()[costumeIndex].assetId; - if (!id || !this.runtime || !this.runtime.storage) return null; - const format = this.runtime.storage.get(id).dataFormat; + const asset = this.editingTarget.getCostumes()[costumeIndex].asset; + if (!asset || !this.runtime || !this.runtime.storage) return null; + const format = asset.dataFormat; if (format === this.runtime.storage.DataFormat.SVG) { - return this.runtime.storage.get(id).decodeText(); + return asset.decodeText(); } else if (format === this.runtime.storage.DataFormat.PNG || format === this.runtime.storage.DataFormat.JPG) { - return this.runtime.storage.get(id).encodeDataURI(); + return asset.encodeDataURI(); } - log.error(`Unhandled format: ${this.runtime.storage.get(id).dataFormat}`); + log.error(`Unhandled format: ${asset.dataFormat}`); return null; } @@ -781,14 +784,17 @@ class VirtualMachine extends EventEmitter { const reader = new FileReader(); reader.addEventListener('loadend', () => { const storage = this.runtime.storage; - costume.assetId = storage.builtinHelper.cache( - storage.AssetType.ImageBitmap, - storage.DataFormat.PNG, - Buffer.from(reader.result) - ); costume.dataFormat = storage.DataFormat.PNG; costume.bitmapResolution = bitmapResolution; costume.size = [bitmap.width, bitmap.height]; + costume.asset = storage.createAsset( + storage.AssetType.ImageBitmap, + costume.dataFormat, + Buffer.from(reader.result), + null, // id + true // generate md5 + ); + costume.assetId = costume.asset.assetId; costume.md5 = `${costume.assetId}.${costume.dataFormat}`; this.emitTargetsUpdate(); }); @@ -812,16 +818,19 @@ class VirtualMachine extends EventEmitter { costume.size = this.runtime.renderer.getSkinSize(costume.skinId); } const storage = this.runtime.storage; - costume.assetId = storage.builtinHelper.cache( - storage.AssetType.ImageVector, - storage.DataFormat.SVG, - (new TextEncoder()).encode(svg) - ); // If we're in here, we've edited an svg in the vector editor, // so the dataFormat should be 'svg' costume.dataFormat = storage.DataFormat.SVG; - costume.md5 = `${costume.assetId}.${costume.dataFormat}`; costume.bitmapResolution = 1; + costume.asset = storage.createAsset( + storage.AssetType.ImageVector, + costume.dataFormat, + (new TextEncoder()).encode(svg), + null, + true // generate md5 + ); + costume.assetId = costume.asset.assetId; + costume.md5 = `${costume.assetId}.${costume.dataFormat}`; this.emitTargetsUpdate(); } From b6aae7cd18c845532087b031710ca3d5396d2a46 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 29 Oct 2018 11:39:41 -0400 Subject: [PATCH 1711/1971] Merge pull request #1517 from joker314/costume-compatibility Make "switch costume" and "switch backdrop" blocks compatible with 2.0 --- packages/scratch-vm/src/sprites/rendered-target.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 0d47295169..e4ec58d84d 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -471,6 +471,8 @@ class RenderedTarget extends Target { setCostume (index) { // Keep the costume index within possible values. index = Math.round(index); + if ([Infinity, -Infinity, NaN].includes(index)) index = 0; + this.currentCostume = MathUtil.wrapClamp( index, 0, this.sprite.costumes.length - 1 ); From 7d1e05e970d50702205c7ab2341b43da3b28b540 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Tue, 30 Oct 2018 11:57:20 -0400 Subject: [PATCH 1712/1971] Merge pull request #1703 from kchadha/cloud-io Cloud IO --- packages/scratch-vm/src/engine/runtime.js | 3 +++ packages/scratch-vm/src/virtual-machine.js | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 2e17500cd3..082eedaf8a 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -17,6 +17,7 @@ const Variable = require('./variable'); // Virtual I/O devices. const Clock = require('../io/clock'); +const Cloud = require('../io/cloud'); const DeviceManager = require('../io/deviceManager'); const Keyboard = require('../io/keyboard'); const Mouse = require('../io/mouse'); @@ -262,6 +263,7 @@ class Runtime extends EventEmitter { /** @type {Object.} */ this.ioDevices = { clock: new Clock(), + cloud: new Cloud(), deviceManager: new DeviceManager(), keyboard: new Keyboard(this), mouse: new Mouse(this), @@ -1383,6 +1385,7 @@ class Runtime extends EventEmitter { this.targets.map(this.disposeTarget, this); this._monitorState = OrderedMap({}); // @todo clear out extensions? turboMode? etc. + this.ioDevices.cloud.clear(); } /** diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 96ab30411f..b5f16c654b 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -218,6 +218,10 @@ class VirtualMachine extends EventEmitter { this.runtime.ioDevices.video.setProvider(videoProvider); } + setCloudProvider (cloudProvider) { + this.runtime.ioDevices.cloud.setProvider(cloudProvider); + } + /** * Tell the specified extension to scan for a peripheral. * @param {string} extensionId - the id of the extension. @@ -463,6 +467,7 @@ class VirtualMachine extends EventEmitter { this.emitTargetsUpdate(); this.emitWorkspaceUpdate(); this.runtime.setEditingTarget(this.editingTarget); + this.runtime.ioDevices.cloud.setStage(this.runtime.getTargetForStage()); }); } From 053daddc01143ab33de6300606366f427dd7adfa Mon Sep 17 00:00:00 2001 From: Katie Broida Date: Tue, 30 Oct 2018 15:26:22 -0400 Subject: [PATCH 1713/1971] Add runtime event for when the project is loaded (#1669) --- packages/scratch-vm/src/engine/runtime.js | 15 +++++++++++++++ packages/scratch-vm/src/virtual-machine.js | 1 + 2 files changed, 16 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 082eedaf8a..56e1a572ae 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -402,6 +402,14 @@ class Runtime extends EventEmitter { return 'VISUAL_REPORT'; } + /** + * Event name for project loaded report. + * @const {string} + */ + static get PROJECT_LOADED () { + return 'PROJECT_LOADED'; + } + /** * Event name for targets update report. * @const {string} @@ -1913,6 +1921,13 @@ class Runtime extends EventEmitter { return this._cloneCounter < Runtime.MAX_CLONES; } + /** + * Report that the project has loaded in the Virtual Machine. + */ + emitProjectLoaded () { + this.emit(Runtime.PROJECT_LOADED); + } + /** * Report that a new target has been created, possibly by cloning an existing target. * @param {Target} newTarget - the newly created target. diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index b5f16c654b..b7659dd31a 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -284,6 +284,7 @@ class VirtualMachine extends EventEmitter { return validationPromise .then(validatedInput => this.deserializeProject(validatedInput[0], validatedInput[1])) + .then(() => this.runtime.emitProjectLoaded()) .catch(error => { // Intentionally rejecting here (want errors to be handled by caller) if (error.hasOwnProperty('validationError')) { From f49a94f880d0e66f3856201b1bdab9a9ba2a4fdf Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 1 Nov 2018 09:46:37 -0400 Subject: [PATCH 1714/1971] Merge pull request #1711 from kchadha/import-cloud-var-info Import cloud var info and enforce cloud var limit --- packages/scratch-vm/src/engine/runtime.js | 88 +++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 56e1a572ae..95d07aa091 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -71,6 +71,54 @@ const ArgumentTypeMap = (() => { return map; })(); +/** + * A pair of functions used to manage the cloud variable limit, + * to be used when adding (or attempting to add) or removing a cloud variable. + * @typedef {object} CloudDataManager + * @property {function} canAddCloudVariable A function to call to check that + * a cloud variable can be added. + * @property {function} addCloudVariable A function to call to track a new + * cloud variable on the runtime. + * @property {function} removeCloudVariable A function to call when + * removing an existing cloud variable. + * @property {function} hasCloudVariables A function to call to check that + * the runtime has any cloud variables. + */ + +/** + * Creates and manages cloud variable limit in a project, + * and returns two functions to be used to add a new + * cloud variable (while checking that it can be added) + * and remove an existing cloud variable. + * These are to be called whenever attempting to create or delete + * a cloud variable. + * @return {CloudDataManager} The functions to be used when adding or removing a + * cloud variable. + */ +const cloudDataManager = () => { + const limit = 8; + let count = 0; + + const canAddCloudVariable = () => count < limit; + + const addCloudVariable = () => { + count++; + }; + + const removeCloudVariable = () => { + count--; + }; + + const hasCloudVariables = () => count > 0; + + return { + canAddCloudVariable, + addCloudVariable, + removeCloudVariable, + hasCloudVariables + }; +}; + /** * Predefined "Converted block info" for a separator between blocks in a block category * @type {ConvertedBlockInfo} @@ -283,6 +331,38 @@ class Runtime extends EventEmitter { * @type {Profiler} */ this.profiler = null; + + const newCloudDataManager = cloudDataManager(); + + /** + * Check wether the runtime has any cloud data. + * @type {function} + * @return {boolean} Whether or not the runtime currently has any + * cloud variables. + */ + this.hasCloudData = newCloudDataManager.hasCloudVariables; + + /** + * A function which checks whether a new cloud variable can be added + * to the runtime. + * @type {function} + * @return {boolean} Whether or not a new cloud variable can be added + * to the runtime. + */ + this.canAddCloudVariable = newCloudDataManager.canAddCloudVariable; + + /** + * A function that tracks a new cloud variable in the runtime, + * updating the cloud variable limit. + */ + this.addCloudVariable = newCloudDataManager.addCloudVariable; + + /** + * A function which updates the runtime's cloud variable limit + * when removing a cloud variable. + * @type {function} + */ + this.removeCloudVariable = newCloudDataManager.removeCloudVariable; } /** @@ -1394,6 +1474,14 @@ class Runtime extends EventEmitter { this._monitorState = OrderedMap({}); // @todo clear out extensions? turboMode? etc. this.ioDevices.cloud.clear(); + + // Reset runtime cloud data info + const newCloudDataManager = cloudDataManager(); + this.hasCloudData = newCloudDataManager.hasCloudVariables; + this.canAddCloudVariable = newCloudDataManager.canAddCloudVariable; + this.addCloudVariable = newCloudDataManager.addCloudVariable; + this.removeCloudVariable = newCloudDataManager.removeCloudVariable; + } /** From 5cc7e80a1a93d73df28bdc9290d0549af03189a5 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 1 Nov 2018 15:03:29 -0400 Subject: [PATCH 1715/1971] Merge pull request #1718 from paulkaplan/block-id-on-drag-end Include the original blockId of the top block being dragged. --- packages/scratch-vm/src/engine/runtime.js | 5 +++-- packages/scratch-vm/src/virtual-machine.js | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 95d07aa091..26315a952e 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1857,9 +1857,10 @@ class Runtime extends EventEmitter { /** * Emit event to indicate that the block drag has ended with the blocks outside the blocks workspace * @param {Array.} blocks The set of blocks dragged to the GUI + * @param {string} topBlockId The original id of the top block being dragged */ - emitBlockEndDrag (blocks) { - this.emit(Runtime.BLOCK_DRAG_END, blocks); + emitBlockEndDrag (blocks, topBlockId) { + this.emit(Runtime.BLOCK_DRAG_END, blocks, topBlockId); } /** diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index b7659dd31a..1aaae95783 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -96,8 +96,8 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.BLOCK_DRAG_UPDATE, areBlocksOverGui => { this.emit(Runtime.BLOCK_DRAG_UPDATE, areBlocksOverGui); }); - this.runtime.on(Runtime.BLOCK_DRAG_END, blocks => { - this.emit(Runtime.BLOCK_DRAG_END, blocks); + this.runtime.on(Runtime.BLOCK_DRAG_END, (blocks, topBlockId) => { + this.emit(Runtime.BLOCK_DRAG_END, blocks, topBlockId); }); this.runtime.on(Runtime.EXTENSION_ADDED, blocksInfo => { this.emit(Runtime.EXTENSION_ADDED, blocksInfo); From e5064d5a8da0ca5b892dc33761b05699bee03590 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 7 Nov 2018 11:50:15 -0500 Subject: [PATCH 1716/1971] Use the new note picker field in music and EV3 extensions (#1720) * Add note field type * Note picker in music extension plays notes * Use note picker in EV3 beep block --- packages/scratch-vm/src/engine/runtime.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 26315a952e..a86c17130e 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -68,6 +68,10 @@ const ArgumentTypeMap = (() => { shadowType: 'matrix', fieldType: 'MATRIX' }; + map[ArgumentType.NOTE] = { + shadowType: 'note', + fieldType: 'NOTE' + }; return map; })(); From cd58b6bcb0774c6da5179beae617ed99a0aa26ba Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 7 Nov 2018 17:15:11 -0500 Subject: [PATCH 1717/1971] Merge pull request #1683 from mzgoddard/sequencer-thread-cleanup Sequencer thread cleanup --- packages/scratch-vm/src/engine/runtime.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index a86c17130e..942c0e6125 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -241,6 +241,13 @@ class Runtime extends EventEmitter { */ this._nonMonitorThreadCount = 0; + /** + * All threads that finished running and were removed from this.threads + * by behaviour in Sequencer.stepThreads. + * @type {Array} + */ + this._lastStepDoneThreads = null; + /** * Currently known number of clones, used to enforce clone limit. * @type {number} @@ -1663,6 +1670,9 @@ class Runtime extends EventEmitter { this._emitProjectRunStatus( this.threads.length + doneThreads.length - this._getMonitorThreadCount([...this.threads, ...doneThreads])); + // Store threads that completed this iteration for testing and other + // internal purposes. + this._lastStepDoneThreads = doneThreads; if (this.renderer) { // @todo: Only render when this.redrawRequested or clones rendered. if (this.profiler !== null) { From dac7dc1720e813d1806043c6c5d17af6690ed004 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 8 Nov 2018 10:15:58 +0000 Subject: [PATCH 1718/1971] Merge pull request #1727 from paulkaplan/add-ext-on-share Add unloaded extensions on block sharing --- packages/scratch-vm/src/virtual-machine.js | 23 ++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 1aaae95783..764242d31f 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -1107,6 +1107,7 @@ class VirtualMachine extends EventEmitter { * @param {!string} targetId Id of target to add blocks to. * @param {?string} optFromTargetId Optional target id indicating that blocks are being * shared from that target. This is needed for resolving any potential variable conflicts. + * @return {!Promise} Promise that resolves when the extensions and blocks have been added. */ shareBlocksToTarget (blocks, targetId, optFromTargetId) { const copiedBlocks = JSON.parse(JSON.stringify(blocks)); @@ -1119,10 +1120,24 @@ class VirtualMachine extends EventEmitter { fromTarget.resolveVariableSharingConflictsWithTarget(copiedBlocks, target); } - for (let i = 0; i < copiedBlocks.length; i++) { - target.blocks.createBlock(copiedBlocks[i]); - } - target.blocks.updateTargetSpecificBlocks(target.isStage); + // Create a unique set of extensionIds that are not yet loaded + const extensionIDs = new Set(copiedBlocks + .map(b => sb3.getExtensionIdForOpcode(b.opcode)) + .filter(id => !!id) // Remove ids that do not exist + .filter(id => !this.extensionManager.isExtensionLoaded(id)) // and remove loaded extensions + ); + + // Create an array promises for extensions to load + const extensionPromises = Array.from(extensionIDs, + id => this.extensionManager.loadExtensionURL(id) + ); + + return Promise.all(extensionPromises).then(() => { + copiedBlocks.forEach(block => { + target.blocks.createBlock(block); + }); + target.blocks.updateTargetSpecificBlocks(target.isStage); + }); } /** From 807928503bee645c1f1506cf0adacb093846df69 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 14 Nov 2018 13:23:10 -0500 Subject: [PATCH 1719/1971] Merge pull request #1686 from kchadha/monitor-save-load Monitor save & load --- packages/scratch-vm/src/engine/runtime.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 942c0e6125..b635bbd1fe 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -659,6 +659,10 @@ class Runtime extends EventEmitter { } } + getMonitorState () { + return this._monitorState; + } + /** * Generate an extension-specific menu ID. * @param {string} menuName - the name of the menu. @@ -2098,9 +2102,9 @@ class Runtime extends EventEmitter { const block = categoryInfo.blocks.find(b => b.info.opcode === opcode); if (!block) return; - // TODO: should this use some other category? Also, we may want to format the label in a locale-specific way. + // TODO: we may want to format the label in a locale-specific way. return { - category: 'data', + category: 'extension', // This assumes that all extensions have the same monitor color. label: `${categoryInfo.name}: ${block.info.text}` }; } From f989b5a745b4dc31889f2658b4535398d986acab Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 14 Nov 2018 21:39:29 -0500 Subject: [PATCH 1720/1971] Merge pull request #1755 from kchadha/cloud-var-create Create cloud variables --- packages/scratch-vm/src/engine/runtime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index b635bbd1fe..e16ac5411a 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -322,7 +322,7 @@ class Runtime extends EventEmitter { /** @type {Object.} */ this.ioDevices = { clock: new Clock(), - cloud: new Cloud(), + cloud: new Cloud(this), deviceManager: new DeviceManager(), keyboard: new Keyboard(this), mouse: new Mouse(this), From 4462bd902c416dada4cbb77b8cf07c0bdbff29e7 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 15 Nov 2018 10:31:58 +0000 Subject: [PATCH 1721/1971] Merge pull request #1750 from rschamp/feature/sb3-json-assets Add method for collecting all targets' assets --- packages/scratch-vm/src/virtual-machine.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 764242d31f..6f15c43d1a 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -336,6 +336,17 @@ class VirtualMachine extends EventEmitter { }); } + /* + * @type {Array} Array of all costumes and sounds currently in the runtime + */ + get assets () { + return this.runtime.targets.reduce((acc, target) => ( + acc + .concat(target.sprite.sounds.map(sound => sound.asset)) + .concat(target.sprite.costumes.map(costume => costume.asset)) + ), []); + } + _addFileDescsToZip (fileDescs, zip) { for (let i = 0; i < fileDescs.length; i++) { const currFileDesc = fileDescs[i]; From 6ffb796ef345589baa5510b0392fdba8bc72a1cf Mon Sep 17 00:00:00 2001 From: Katie Broida Date: Thu, 15 Nov 2018 15:50:56 -0500 Subject: [PATCH 1722/1971] Handle coordinate precision the same as Scratch 2 (#1722) --- .../scratch-vm/src/sprites/rendered-target.js | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index e4ec58d84d..4c14b013e0 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -256,17 +256,6 @@ class RenderedTarget extends Target { }; } - /** - * Round a number to n digits - * @param {number} value The number to be rounded - * @param {number} places The number of decimal places to round to - * @return {number} The rounded number - */ - _roundCoord (value, places) { - const power = Math.pow(10, places); - return Math.round(value * power) / power; - } - /** * Set the X and Y coordinates. * @param {!number} x New X coordinate, in Scratch coordinates. @@ -280,8 +269,6 @@ class RenderedTarget extends Target { const oldY = this.y; if (this.renderer) { const position = this.renderer.getFencedPositionOfDrawable(this.drawableID, [x, y]); - position[0] = this._roundCoord(position[0], 8); - position[1] = this._roundCoord(position[1], 8); this.x = position[0]; this.y = position[1]; @@ -293,8 +280,8 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } else { - this.x = this._roundCoord(x, 8); - this.y = this._roundCoord(y, 8); + this.x = x; + this.y = y; } this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY, force); this.runtime.requestTargetsUpdate(this); From 99feb0925fe8ea92472ca1607386691b0021379b Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 27 Nov 2018 08:54:30 -0500 Subject: [PATCH 1723/1971] Merge pull request #1779 from rschamp/project-dirty-signal Add "project changed" event --- packages/scratch-vm/src/engine/runtime.js | 16 +++++++++++++++- packages/scratch-vm/src/virtual-machine.js | 15 ++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index e16ac5411a..fa9c366bdf 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -501,6 +501,14 @@ class Runtime extends EventEmitter { return 'PROJECT_LOADED'; } + /** + * Event name for report that a change was made that can be saved + * @const {string} + */ + static get PROJECT_CHANGED () { + return 'PROJECT_CHANGED'; + } + /** * Event name for targets update report. * @const {string} @@ -1737,7 +1745,6 @@ class Runtime extends EventEmitter { // Script glows must be cleared. this._scriptGlowsPreviousFrame = []; this._updateGlows(); - this.requestTargetsUpdate(editingTarget); } /** @@ -2035,6 +2042,13 @@ class Runtime extends EventEmitter { this.emit(Runtime.PROJECT_LOADED); } + /** + * Report that the project has changed in a way that would affect serialization + */ + emitProjectChanged () { + this.emit(Runtime.PROJECT_CHANGED); + } + /** * Report that a new target has been created, possibly by cloning an existing target. * @param {Target} newTarget - the newly created target. diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 6f15c43d1a..b829664a21 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -84,6 +84,9 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.PROJECT_RUN_STOP, () => { this.emit(Runtime.PROJECT_RUN_STOP); }); + this.runtime.on(Runtime.PROJECT_CHANGED, () => { + this.emit(Runtime.PROJECT_CHANGED); + }); this.runtime.on(Runtime.VISUAL_REPORT, visualReport => { this.emit(Runtime.VISUAL_REPORT, visualReport); }); @@ -476,7 +479,7 @@ class VirtualMachine extends EventEmitter { } // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); + this.emitTargetsUpdate(false /* Don't emit project change */); this.emitWorkspaceUpdate(); this.runtime.setEditingTarget(this.editingTarget); this.runtime.ioDevices.cloud.setStage(this.runtime.getTargetForStage()); @@ -1105,7 +1108,7 @@ class VirtualMachine extends EventEmitter { if (target) { this.editingTarget = target; // Emit appropriate UI updates. - this.emitTargetsUpdate(); + this.emitTargetsUpdate(false /* Don't emit project change */); this.emitWorkspaceUpdate(); this.runtime.setEditingTarget(target); } @@ -1199,6 +1202,7 @@ class VirtualMachine extends EventEmitter { if (this.editingTarget) { this.emitWorkspaceUpdate(); this.runtime.setEditingTarget(this.editingTarget); + this.emitTargetsUpdate(false /* Don't emit project change */); } } @@ -1206,8 +1210,12 @@ class VirtualMachine extends EventEmitter { * Emit metadata about available targets. * An editor UI could use this to display a list of targets and show * the currently editing one. + * @param {bool} triggerProjectChange If true, also emit a project changed event. + * Disabled selectively by updates that don't affect project serialization. + * Defaults to true. */ - emitTargetsUpdate () { + emitTargetsUpdate (triggerProjectChange) { + if (typeof triggerProjectChange === 'undefined') triggerProjectChange = true; this.emit('targetsUpdate', { // [[target id, human readable target name], ...]. targetList: this.runtime.targets @@ -1220,6 +1228,7 @@ class VirtualMachine extends EventEmitter { // Currently editing target id. editingTarget: this.editingTarget ? this.editingTarget.id : null }); + if (triggerProjectChange) this.runtime.emitProjectChanged(); } /** From cd8991d47b2b4df0ddbac168849e552c6ce869a1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 27 Nov 2018 16:38:27 -0500 Subject: [PATCH 1724/1971] Merge pull request #1783 from paulkaplan/emit-start-event Emit a RUNTIME_STARTED event to track if the vm has been started --- packages/scratch-vm/src/engine/runtime.js | 9 +++++++++ packages/scratch-vm/src/virtual-machine.js | 3 +++ 2 files changed, 12 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index fa9c366bdf..e756ec778d 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -605,6 +605,14 @@ class Runtime extends EventEmitter { return 'BLOCKSINFO_UPDATE'; } + /** + * Event name when the runtime tick loop has been started. + * @const {string} + */ + static get RUNTIME_STARTED () { + return 'RUNTIME_STARTED'; + } + /** * How rapidly we try to step threads by default, in ms. */ @@ -2172,6 +2180,7 @@ class Runtime extends EventEmitter { this._steppingInterval = setInterval(() => { this._step(); }, interval); + this.emit(Runtime.RUNTIME_STARTED); } /** diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index b829664a21..436d37d12e 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -126,6 +126,9 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.MIC_LISTENING, listening => { this.emit(Runtime.MIC_LISTENING, listening); }); + this.runtime.on(Runtime.RUNTIME_STARTED, () => { + this.emit(Runtime.RUNTIME_STARTED); + }); this.extensionManager = new ExtensionManager(this.runtime); From c580bacdb9a6327fb8f81e188a45b11d95648189 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Tue, 27 Nov 2018 19:19:41 -0800 Subject: [PATCH 1725/1971] Merge pull request #1784 from cwillisf/setLocale-always-refreshBlocks Make setLocale consistently refresh blocks --- packages/scratch-vm/src/virtual-machine.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 436d37d12e..29b0be2890 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -1036,10 +1036,9 @@ class VirtualMachine extends EventEmitter { * updated for a new locale (or empty if locale hasn't changed.) */ setLocale (locale, messages) { - if (locale === formatMessage.setup().locale) { - return Promise.resolve(); + if (locale !== formatMessage.setup().locale) { + formatMessage.setup({locale: locale, translations: {[locale]: messages}}); } - formatMessage.setup({locale: locale, translations: {[locale]: messages}}); return this.extensionManager.refreshBlocks(); } From 3c66e634a5c39ea30290dba2b55e4baed04897c3 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 28 Nov 2018 08:52:18 -0500 Subject: [PATCH 1726/1971] Merge pull request #1785 from paulkaplan/no-multi-starts Prevent the runtime from spawning new stepping intervals --- packages/scratch-vm/src/engine/runtime.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index e756ec778d..c1f9c90fc9 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1763,6 +1763,7 @@ class Runtime extends EventEmitter { this.compatibilityMode = compatibilityModeOn; if (this._steppingInterval) { clearInterval(this._steppingInterval); + this._steppingInterval = null; this.start(); } } @@ -2172,6 +2173,9 @@ class Runtime extends EventEmitter { * Set up timers to repeatedly step in a browser. */ start () { + // Do not start if we are already running + if (this._steppingInterval) return; + let interval = Runtime.THREAD_STEP_INTERVAL; if (this.compatibilityMode) { interval = Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY; From edb7f0819468304005a8e440d6e8eb1cb1b1e6fc Mon Sep 17 00:00:00 2001 From: picklesrus Date: Wed, 28 Nov 2018 15:12:27 -0500 Subject: [PATCH 1727/1971] Merge pull request #1786 from picklesrus/test-log VM changes for the sensing_of block. --- packages/scratch-vm/src/engine/runtime.js | 15 +++++++++++++++ packages/scratch-vm/src/virtual-machine.js | 3 +++ 2 files changed, 18 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index c1f9c90fc9..b53014c192 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -613,6 +613,14 @@ class Runtime extends EventEmitter { return 'RUNTIME_STARTED'; } + /** + * Event name for reporting that a block was updated and needs to be rerendered. + * @const {string} + */ + static get BLOCKS_NEED_UPDATE () { + return 'BLOCKS_NEED_UPDATE'; + } + /** * How rapidly we try to step threads by default, in ms. */ @@ -2169,6 +2177,13 @@ class Runtime extends EventEmitter { this._refreshTargets = true; } + /** + * Emit an event that indicate that the blocks on the workspace need updating. + */ + requestBlocksUpdate () { + this.emit(Runtime.BLOCKS_NEED_UPDATE); + } + /** * Set up timers to repeatedly step in a browser. */ diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 29b0be2890..757e351c80 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -108,6 +108,9 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.BLOCKSINFO_UPDATE, blocksInfo => { this.emit(Runtime.BLOCKSINFO_UPDATE, blocksInfo); }); + this.runtime.on(Runtime.BLOCKS_NEED_UPDATE, () => { + this.emitWorkspaceUpdate(); + }); this.runtime.on(Runtime.PERIPHERAL_LIST_UPDATE, info => { this.emit(Runtime.PERIPHERAL_LIST_UPDATE, info); }); From 42cfcfed4dbe8255130a79b2aaf9bc94401d130d Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 28 Nov 2018 17:23:29 -0500 Subject: [PATCH 1728/1971] Merge pull request #1778 from kchadha/emit-cloud-data-update Emit cloud data update --- packages/scratch-vm/src/engine/runtime.js | 59 +++++++++++++++++++--- packages/scratch-vm/src/virtual-machine.js | 3 ++ 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index b53014c192..4031ea49de 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -364,16 +364,20 @@ class Runtime extends EventEmitter { /** * A function that tracks a new cloud variable in the runtime, - * updating the cloud variable limit. + * updating the cloud variable limit. Calling this function will + * emit a cloud data update event if this is the first cloud variable + * being added. + * @type {function} */ - this.addCloudVariable = newCloudDataManager.addCloudVariable; + this.addCloudVariable = this._initializeAddCloudVariable(newCloudDataManager); /** * A function which updates the runtime's cloud variable limit - * when removing a cloud variable. + * when removing a cloud variable and emits a cloud update event + * if the last of the cloud variables is being removed. * @type {function} */ - this.removeCloudVariable = newCloudDataManager.removeCloudVariable; + this.removeCloudVariable = this._initializeRemoveCloudVariable(newCloudDataManager); } /** @@ -424,6 +428,15 @@ class Runtime extends EventEmitter { return 'BLOCK_GLOW_OFF'; } + /** + * Event name for a cloud data update + * to this project. + * @const {string} + */ + static get HAS_CLOUD_DATA_UPDATE () { + return 'HAS_CLOUD_DATA_UPDATE'; + } + /** * Event name for turning on turbo mode. * @const {string} @@ -646,6 +659,29 @@ class Runtime extends EventEmitter { // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- + // Helper function for initializing the addCloudVariable function + _initializeAddCloudVariable (newCloudDataManager) { + // The addCloudVariable function + return (() => { + const hadCloudVarsBefore = this.hasCloudData(); + newCloudDataManager.addCloudVariable(); + if (!hadCloudVarsBefore && this.hasCloudData()) { + this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, true); + } + }); + } + + // Helper function for initializing the removeCloudVariable function + _initializeRemoveCloudVariable (newCloudDataManager) { + return (() => { + const hadCloudVarsBefore = this.hasCloudData(); + newCloudDataManager.removeCloudVariable(); + if (hadCloudVarsBefore && !this.hasCloudData()) { + this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, false); + } + }); + } + /** * Register default block packages with this runtime. * @todo Prefix opcodes with package name. @@ -1512,15 +1548,24 @@ class Runtime extends EventEmitter { this.targets.map(this.disposeTarget, this); this._monitorState = OrderedMap({}); // @todo clear out extensions? turboMode? etc. + + // *********** Cloud ******************* + + // If the runtime currently has cloud data, + // emit a has cloud data update event resetting + // it to false + if (this.hasCloudData()) { + this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, false); + } + this.ioDevices.cloud.clear(); // Reset runtime cloud data info const newCloudDataManager = cloudDataManager(); this.hasCloudData = newCloudDataManager.hasCloudVariables; this.canAddCloudVariable = newCloudDataManager.canAddCloudVariable; - this.addCloudVariable = newCloudDataManager.addCloudVariable; - this.removeCloudVariable = newCloudDataManager.removeCloudVariable; - + this.addCloudVariable = this._initializeAddCloudVariable(newCloudDataManager); + this.removeCloudVariable = this._initializeRemoveCloudVariable(newCloudDataManager); } /** diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 757e351c80..2bc8eb0d4f 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -132,6 +132,9 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.RUNTIME_STARTED, () => { this.emit(Runtime.RUNTIME_STARTED); }); + this.runtime.on(Runtime.HAS_CLOUD_DATA_UPDATE, hasCloudData => { + this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, hasCloudData); + }); this.extensionManager = new ExtensionManager(this.runtime); From 9f9bdceda33005bfa0c747f74928dd24a468c8ea Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 29 Nov 2018 10:45:40 -0500 Subject: [PATCH 1729/1971] Makey Makey extension (#1782) * Initial working makey makey extension * Cleanup and localization * Add block icon * Localization and cleanup * Docs and cleanup * Update block icon * Cleanup * Fix key press args --- .../scratch-vm/src/extension-support/extension-manager.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index ce6b1df2a3..86a335989d 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -16,6 +16,7 @@ const Scratch3TranslateBlocks = require('../extensions/scratch3_translate'); const Scratch3VideoSensingBlocks = require('../extensions/scratch3_video_sensing'); const Scratch3Speech2TextBlocks = require('../extensions/scratch3_speech2text'); const Scratch3Ev3Blocks = require('../extensions/scratch3_ev3'); +const Scratch3MakeyMakeyBlocks = require('../extensions/scratch3_makeymakey'); const builtinExtensions = { pen: Scratch3PenBlocks, @@ -26,7 +27,8 @@ const builtinExtensions = { translate: Scratch3TranslateBlocks, videoSensing: Scratch3VideoSensingBlocks, speech2text: Scratch3Speech2TextBlocks, - ev3: Scratch3Ev3Blocks + ev3: Scratch3Ev3Blocks, + makeymakey: Scratch3MakeyMakeyBlocks }; /** From 3b55b4fb5c2c62bdb0dd1a954061cefff863fe87 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 3 Dec 2018 15:52:23 -0500 Subject: [PATCH 1730/1971] Remove vestigial device manager (#1793) --- packages/scratch-vm/src/engine/runtime.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 4031ea49de..31d59e731b 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -18,7 +18,6 @@ const Variable = require('./variable'); // Virtual I/O devices. const Clock = require('../io/clock'); const Cloud = require('../io/cloud'); -const DeviceManager = require('../io/deviceManager'); const Keyboard = require('../io/keyboard'); const Mouse = require('../io/mouse'); const MouseWheel = require('../io/mouseWheel'); @@ -323,7 +322,6 @@ class Runtime extends EventEmitter { this.ioDevices = { clock: new Clock(), cloud: new Cloud(this), - deviceManager: new DeviceManager(), keyboard: new Keyboard(this), mouse: new Mouse(this), mouseWheel: new MouseWheel(this), From 0d44f7fa542ce373bdd9a4f7672a167545575dec Mon Sep 17 00:00:00 2001 From: Katie Broida Date: Tue, 4 Dec 2018 10:35:42 -0500 Subject: [PATCH 1731/1971] Make the wait block's logic and behavior more consistent with Scratch 2 (#1759) --- packages/scratch-vm/src/engine/runtime.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 31d59e731b..ca7447ffa9 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -304,6 +304,9 @@ class Runtime extends EventEmitter { */ this.currentStepTime = null; + // Set an intial value for this.currentMSecs + this.updateCurrentMSecs(); + /** * Whether any primitive has requested a redraw. * Affects whether `Sequencer.stepThreads` will yield @@ -2262,6 +2265,15 @@ class Runtime extends EventEmitter { disableProfiling () { this.profiler = null; } + + /** + * Update a millisecond timestamp value that is saved on the Runtime. + * This value is helpful in certain instances for compatibility with Scratch 2, + * which sometimes uses a `currentMSecs` timestamp value in Interpreter.as + */ + updateCurrentMSecs () { + this.currentMSecs = Date.now(); + } } /** From 508df097cc058dfa41ddde1c810d53f208ec9d65 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 5 Dec 2018 08:28:50 -0500 Subject: [PATCH 1732/1971] Merge pull request #1809 from paulkaplan/serialize-infinity Serialize Infinity and NaN to 0 --- packages/scratch-vm/src/virtual-machine.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 2bc8eb0d4f..e25f757a14 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -381,7 +381,7 @@ class VirtualMachine extends EventEmitter { exportSprite (targetId, optZipType) { const soundDescs = serializeSounds(this.runtime, targetId); const costumeDescs = serializeCostumes(this.runtime, targetId); - const spriteJson = JSON.stringify(sb3.serialize(this.runtime, targetId)); + const spriteJson = StringUtil.stringify(sb3.serialize(this.runtime, targetId)); const zip = new JSZip(); zip.file('sprite.json', spriteJson); @@ -401,7 +401,7 @@ class VirtualMachine extends EventEmitter { * @return {string} Serialized state of the runtime. */ toJSON () { - return JSON.stringify(sb3.serialize(this.runtime)); + return StringUtil.stringify(sb3.serialize(this.runtime)); } // TODO do we still need this function? Keeping it here so as not to introduce From 52969452b4a4debcc3a8e9b7c237a5a27d233c68 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 5 Dec 2018 12:30:24 -0500 Subject: [PATCH 1733/1971] Merge pull request #1815 from paulkaplan/clear-pen-on-dispose Clear the pen layer when runtime dispose happens. --- packages/scratch-vm/src/engine/runtime.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index ca7447ffa9..98926c21e9 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -627,6 +627,14 @@ class Runtime extends EventEmitter { return 'RUNTIME_STARTED'; } + /** + * Event name when the runtime dispose has been called. + * @const {string} + */ + static get RUNTIME_DISPOSED () { + return 'RUNTIME_DISPOSED'; + } + /** * Event name for reporting that a block was updated and needs to be rerendered. * @const {string} @@ -1548,6 +1556,7 @@ class Runtime extends EventEmitter { this.stopAll(); this.targets.map(this.disposeTarget, this); this._monitorState = OrderedMap({}); + this.emit(Runtime.RUNTIME_DISPOSED); // @todo clear out extensions? turboMode? etc. // *********** Cloud ******************* From 4ca486300cee461bf63363d511789c88d64f313d Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 6 Dec 2018 10:56:07 -0500 Subject: [PATCH 1734/1971] Merge pull request #1823 from kchadha/slider-cloud-vars Request cloud variable update in top level setVariableValue API --- packages/scratch-vm/src/virtual-machine.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index e25f757a14..3ad5a79ac1 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -1410,6 +1410,11 @@ class VirtualMachine extends EventEmitter { const variable = target.lookupVariableById(variableId); if (variable) { variable.value = value; + + if (variable.isCloud) { + this.runtime.ioDevices.cloud.requestUpdateVariable(variable.name, variable.value); + } + return true; } } From e1f47683f989cf5b617b503985a5d839820315f6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 6 Dec 2018 15:38:05 -0500 Subject: [PATCH 1735/1971] Merge pull request #1825 from paulkaplan/emit-start-publicly Emit PROJECT_START event publicly on green flag click. --- packages/scratch-vm/src/virtual-machine.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 3ad5a79ac1..312dc5f972 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -78,6 +78,9 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.BLOCK_GLOW_OFF, glowData => { this.emit(Runtime.BLOCK_GLOW_OFF, glowData); }); + this.runtime.on(Runtime.PROJECT_START, () => { + this.emit(Runtime.PROJECT_START); + }); this.runtime.on(Runtime.PROJECT_RUN_START, () => { this.emit(Runtime.PROJECT_RUN_START); }); From 84f51abf9b3777b088271ba599c8240fcca6deee Mon Sep 17 00:00:00 2001 From: picklesrus Date: Fri, 7 Dec 2018 15:08:09 -0500 Subject: [PATCH 1736/1971] Merge pull request #1818 from picklesrus/monitor-vars-project-load MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clear out the blocks in dispose. Fixes #1758 where old monitored vari… --- packages/scratch-vm/src/engine/runtime.js | 6 ++++++ packages/scratch-vm/src/virtual-machine.js | 7 +------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 98926c21e9..071d8a4a79 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1549,11 +1549,17 @@ class Runtime extends EventEmitter { return newThreads; } + /** * Dispose all targets. Return to clean state. */ dispose () { this.stopAll(); + // Deleting each target's variable's monitors. + this.targets.forEach(target => { + if (target.isOriginal) target.deleteMonitors(); + }); + this.targets.map(this.disposeTarget, this); this._monitorState = OrderedMap({}); this.emit(Runtime.RUNTIME_DISPOSED); diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 312dc5f972..595b6b678c 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -939,12 +939,7 @@ class VirtualMachine extends EventEmitter { const restoreSprite = () => spritePromise.then(spriteBuffer => this.addSprite(spriteBuffer)); // Remove monitors from the runtime state and remove the // target-specific monitored blocks (e.g. local variables) - this.runtime.requestRemoveMonitorByTargetId(targetId); - const targetSpecificMonitorBlockIds = Object.keys(this.runtime.monitorBlocks._blocks) - .filter(key => this.runtime.monitorBlocks._blocks[key].targetId === targetId); - for (const blockId of targetSpecificMonitorBlockIds) { - this.runtime.monitorBlocks.deleteBlock(blockId); - } + target.deleteMonitors(); const currentEditingTarget = this.editingTarget; for (let i = 0; i < sprite.clones.length; i++) { const clone = sprite.clones[i]; From ca661caf901bdcd90a83a9768231fb828e8ee05f Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 12 Dec 2018 21:13:09 -0500 Subject: [PATCH 1737/1971] Merge pull request #1834 from kchadha/edge-hat-duplicate-sprite Fix issue where edge-activated hats only run on one sprite after duplicating the sprite --- packages/scratch-vm/src/engine/runtime.js | 27 +------------------ .../scratch-vm/src/sprites/rendered-target.js | 4 ++- 2 files changed, 4 insertions(+), 27 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 071d8a4a79..417ded7625 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -221,13 +221,6 @@ class Runtime extends EventEmitter { */ this._hats = {}; - /** - * Currently known values for edge-activated hats. - * Keys are block ID for the hat; values are the currently known values. - * @type {Object.} - */ - this._edgeActivatedHatValues = {}; - /** * A list of script block IDs that were glowing during the previous frame. * @type {!Array.} @@ -1230,24 +1223,6 @@ class Runtime extends EventEmitter { this._hats[opcode].edgeActivated; } - /** - * Update an edge-activated hat block value. - * @param {!string} blockId ID of hat to store value for. - * @param {*} newValue Value to store for edge-activated hat. - * @return {*} The old value for the edge-activated hat. - */ - updateEdgeActivatedValue (blockId, newValue) { - const oldValue = this._edgeActivatedHatValues[blockId]; - this._edgeActivatedHatValues[blockId] = newValue; - return oldValue; - } - - /** - * Clear all edge-activaed hat values. - */ - clearEdgeActivatedValues () { - this._edgeActivatedHatValues = {}; - } /** * Attach the audio engine @@ -1687,7 +1662,7 @@ class Runtime extends EventEmitter { this.stopAll(); this.emit(Runtime.PROJECT_START); this.ioDevices.clock.resetProjectTimer(); - this.clearEdgeActivatedValues(); + this.targets.forEach(target => target.clearEdgeActivatedValues()); // Inform all targets of the green flag. for (let i = 0; i < this.targets.length; i++) { this.targets[i].onGreenFlag(); diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 4c14b013e0..019513f22b 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -1,6 +1,7 @@ const log = require('../util/log'); const MathUtil = require('../util/math-util'); const StringUtil = require('../util/string-util'); +const Clone = require('../util/clone'); const Target = require('../engine/target'); const StageLayering = require('../engine/stage-layering'); @@ -1015,8 +1016,9 @@ class RenderedTarget extends Target { newClone.size = this.size; newClone.currentCostume = this.currentCostume; newClone.rotationStyle = this.rotationStyle; - newClone.effects = JSON.parse(JSON.stringify(this.effects)); + newClone.effects = Clone.simple(this.effects); newClone.variables = this.duplicateVariables(); + newClone._edgeActivatedHatValues = Clone.simple(this._edgeActivatedHatValues); newClone.initDrawable(StageLayering.SPRITE_LAYER); newClone.updateAllDrawableProperties(); // Place behind the current target. From c98ac6dac2444530e6226e5f8b02c076e129b7ef Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 18 Dec 2018 12:12:18 -0500 Subject: [PATCH 1738/1971] Merge pull request #1849 from fsih/addCostumeFromLibrary Add addCostumeFromLibrary --- packages/scratch-vm/src/virtual-machine.js | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 595b6b678c..e065d60ccc 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -584,13 +584,14 @@ class VirtualMachine extends EventEmitter { * @property {number} rotationCenterY - the Y component of the costume's origin. * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume. * @param {string} optTargetId - the id of the target to add to, if not the editing target. + * @param {string} optVersion - if this is 2, load costume as sb2, otherwise load costume as sb3. * @returns {?Promise} - a promise that resolves when the costume has been added */ - addCostume (md5ext, costumeObject, optTargetId) { + addCostume (md5ext, costumeObject, optTargetId, optVersion) { const target = optTargetId ? this.runtime.getTargetById(optTargetId) : this.editingTarget; if (target) { - return loadCostume(md5ext, costumeObject, this.runtime).then(() => { + return loadCostume(md5ext, costumeObject, this.runtime, optVersion).then(() => { target.addCostume(costumeObject); target.setCostume( target.getCostumes().length - 1 @@ -598,7 +599,22 @@ class VirtualMachine extends EventEmitter { }); } // If the target cannot be found by id, return a rejected promise - return new Promise.reject(); + return Promise.reject(); + } + + /** + * Add a costume loaded from the library to the current editing target. + * @param {string} md5ext - the MD5 and extension of the costume to be loaded. + * @param {!object} costumeObject Object representing the costume. + * @property {int} skinId - the ID of the costume's render skin, once installed. + * @property {number} rotationCenterX - the X component of the costume's origin. + * @property {number} rotationCenterY - the Y component of the costume's origin. + * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume. + * @returns {?Promise} - a promise that resolves when the costume has been added + */ + addCostumeFromLibrary (md5ext, costumeObject) { + if (!this.editingTarget) return Promise.reject(); + return this.addCostume(md5ext, costumeObject, this.editingTarget.id, 2 /* optVersion */); } /** From 68c8dce4b6ebb689ea542b8d82840fd6a5bcfeaf Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Thu, 20 Dec 2018 14:42:35 -0500 Subject: [PATCH 1739/1971] Merge pull request #1852 from mzgoddard/sb1-convert depend on scratch-sb1-converter and convert sb1 files to sb2 --- packages/scratch-vm/src/virtual-machine.js | 23 +++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index e065d60ccc..c19098ec83 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -8,6 +8,7 @@ const ExtensionManager = require('./extension-support/extension-manager'); const log = require('./util/log'); const MathUtil = require('./util/math-util'); const Runtime = require('./engine/runtime'); +const {SB1File, ValidationError} = require('scratch-sb1-converter'); const sb2 = require('./serialization/sb2'); const sb3 = require('./serialization/sb3'); const StringUtil = require('./util/string-util'); @@ -295,7 +296,27 @@ class VirtualMachine extends EventEmitter { if (error) return reject(error); resolve(res); }); - }); + }) + .catch(error => { + try { + const sb1 = new SB1File(input); + const json = sb1.json; + json.projectVersion = 2; + return Promise.resolve([json, sb1.zip]); + } catch (sb1Error) { + if (sb1Error instanceof ValidationError) { + // The input does not validate as a Scratch 1 file. + } else { + // The project appears to be a Scratch 1 file but it + // could not be successfully translated into a Scratch 2 + // project. + return Promise.reject(sb1Error); + } + } + // Throw original error since the input does not appear to be + // an SB1File. + return Promise.reject(error); + }); return validationPromise .then(validatedInput => this.deserializeProject(validatedInput[0], validatedInput[1])) From e0c848f0bde3c92e44b52cd79fffbbbf3a7e43ee Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 7 Jan 2019 13:28:47 -0500 Subject: [PATCH 1740/1971] Merge pull request #1897 from kchadha/fix-cloud-var-limit Fix cloud variable limit --- packages/scratch-vm/src/engine/runtime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 417ded7625..7787618452 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -99,7 +99,7 @@ const ArgumentTypeMap = (() => { * cloud variable. */ const cloudDataManager = () => { - const limit = 8; + const limit = 10; let count = 0; const canAddCloudVariable = () => count < limit; From bac76f7718265e4884c07d7c7cfb74abd7569367 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 10 Jan 2019 11:04:33 -0500 Subject: [PATCH 1741/1971] Merge pull request #1905 from bocoup/update-blocks Add Vernier's GDX-FOR extension --- .../scratch-vm/src/extension-support/extension-manager.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 86a335989d..50f11dcdbf 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -17,6 +17,7 @@ const Scratch3VideoSensingBlocks = require('../extensions/scratch3_video_sensing const Scratch3Speech2TextBlocks = require('../extensions/scratch3_speech2text'); const Scratch3Ev3Blocks = require('../extensions/scratch3_ev3'); const Scratch3MakeyMakeyBlocks = require('../extensions/scratch3_makeymakey'); +const Scratch3GdxForBlocks = require('../extensions/scratch3_gdx_for'); const builtinExtensions = { pen: Scratch3PenBlocks, @@ -28,7 +29,8 @@ const builtinExtensions = { videoSensing: Scratch3VideoSensingBlocks, speech2text: Scratch3Speech2TextBlocks, ev3: Scratch3Ev3Blocks, - makeymakey: Scratch3MakeyMakeyBlocks + makeymakey: Scratch3MakeyMakeyBlocks, + gdxfor: Scratch3GdxForBlocks }; /** From 1d7b2453f02a9f9f4eb4d69df4f8dacb10581920 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 14 Jan 2019 14:57:29 -0500 Subject: [PATCH 1742/1971] Merge pull request #1912 from kchadha/executable-targets-fix Populate executable targets when runtime.targets is populated --- packages/scratch-vm/src/engine/runtime.js | 13 ++++++++----- packages/scratch-vm/src/sprites/rendered-target.js | 3 --- packages/scratch-vm/src/virtual-machine.js | 12 ++++++++++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 7787618452..f9a0178dfa 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1560,11 +1560,14 @@ class Runtime extends EventEmitter { } /** - * Add a target to the execution order. - * @param {Target} executableTarget target to add - */ - addExecutable (executableTarget) { - this.executableTargets.push(executableTarget); + * Add a target to the runtime. This tracks the sprite pane + * ordering of the target. The target still needs to be put + * into the correct execution order after calling this function. + * @param {Target} target target to add + */ + addTarget (target) { + this.targets.push(target); + this.executableTargets.push(target); } /** diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 019513f22b..78243a2697 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -1021,8 +1021,6 @@ class RenderedTarget extends Target { newClone._edgeActivatedHatValues = Clone.simple(this._edgeActivatedHatValues); newClone.initDrawable(StageLayering.SPRITE_LAYER); newClone.updateAllDrawableProperties(); - // Place behind the current target. - newClone.goBehindOther(this); return newClone; } @@ -1046,7 +1044,6 @@ class RenderedTarget extends Target { newTarget.effects = JSON.parse(JSON.stringify(this.effects)); newTarget.variables = this.duplicateVariables(newTarget.blocks); newTarget.updateAllDrawableProperties(); - newTarget.goBehindOther(this); return newTarget; }); } diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index c19098ec83..599d23405e 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -495,11 +495,18 @@ class VirtualMachine extends EventEmitter { return Promise.all(extensionPromises).then(() => { targets.forEach(target => { - this.runtime.targets.push(target); + this.runtime.addTarget(target); (/** @type RenderedTarget */ target).updateAllDrawableProperties(); // Ensure unique sprite name if (target.isSprite()) this.renameSprite(target.id, target.getName()); }); + // Sort the executable targets by layerOrder. + // Remove layerOrder property after use. + this.runtime.executableTargets.sort((a, b) => a.layerOrder - b.layerOrder); + targets.forEach(target => { + delete target.layerOrder; + }); + // Select the first target for editing, e.g., the first sprite. if (wholeProject && (targets.length > 1)) { this.editingTarget = targets[1]; @@ -1016,7 +1023,8 @@ class VirtualMachine extends EventEmitter { throw new Error('No sprite associated with this target.'); } return target.duplicate().then(newTarget => { - this.runtime.targets.push(newTarget); + this.runtime.addTarget(newTarget); + newTarget.goBehindOther(target); this.setEditingTarget(newTarget.id); }); } From 59037945563d2d8425a9b95cf857d44a5446725b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 15 Jan 2019 15:29:15 -0500 Subject: [PATCH 1743/1971] Merge pull request #1925 from ericrosenbaum/bugfix/dont-load-vernier-yet Do not load Vernier extension yet --- .../scratch-vm/src/extension-support/extension-manager.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 50f11dcdbf..bdebb2acee 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -17,7 +17,9 @@ const Scratch3VideoSensingBlocks = require('../extensions/scratch3_video_sensing const Scratch3Speech2TextBlocks = require('../extensions/scratch3_speech2text'); const Scratch3Ev3Blocks = require('../extensions/scratch3_ev3'); const Scratch3MakeyMakeyBlocks = require('../extensions/scratch3_makeymakey'); -const Scratch3GdxForBlocks = require('../extensions/scratch3_gdx_for'); +// todo: only load this extension once we have a compatible way to load its +// Vernier module dependency. +// const Scratch3GdxForBlocks = require('../extensions/scratch3_gdx_for'); const builtinExtensions = { pen: Scratch3PenBlocks, @@ -29,8 +31,8 @@ const builtinExtensions = { videoSensing: Scratch3VideoSensingBlocks, speech2text: Scratch3Speech2TextBlocks, ev3: Scratch3Ev3Blocks, - makeymakey: Scratch3MakeyMakeyBlocks, - gdxfor: Scratch3GdxForBlocks + makeymakey: Scratch3MakeyMakeyBlocks + // gdxfor: Scratch3GdxForBlocks }; /** From 2d29099c692b0d8706ba66090dce68cb4e1ca38e Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 18 Jan 2019 16:58:45 -0500 Subject: [PATCH 1744/1971] Merge pull request #1763 from evhan55/extensions/disconnect-errors Various fixes to extension disconnect errors --- packages/scratch-vm/src/engine/runtime.js | 21 ++++++++++++++++++--- packages/scratch-vm/src/virtual-machine.js | 7 +++++-- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index f9a0178dfa..89beb4ace3 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -558,6 +558,8 @@ class Runtime extends EventEmitter { /** * Event name for updating the available set of peripheral devices. + * This causes the peripheral connection modal to update a list of + * available peripherals. * @const {string} */ static get PERIPHERAL_LIST_UPDATE () { @@ -566,14 +568,25 @@ class Runtime extends EventEmitter { /** * Event name for reporting that a peripheral has connected. + * This causes the status button in the blocks menu to indicate 'connected'. * @const {string} */ static get PERIPHERAL_CONNECTED () { return 'PERIPHERAL_CONNECTED'; } + /** + * Event name for reporting that a peripheral has been intentionally disconnected. + * This causes the status button in the blocks menu to indicate 'disconnected'. + * @const {string} + */ + static get PERIPHERAL_DISCONNECTED () { + return 'PERIPHERAL_DISCONNECTED'; + } + /** * Event name for reporting that a peripheral has encountered a request error. + * This causes the peripheral connection modal to switch to an error state. * @const {string} */ static get PERIPHERAL_REQUEST_ERROR () { @@ -581,15 +594,17 @@ class Runtime extends EventEmitter { } /** - * Event name for reporting that a peripheral has encountered a disconnect error. + * Event name for reporting that a peripheral connection has been lost. + * This causes a 'peripheral connection lost' error alert to display. * @const {string} */ - static get PERIPHERAL_DISCONNECT_ERROR () { - return 'PERIPHERAL_DISCONNECT_ERROR'; + static get PERIPHERAL_CONNECTION_LOST_ERROR () { + return 'PERIPHERAL_CONNECTION_LOST_ERROR'; } /** * Event name for reporting that a peripheral has not been discovered. + * This causes the peripheral connection modal to show a timeout state. * @const {string} */ static get PERIPHERAL_SCAN_TIMEOUT () { diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 599d23405e..a40d135e53 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -124,8 +124,11 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.PERIPHERAL_REQUEST_ERROR, () => this.emit(Runtime.PERIPHERAL_REQUEST_ERROR) ); - this.runtime.on(Runtime.PERIPHERAL_DISCONNECT_ERROR, data => - this.emit(Runtime.PERIPHERAL_DISCONNECT_ERROR, data) + this.runtime.on(Runtime.PERIPHERAL_DISCONNECTED, () => + this.emit(Runtime.PERIPHERAL_DISCONNECTED) + ); + this.runtime.on(Runtime.PERIPHERAL_CONNECTION_LOST_ERROR, data => + this.emit(Runtime.PERIPHERAL_CONNECTION_LOST_ERROR, data) ); this.runtime.on(Runtime.PERIPHERAL_SCAN_TIMEOUT, () => this.emit(Runtime.PERIPHERAL_SCAN_TIMEOUT) From 51cb80343cb9848c362a9c30db4671023f1e322a Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 30 Jan 2019 10:57:45 -0500 Subject: [PATCH 1745/1971] Merge pull request #1941 from kchadha/add-runtime-to-blocks Add reference for `runtime` to blocks container --- packages/scratch-vm/src/engine/runtime.js | 4 ++-- packages/scratch-vm/src/virtual-machine.js | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 89beb4ace3..e946670155 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -185,14 +185,14 @@ class Runtime extends EventEmitter { * These will execute on `_editingTarget.` * @type {!Blocks} */ - this.flyoutBlocks = new Blocks(true /* force no glow */); + this.flyoutBlocks = new Blocks(this, true /* force no glow */); /** * Storage container for monitor blocks. * These will execute on a target maybe * @type {!Blocks} */ - this.monitorBlocks = new Blocks(true /* force no glow */); + this.monitorBlocks = new Blocks(this, true /* force no glow */); /** * Currently known editing target for the VM. diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index a40d135e53..38eb79e9f3 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -213,7 +213,7 @@ class VirtualMachine extends EventEmitter { const threadData = this.runtime.threads.filter(thread => thread.target === instance.editingTarget); // Remove the target key, since it's a circular reference. const filteredThreadData = JSON.stringify(threadData, (key, value) => { - if (key === 'target') return; + if (key === 'target' || key === 'blockContainer') return; return value; }, 2); this.emit('playgroundData', { @@ -1108,7 +1108,7 @@ class VirtualMachine extends EventEmitter { */ blockListener (e) { if (this.editingTarget) { - this.editingTarget.blocks.blocklyListen(e, this.runtime); + this.editingTarget.blocks.blocklyListen(e); } } @@ -1117,7 +1117,7 @@ class VirtualMachine extends EventEmitter { * @param {!Blockly.Event} e Any Blockly event. */ flyoutBlockListener (e) { - this.runtime.flyoutBlocks.blocklyListen(e, this.runtime); + this.runtime.flyoutBlocks.blocklyListen(e); } /** @@ -1128,7 +1128,7 @@ class VirtualMachine extends EventEmitter { // Filter events by type, since monitor blocks only need to listen to these events. // Monitor blocks shouldn't be destroyed when flyout blocks are deleted. if (['create', 'change'].indexOf(e.type) !== -1) { - this.runtime.monitorBlocks.blocklyListen(e, this.runtime); + this.runtime.monitorBlocks.blocklyListen(e); } } @@ -1140,8 +1140,7 @@ class VirtualMachine extends EventEmitter { // Filter events by type, since blocks only needs to listen to these // var events. if (['var_create', 'var_rename', 'var_delete'].indexOf(e.type) !== -1) { - this.runtime.getTargetForStage().blocks.blocklyListen(e, - this.runtime); + this.runtime.getTargetForStage().blocks.blocklyListen(e); } } From eb0ff7a2e78459eb425a9e8be235b93d826d4886 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Tue, 5 Feb 2019 16:38:30 -0500 Subject: [PATCH 1746/1971] Merge pull request #1962 from LLK/project-dirty-state-fixes Project dirty state fixes --- packages/scratch-vm/src/engine/runtime.js | 2 +- packages/scratch-vm/src/virtual-machine.js | 35 +++++++++++++++++----- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index e946670155..532a35388b 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1770,7 +1770,7 @@ class Runtime extends EventEmitter { } if (this._refreshTargets) { - this.emit(Runtime.TARGETS_UPDATE); + this.emit(Runtime.TARGETS_UPDATE, false /* Don't emit project changed */); this._refreshTargets = false; } diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 38eb79e9f3..68f888257d 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -94,8 +94,8 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.VISUAL_REPORT, visualReport => { this.emit(Runtime.VISUAL_REPORT, visualReport); }); - this.runtime.on(Runtime.TARGETS_UPDATE, () => { - this.emitTargetsUpdate(); + this.runtime.on(Runtime.TARGETS_UPDATE, emitProjectChanged => { + this.emitTargetsUpdate(emitProjectChanged); }); this.runtime.on(Runtime.MONITORS_UPDATE, monitorList => { this.emit(Runtime.MONITORS_UPDATE, monitorList); @@ -201,7 +201,7 @@ class VirtualMachine extends EventEmitter { clear () { this.runtime.dispose(); this.editingTarget = null; - this.emitTargetsUpdate(); + this.emitTargetsUpdate(false /* Don't emit project change */); } /** @@ -569,6 +569,7 @@ class VirtualMachine extends EventEmitter { } return Promise.reject(`${errorPrefix} Unable to verify sprite version.`); }) + .then(() => this.runtime.emitProjectChanged()) .catch(error => { // Intentionally rejecting here (want errors to be handled by caller) if (error.hasOwnProperty('validationError')) { @@ -627,6 +628,7 @@ class VirtualMachine extends EventEmitter { target.setCostume( target.getCostumes().length - 1 ); + this.runtime.emitProjectChanged(); }); } // If the target cannot be found by id, return a rejected promise @@ -698,6 +700,7 @@ class VirtualMachine extends EventEmitter { const deletedCostume = this.editingTarget.deleteCostume(costumeIndex); if (deletedCostume) { const target = this.editingTarget; + this.runtime.emitProjectChanged(); return () => { target.addCostume(deletedCostume); this.emitTargetsUpdate(); @@ -797,6 +800,7 @@ class VirtualMachine extends EventEmitter { const target = this.editingTarget; const deletedSound = this.editingTarget.deleteSound(soundIndex); if (deletedSound) { + this.runtime.emitProjectChanged(); const restoreFun = () => { target.addSound(deletedSound); this.emitTargetsUpdate(); @@ -928,6 +932,7 @@ class VirtualMachine extends EventEmitter { const stage = this.runtime.getTargetForStage(); stage.addCostume(backdropObject); stage.setCostume(stage.getCostumes().length - 1); + this.runtime.emitProjectChanged(); }); } @@ -958,8 +963,9 @@ class VirtualMachine extends EventEmitter { const currTarget = allTargets[i]; currTarget.blocks.updateAssetName(oldName, newName, 'sprite'); } + + if (newUnusedName !== oldName) this.emitTargetsUpdate(); } - this.emitTargetsUpdate(); } else { throw new Error('No target with the provided id.'); } @@ -1281,7 +1287,9 @@ class VirtualMachine extends EventEmitter { // Currently editing target id. editingTarget: this.editingTarget ? this.editingTarget.id : null }); - if (triggerProjectChange) this.runtime.emitProjectChanged(); + if (triggerProjectChange) { + this.runtime.emitProjectChanged(); + } } /** @@ -1383,7 +1391,11 @@ class VirtualMachine extends EventEmitter { reorderCostume (targetId, costumeIndex, newIndex) { const target = this.runtime.getTargetById(targetId); if (target) { - return target.reorderCostume(costumeIndex, newIndex); + const reorderSuccessful = target.reorderCostume(costumeIndex, newIndex); + if (reorderSuccessful) { + this.runtime.emitProjectChanged(); + } + return reorderSuccessful; } return false; } @@ -1398,7 +1410,11 @@ class VirtualMachine extends EventEmitter { reorderSound (targetId, soundIndex, newIndex) { const target = this.runtime.getTargetById(targetId); if (target) { - return target.reorderSound(soundIndex, newIndex); + const reorderSuccessful = target.reorderSound(soundIndex, newIndex); + if (reorderSuccessful) { + this.runtime.emitProjectChanged(); + } + return reorderSuccessful; } return false; } @@ -1440,6 +1456,11 @@ class VirtualMachine extends EventEmitter { } else { this.editingTarget.postSpriteInfo(data); } + // Post sprite info means the gui has changed something about a sprite, + // either through the sprite info pane fields (e.g. direction, size) or + // through dragging a sprite on the stage + // Emit a project changed event. + this.runtime.emitProjectChanged(); } /** From 79d785225a72fbcdd8638580a7a65b1f8cb2f72b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 7 Feb 2019 08:31:25 -0500 Subject: [PATCH 1747/1971] Merge pull request #1979 from paulkaplan/share-blocks-new-ids Add a utility for giving blocks new IDs, use it for sharing blocks. --- packages/scratch-vm/src/virtual-machine.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 68f888257d..7763f4ac19 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -16,6 +16,7 @@ const formatMessage = require('format-message'); const validate = require('scratch-parser'); const Variable = require('./engine/variable'); +const newBlockIds = require('./util/new-block-ids'); const {loadCostume} = require('./import/load-costume.js'); const {loadSound} = require('./import/load-sound.js'); @@ -1184,6 +1185,7 @@ class VirtualMachine extends EventEmitter { */ shareBlocksToTarget (blocks, targetId, optFromTargetId) { const copiedBlocks = JSON.parse(JSON.stringify(blocks)); + newBlockIds(copiedBlocks); const target = this.runtime.getTargetById(targetId); if (optFromTargetId) { From 75bd36ae378f0c94d0d624896d9761808f9f95cb Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Tue, 12 Feb 2019 09:59:22 -0500 Subject: [PATCH 1748/1971] Merge pull request #1933 from ErikMejerHansen/feature/field_type_support Feature/field type support --- packages/scratch-vm/src/engine/runtime.js | 107 +++++++++++++++++++-- packages/scratch-vm/src/virtual-machine.js | 3 + 2 files changed, 104 insertions(+), 6 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 532a35388b..08a3e95a02 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -556,6 +556,14 @@ class Runtime extends EventEmitter { return 'EXTENSION_ADDED'; } + /** + * Event name for reporting that an extension as asked for a custom field to be added + * @const {string} + */ + static get EXTENSION_FIELD_ADDED () { + return 'EXTENSION_FIELD_ADDED'; + } + /** * Event name for updating the available set of peripheral devices. * This causes the peripheral connection modal to update a list of @@ -779,6 +787,7 @@ class Runtime extends EventEmitter { color1: extensionInfo.colour || '#0FBD8C', color2: extensionInfo.colourSecondary || '#0DA57A', color3: extensionInfo.colourTertiary || '#0B8E69', + customFieldTypes: {}, blocks: [], menus: [] }; @@ -787,7 +796,23 @@ class Runtime extends EventEmitter { this._fillExtensionCategory(categoryInfo, extensionInfo); - this.emit(Runtime.EXTENSION_ADDED, categoryInfo.blocks.concat(categoryInfo.menus)); + const fieldTypeDefinitionsForScratch = []; + for (const fieldTypeName in categoryInfo.customFieldTypes) { + if (extensionInfo.customFieldTypes.hasOwnProperty(fieldTypeName)) { + const fieldTypeInfo = categoryInfo.customFieldTypes[fieldTypeName]; + fieldTypeDefinitionsForScratch.push(fieldTypeInfo.scratchBlocksDefinition); + + // Emit events for custom field types from extension + this.emit(Runtime.EXTENSION_FIELD_ADDED, { + name: `field_${fieldTypeInfo.extendedName}`, + implementation: fieldTypeInfo.fieldImplementation + }); + } + } + + const allBlocks = fieldTypeDefinitionsForScratch.concat(categoryInfo.blocks).concat(categoryInfo.menus); + + this.emit(Runtime.EXTENSION_ADDED, allBlocks); } /** @@ -811,7 +836,8 @@ class Runtime extends EventEmitter { } /** - * Read extension information, convert menus and blocks, and store the results in the provided category object. + * Read extension information, convert menus, blocks and custom field types + * and store the results in the provided category object. * @param {CategoryInfo} categoryInfo - the category to be filled * @param {ExtensionMetadata} extensionInfo - the extension metadata to read * @private @@ -824,6 +850,19 @@ class Runtime extends EventEmitter { categoryInfo.menus.push(convertedMenu); } } + for (const fieldTypeName in extensionInfo.customFieldTypes) { + if (extensionInfo.customFieldTypes.hasOwnProperty(fieldTypeName)) { + const fieldType = extensionInfo.customFieldTypes[fieldTypeName]; + const fieldTypeInfo = this._buildCustomFieldInfo( + fieldTypeName, + fieldType, + extensionInfo.id, + categoryInfo + ); + + categoryInfo.customFieldTypes[fieldTypeName] = fieldTypeInfo; + } + } for (const blockInfo of extensionInfo.blocks) { if (blockInfo === '---') { @@ -897,6 +936,55 @@ class Runtime extends EventEmitter { }; } + _buildCustomFieldInfo (fieldName, fieldInfo, extensionId, categoryInfo) { + const extendedName = `${extensionId}_${fieldName}`; + return { + fieldName: fieldName, + extendedName: extendedName, + argumentTypeInfo: { + shadowType: extendedName, + fieldType: `field_${extendedName}` + }, + scratchBlocksDefinition: this._buildCustomFieldTypeForScratchBlocks( + extendedName, + fieldInfo.output, + fieldInfo.outputShape, + categoryInfo + ), + fieldImplementation: fieldInfo.implementation + }; + } + + /** + * Build the scratch-blocks JSON needed for a fieldType. + * Custom field types need to be namespaced to the extension so that extensions can't interfere with each other + * @param {string} fieldName - The name of the field + * @param {string} output - The output of the field + * @param {number} outputShape - Shape of the field (from ScratchBlocksConstants) + * @param {object} categoryInfo - The category the field belongs to (Used to set its colors) + * @returns {object} - Object to be inserted into scratch-blocks + */ + _buildCustomFieldTypeForScratchBlocks (fieldName, output, outputShape, categoryInfo) { + return { + json: { + type: fieldName, + message0: '%1', + inputsInline: true, + output: output, + colour: categoryInfo.color1, + colourSecondary: categoryInfo.color2, + colourTertiary: categoryInfo.color3, + outputShape: outputShape, + args0: [ + { + name: `field_${fieldName}`, + type: `field_${fieldName}` + } + ] + } + }; + } + /** * Convert ExtensionBlockMetadata into scratch-blocks JSON & XML, and generate a proxy function. * @param {ExtensionBlockMetadata} blockInfo - the block to convert @@ -1065,10 +1153,16 @@ class Runtime extends EventEmitter { }; const argInfo = context.blockInfo.arguments[placeholder] || {}; - const argTypeInfo = ArgumentTypeMap[argInfo.type] || {}; - const defaultValue = (typeof argInfo.defaultValue === 'undefined' ? - '' : - escapeHtml(maybeFormatMessage(argInfo.defaultValue, this.makeMessageContextForTarget()).toString())); + let argTypeInfo = ArgumentTypeMap[argInfo.type] || {}; + + // Field type not a standard field type, see if extension has registered custom field type + if (!ArgumentTypeMap[argInfo.type] && context.categoryInfo.customFieldTypes[argInfo.type]) { + argTypeInfo = context.categoryInfo.customFieldTypes[argInfo.type].argumentTypeInfo; + } + + const defaultValue = + typeof argInfo.defaultValue === 'undefined' ? '' : + escapeHtml(maybeFormatMessage(argInfo.defaultValue, this.makeMessageContextForTarget()).toString()); if (argTypeInfo.check) { argJSON.check = argTypeInfo.check; @@ -1103,6 +1197,7 @@ class Runtime extends EventEmitter { blockArgs.push(argJSON); const argNum = blockArgs.length; context.argsMap[placeholder] = argNum; + return `%${argNum}`; } diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 7763f4ac19..e762ce1962 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -110,6 +110,9 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.EXTENSION_ADDED, blocksInfo => { this.emit(Runtime.EXTENSION_ADDED, blocksInfo); }); + this.runtime.on(Runtime.EXTENSION_FIELD_ADDED, (fieldName, fieldImplementation) => { + this.emit(Runtime.EXTENSION_FIELD_ADDED, fieldName, fieldImplementation); + }); this.runtime.on(Runtime.BLOCKSINFO_UPDATE, blocksInfo => { this.emit(Runtime.BLOCKSINFO_UPDATE, blocksInfo); }); From 98bc9318c56d92d54381671e5042152437248a9b Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 13 Feb 2019 10:41:36 -0800 Subject: [PATCH 1749/1971] Merge pull request #1871 from Affonso-Gui/fix_dynamic_menu_string Fix dynamic menus for string arrays --- .../scratch-vm/src/extension-support/extension-manager.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index bdebb2acee..eed64ce9b7 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -320,13 +320,17 @@ class ExtensionManager { const menuItems = menuFunc.call(extensionObject, editingTargetID).map( item => { item = maybeFormatMessage(item, extensionMessageContext); - if (typeof item === 'object') { + switch (typeof item) { + case 'object': return [ maybeFormatMessage(item.text, extensionMessageContext), item.value ]; + case 'string': + return [item, item]; + default: + return item; } - return item; }); if (!menuItems || menuItems.length < 1) { From d6942b91be011d699f23c9b35f9756e5b852cdc1 Mon Sep 17 00:00:00 2001 From: Katie Broida Date: Mon, 25 Feb 2019 15:57:52 -0500 Subject: [PATCH 1750/1971] Merge pull request #1959 from ktbee/declare-const-once Declare const once to be reused in for loop --- packages/scratch-vm/src/engine/runtime.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 08a3e95a02..2cbdeb54ce 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1556,6 +1556,8 @@ class Runtime extends EventEmitter { } const instance = this; const newThreads = []; + // Look up metadata for the relevant hat. + const hatMeta = instance._hats[requestedHatOpcode]; for (const opts in optMatchFields) { if (!optMatchFields.hasOwnProperty(opts)) continue; @@ -1602,8 +1604,6 @@ class Runtime extends EventEmitter { } } - // Look up metadata for the relevant hat. - const hatMeta = instance._hats[requestedHatOpcode]; if (hatMeta.restartExistingThreads) { // If `restartExistingThreads` is true, we should stop // any existing threads starting with the top block. From 94006d5276cb7b28ccbedd75abea11175804b988 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Mon, 4 Mar 2019 12:51:10 -0500 Subject: [PATCH 1751/1971] Merge pull request #1972 from mzgoddard/sound-bank-over-sprite Sound bank over sprite --- packages/scratch-vm/src/virtual-machine.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index e762ce1962..b610d193ea 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -678,7 +678,7 @@ class VirtualMachine extends EventEmitter { duplicateSound (soundIndex) { const originalSound = this.editingTarget.getSounds()[soundIndex]; const clone = Object.assign({}, originalSound); - return loadSound(clone, this.runtime, this.editingTarget.sprite).then(() => { + return loadSound(clone, this.runtime, this.editingTarget.sprite.soundBank).then(() => { this.editingTarget.addSound(clone, soundIndex + 1); this.emitTargetsUpdate(); }); @@ -723,7 +723,7 @@ class VirtualMachine extends EventEmitter { const target = optTargetId ? this.runtime.getTargetById(optTargetId) : this.editingTarget; if (target) { - return loadSound(soundObject, this.runtime, target.sprite).then(() => { + return loadSound(soundObject, this.runtime, target.sprite.soundBank).then(() => { target.addSound(soundObject); this.emitTargetsUpdate(); }); @@ -1250,7 +1250,7 @@ class VirtualMachine extends EventEmitter { const originalSound = this.editingTarget.getSounds()[soundIndex]; const clone = Object.assign({}, originalSound); const target = this.runtime.getTargetById(targetId); - return loadSound(clone, this.runtime, target.sprite).then(() => { + return loadSound(clone, this.runtime, target.sprite.soundBank).then(() => { if (target) { target.addSound(clone); this.emitTargetsUpdate(); From 52d9e4b59e4c0bb764ac5573487e8b5a426f9d7c Mon Sep 17 00:00:00 2001 From: Katie Broida Date: Fri, 8 Mar 2019 11:52:23 -0500 Subject: [PATCH 1752/1971] Merge pull request #2038 from ktbee/cast-sprite-names-to-string Cast sprite name to string before getSpriteTargetByName --- packages/scratch-vm/src/sprites/rendered-target.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 78243a2697..12a51f2abc 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -1,6 +1,7 @@ const log = require('../util/log'); const MathUtil = require('../util/math-util'); const StringUtil = require('../util/string-util'); +const Cast = require('../util/cast'); const Clone = require('../util/clone'); const Target = require('../engine/target'); const StageLayering = require('../engine/stage-layering'); @@ -840,6 +841,7 @@ class RenderedTarget extends Target { * @return {boolean} True iff touching a clone of the sprite. */ isTouchingSprite (spriteName) { + spriteName = Cast.toString(spriteName); const firstClone = this.runtime.getSpriteTargetByName(spriteName); if (!firstClone || !this.renderer) { return false; From d90ae521fcaaafb075a263610314021be3fdb2cb Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Tue, 12 Mar 2019 17:11:26 -0400 Subject: [PATCH 1753/1971] Merge pull request #1956 from mzgoddard/lazy-eval-big-dependencies Lazy eval big dependencies --- .../extension-support/extension-manager.js | 39 +++++++------------ packages/scratch-vm/src/virtual-machine.js | 28 +++++++++---- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index eed64ce9b7..4d4e2d283b 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -7,32 +7,21 @@ const BlockType = require('./block-type'); // These extensions are currently built into the VM repository but should not be loaded at startup. // TODO: move these out into a separate repository? // TODO: change extension spec so that library info, including extension ID, can be collected through static methods -const Scratch3PenBlocks = require('../extensions/scratch3_pen'); -const Scratch3WeDo2Blocks = require('../extensions/scratch3_wedo2'); -const Scratch3MusicBlocks = require('../extensions/scratch3_music'); -const Scratch3MicroBitBlocks = require('../extensions/scratch3_microbit'); -const Scratch3Text2SpeechBlocks = require('../extensions/scratch3_text2speech'); -const Scratch3TranslateBlocks = require('../extensions/scratch3_translate'); -const Scratch3VideoSensingBlocks = require('../extensions/scratch3_video_sensing'); -const Scratch3Speech2TextBlocks = require('../extensions/scratch3_speech2text'); -const Scratch3Ev3Blocks = require('../extensions/scratch3_ev3'); -const Scratch3MakeyMakeyBlocks = require('../extensions/scratch3_makeymakey'); -// todo: only load this extension once we have a compatible way to load its -// Vernier module dependency. -// const Scratch3GdxForBlocks = require('../extensions/scratch3_gdx_for'); const builtinExtensions = { - pen: Scratch3PenBlocks, - wedo2: Scratch3WeDo2Blocks, - music: Scratch3MusicBlocks, - microbit: Scratch3MicroBitBlocks, - text2speech: Scratch3Text2SpeechBlocks, - translate: Scratch3TranslateBlocks, - videoSensing: Scratch3VideoSensingBlocks, - speech2text: Scratch3Speech2TextBlocks, - ev3: Scratch3Ev3Blocks, - makeymakey: Scratch3MakeyMakeyBlocks - // gdxfor: Scratch3GdxForBlocks + pen: () => require('../extensions/scratch3_pen'), + wedo2: () => require('../extensions/scratch3_wedo2'), + music: () => require('../extensions/scratch3_music'), + microbit: () => require('../extensions/scratch3_microbit'), + text2speech: () => require('../extensions/scratch3_text2speech'), + translate: () => require('../extensions/scratch3_translate'), + videoSensing: () => require('../extensions/scratch3_video_sensing'), + speech2text: () => require('../extensions/scratch3_speech2text'), + ev3: () => require('../extensions/scratch3_ev3'), + makeymakey: () => require('../extensions/scratch3_makeymakey') + // todo: only load this extension once we have a compatible way to load its + // Vernier module dependency. + // gdxfor: () => require('../extensions/scratch3_gdx_for') }; /** @@ -133,7 +122,7 @@ class ExtensionManager { return Promise.reject(new Error(message)); } - const extension = builtinExtensions[extensionURL]; + const extension = builtinExtensions[extensionURL](); const extensionInstance = new extension(this.runtime); return this._registerInternalExtension(extensionInstance).then(serviceName => { this._loadedExtensions.set(extensionURL, serviceName); diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index b610d193ea..daf9df544f 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -1,4 +1,10 @@ -const TextEncoder = require('text-encoding').TextEncoder; +let _TextEncoder; +if (typeof TextEncoder === 'undefined') { + _TextEncoder = require('text-encoding').TextEncoder; +} else { + /* global TextEncoder */ + _TextEncoder = TextEncoder; +} const EventEmitter = require('events'); const JSZip = require('jszip'); @@ -8,12 +14,8 @@ const ExtensionManager = require('./extension-support/extension-manager'); const log = require('./util/log'); const MathUtil = require('./util/math-util'); const Runtime = require('./engine/runtime'); -const {SB1File, ValidationError} = require('scratch-sb1-converter'); -const sb2 = require('./serialization/sb2'); -const sb3 = require('./serialization/sb3'); const StringUtil = require('./util/string-util'); const formatMessage = require('format-message'); -const validate = require('scratch-parser'); const Variable = require('./engine/variable'); const newBlockIds = require('./util/new-block-ids'); @@ -297,6 +299,7 @@ class VirtualMachine extends EventEmitter { } const validationPromise = new Promise((resolve, reject) => { + const validate = require('scratch-parser'); // The second argument of false below indicates to the validator that the // input should be parsed/validated as an entire project (and not a single sprite) validate(input, false, (error, res) => { @@ -305,6 +308,8 @@ class VirtualMachine extends EventEmitter { }); }) .catch(error => { + const {SB1File, ValidationError} = require('scratch-sb1-converter'); + try { const sb1 = new SB1File(input); const json = sb1.json; @@ -410,6 +415,8 @@ class VirtualMachine extends EventEmitter { * specified by optZipType or blob by default. */ exportSprite (targetId, optZipType) { + const sb3 = require('./serialization/sb3'); + const soundDescs = serializeSounds(this.runtime, targetId); const costumeDescs = serializeCostumes(this.runtime, targetId); const spriteJson = StringUtil.stringify(sb3.serialize(this.runtime, targetId)); @@ -432,6 +439,7 @@ class VirtualMachine extends EventEmitter { * @return {string} Serialized state of the runtime. */ toJSON () { + const sb3 = require('./serialization/sb3'); return StringUtil.stringify(sb3.serialize(this.runtime)); } @@ -461,9 +469,11 @@ class VirtualMachine extends EventEmitter { const deserializePromise = function () { const projectVersion = projectJSON.projectVersion; if (projectVersion === 2) { + const sb2 = require('./serialization/sb2'); return sb2.deserialize(projectJSON, runtime, false, zip); } if (projectVersion === 3) { + const sb3 = require('./serialization/sb3'); return sb3.deserialize(projectJSON, runtime, zip); } return Promise.reject('Unable to verify Scratch Project version.'); @@ -553,6 +563,7 @@ class VirtualMachine extends EventEmitter { } const validationPromise = new Promise((resolve, reject) => { + const validate = require('scratch-parser'); // The second argument of true below indicates to the parser/validator // that the given input should be treated as a single sprite and not // an entire project @@ -592,6 +603,7 @@ class VirtualMachine extends EventEmitter { _addSprite2 (sprite, zip) { // Validate & parse + const sb2 = require('./serialization/sb2'); return sb2.deserialize(sprite, this.runtime, true, zip) .then(({targets, extensions}) => this.installTargets(targets, extensions, false)); @@ -605,7 +617,7 @@ class VirtualMachine extends EventEmitter { */ _addSprite3 (sprite, zip) { // Validate & parse - + const sb3 = require('./serialization/sb3'); return sb3 .deserialize(sprite, this.runtime, zip, true) .then(({targets, extensions}) => this.installTargets(targets, extensions, false)); @@ -912,7 +924,7 @@ class VirtualMachine extends EventEmitter { costume.asset = storage.createAsset( storage.AssetType.ImageVector, costume.dataFormat, - (new TextEncoder()).encode(svg), + (new _TextEncoder()).encode(svg), null, true // generate md5 ); @@ -1187,6 +1199,8 @@ class VirtualMachine extends EventEmitter { * @return {!Promise} Promise that resolves when the extensions and blocks have been added. */ shareBlocksToTarget (blocks, targetId, optFromTargetId) { + const sb3 = require('./serialization/sb3'); + const copiedBlocks = JSON.parse(JSON.stringify(blocks)); newBlockIds(copiedBlocks); const target = this.runtime.getTargetById(targetId); From dc71dc01ab0d504f057602a48384a349bb9b2706 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 26 Mar 2019 09:46:06 -0400 Subject: [PATCH 1754/1971] Merge pull request #2069 from ericrosenbaum/bugfix/load-vernier-code Load Vernier extension code --- .../scratch-vm/src/extension-support/extension-manager.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 4d4e2d283b..f22357b360 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -18,10 +18,8 @@ const builtinExtensions = { videoSensing: () => require('../extensions/scratch3_video_sensing'), speech2text: () => require('../extensions/scratch3_speech2text'), ev3: () => require('../extensions/scratch3_ev3'), - makeymakey: () => require('../extensions/scratch3_makeymakey') - // todo: only load this extension once we have a compatible way to load its - // Vernier module dependency. - // gdxfor: () => require('../extensions/scratch3_gdx_for') + makeymakey: () => require('../extensions/scratch3_makeymakey'), + gdxfor: () => require('../extensions/scratch3_gdx_for') }; /** From c00f297fe5711b1d2e228aeac7ca6ed811e5d095 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 28 Mar 2019 14:05:16 -0400 Subject: [PATCH 1755/1971] Merge pull request #2060 from kchadha/load-core-extension Load core extensions synchronously --- .../extension-support/extension-manager.js | 57 +++++++++++++++---- packages/scratch-vm/src/virtual-machine.js | 13 ++--- 2 files changed, 51 insertions(+), 19 deletions(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index f22357b360..99fa83bf1c 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -9,6 +9,10 @@ const BlockType = require('./block-type'); // TODO: change extension spec so that library info, including extension ID, can be collected through static methods const builtinExtensions = { + // This is an example that isn't loaded with the other core blocks, + // but serves as a reference for loading core blocks as extensions. + coreExample: () => require('../blocks/scratch3_core_example'), + // These are the non-core built-in extensions. pen: () => require('../extensions/scratch3_pen'), wedo2: () => require('../extensions/scratch3_wedo2'), music: () => require('../extensions/scratch3_music'), @@ -106,6 +110,30 @@ class ExtensionManager { return this._loadedExtensions.has(extensionID); } + /** + * Synchronously load an internal extension (core or non-core) by ID. This call will + * fail if the provided id is not does not match an internal extension. + * @param {string} extensionId - the ID of an internal extension + */ + loadExtensionIdSync (extensionId) { + if (!builtinExtensions.hasOwnProperty(extensionId)) { + log.warn(`Could not find extension ${extensionId} in the built in extensions.`); + return; + } + + /** @TODO dupe handling for non-builtin extensions. See commit 670e51d33580e8a2e852b3b038bb3afc282f81b9 */ + if (this.isExtensionLoaded(extensionId)) { + const message = `Rejecting attempt to load a second extension with ID ${extensionId}`; + log.warn(message); + return; + } + + const extension = builtinExtensions[extensionId](); + const extensionInstance = new extension(this.runtime); + const serviceName = this._registerInternalExtension(extensionInstance); + this._loadedExtensions.set(extensionId, serviceName); + } + /** * Load an extension by URL or internal extension ID * @param {string} extensionURL - the URL for the extension to load OR the ID of an internal extension @@ -117,14 +145,14 @@ class ExtensionManager { if (this.isExtensionLoaded(extensionURL)) { const message = `Rejecting attempt to load a second extension with ID ${extensionURL}`; log.warn(message); - return Promise.reject(new Error(message)); + return Promise.resolve(); } const extension = builtinExtensions[extensionURL](); const extensionInstance = new extension(this.runtime); - return this._registerInternalExtension(extensionInstance).then(serviceName => { - this._loadedExtensions.set(extensionURL, serviceName); - }); + const serviceName = this._registerInternalExtension(extensionInstance); + this._loadedExtensions.set(extensionURL, serviceName); + return Promise.resolve(); } return new Promise((resolve, reject) => { @@ -148,7 +176,7 @@ class ExtensionManager { dispatch.call('runtime', '_refreshExtensionPrimitives', info); }) .catch(e => { - log.error(`Failed to refresh buildtin extension primitives: ${JSON.stringify(e)}`); + log.error(`Failed to refresh built-in extension primitives: ${JSON.stringify(e)}`); }) ); return Promise.all(allPromises); @@ -161,6 +189,15 @@ class ExtensionManager { return [id, workerInfo.extensionURL]; } + /** + * Synchronously collect extension metadata from the specified service and begin the extension registration process. + * @param {string} serviceName - the name of the service hosting the extension. + */ + registerExtensionServiceSync (serviceName) { + const info = dispatch.callSync(serviceName, 'getInfo'); + this._registerExtensionInfo(serviceName, info); + } + /** * Collect extension metadata from the specified service and begin the extension registration process. * @param {string} serviceName - the name of the service hosting the extension. @@ -189,17 +226,15 @@ class ExtensionManager { /** * Register an internal (non-Worker) extension object * @param {object} extensionObject - the extension object to register - * @returns {Promise} resolved once the extension is fully registered or rejected on failure + * @returns {string} The name of the registered extension service */ _registerInternalExtension (extensionObject) { const extensionInfo = extensionObject.getInfo(); const fakeWorkerId = this.nextExtensionWorker++; const serviceName = `extension_${fakeWorkerId}_${extensionInfo.id}`; - return dispatch.setService(serviceName, extensionObject) - .then(() => { - dispatch.call('extensions', 'registerExtensionService', serviceName); - return serviceName; - }); + dispatch.setServiceSync(serviceName, extensionObject); + dispatch.callSync('extensions', 'registerExtensionServiceSync', serviceName); + return serviceName; } /** diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index daf9df544f..82375c429c 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -151,6 +151,11 @@ class VirtualMachine extends EventEmitter { this.extensionManager = new ExtensionManager(this.runtime); + // Load core extensions + for (const id of CORE_EXTENSIONS) { + this.extensionManager.loadExtensionIdSync(id); + } + this.blockListener = this.blockListener.bind(this); this.flyoutBlockListener = this.flyoutBlockListener.bind(this); this.monitorBlockListener = this.monitorBlockListener.bind(this); @@ -493,14 +498,6 @@ class VirtualMachine extends EventEmitter { installTargets (targets, extensions, wholeProject) { const extensionPromises = []; - if (wholeProject) { - CORE_EXTENSIONS.forEach(extensionID => { - if (!this.extensionManager.isExtensionLoaded(extensionID)) { - extensionPromises.push(this.extensionManager.loadExtensionURL(extensionID)); - } - }); - } - extensions.extensionIDs.forEach(extensionID => { if (!this.extensionManager.isExtensionLoaded(extensionID)) { const extensionURL = extensions.extensionURLs.get(extensionID) || extensionID; From b181abd99b9cbe9f3a3b0d85734e40d3ded3899f Mon Sep 17 00:00:00 2001 From: Katie Broida Date: Fri, 29 Mar 2019 13:01:28 -0400 Subject: [PATCH 1756/1971] Merge pull request #1943 from ktbee/clock-timer-compat-fix Start executing hats before other threads change values --- packages/scratch-vm/src/engine/runtime.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 2cbdeb54ce..c2e37f3c78 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -7,6 +7,7 @@ const Blocks = require('./blocks'); const BlockType = require('../extension-support/block-type'); const Profiler = require('./profiler'); const Sequencer = require('./sequencer'); +const execute = require('./execute.js'); const ScratchBlocksConstants = require('./scratch-blocks-constants'); const TargetType = require('../extension-support/target-type'); const Thread = require('./thread'); @@ -316,7 +317,7 @@ class Runtime extends EventEmitter { // I/O related data. /** @type {Object.} */ this.ioDevices = { - clock: new Clock(), + clock: new Clock(this), cloud: new Cloud(this), keyboard: new Keyboard(this), mouse: new Mouse(this), @@ -1631,6 +1632,12 @@ class Runtime extends EventEmitter { // Start the thread with this top block. newThreads.push(instance._pushThread(topBlockId, target)); }, optTarget); + // For compatibility with Scratch 2, edge triggered hats need to be processed before + // threads are stepped. See ScratchRuntime.as for original implementation + newThreads.forEach(thread => { + execute(this.sequencer, thread); + thread.goToNextBlock(); + }); return newThreads; } From 27e916eda0228babfb2ac0aa66629ee0ead16503 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 3 Apr 2019 12:28:09 -0700 Subject: [PATCH 1757/1971] Merge pull request #2083 from cwillisf/extension-buttons Allow extensions to make buttons --- packages/scratch-vm/src/engine/runtime.js | 92 ++++++++++++++----- .../extension-support/extension-manager.js | 29 ++++-- 2 files changed, 88 insertions(+), 33 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index c2e37f3c78..204cd46335 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -123,16 +123,6 @@ const cloudDataManager = () => { }; }; -/** - * Predefined "Converted block info" for a separator between blocks in a block category - * @type {ConvertedBlockInfo} - */ -const ConvertedSeparator = { - info: {}, - json: null, - xml: '' -}; - /** * Numeric ID for Runtime._step in Profiler instances. * @type {number} @@ -866,22 +856,20 @@ class Runtime extends EventEmitter { } for (const blockInfo of extensionInfo.blocks) { - if (blockInfo === '---') { - categoryInfo.blocks.push(ConvertedSeparator); - continue; - } try { const convertedBlock = this._convertForScratchBlocks(blockInfo, categoryInfo); - const opcode = convertedBlock.json.type; categoryInfo.blocks.push(convertedBlock); - if (blockInfo.blockType !== BlockType.EVENT) { - this._primitives[opcode] = convertedBlock.info.func; - } - if (blockInfo.blockType === BlockType.EVENT || blockInfo.blockType === BlockType.HAT) { - this._hats[opcode] = { - edgeActivated: blockInfo.isEdgeActivated, - restartExistingThreads: blockInfo.shouldRestartExistingThreads - }; + if (convertedBlock.json) { + const opcode = convertedBlock.json.type; + if (blockInfo.blockType !== BlockType.EVENT) { + this._primitives[opcode] = convertedBlock.info.func; + } + if (blockInfo.blockType === BlockType.EVENT || blockInfo.blockType === BlockType.HAT) { + this._hats[opcode] = { + edgeActivated: blockInfo.isEdgeActivated, + restartExistingThreads: blockInfo.shouldRestartExistingThreads + }; + } } } catch (e) { log.error('Error parsing block: ', {block: blockInfo, error: e}); @@ -986,6 +974,25 @@ class Runtime extends EventEmitter { }; } + /** + * Convert ExtensionBlockMetadata into data ready for scratch-blocks. + * @param {ExtensionBlockMetadata} blockInfo - the block info to convert + * @param {CategoryInfo} categoryInfo - the category for this block + * @returns {ConvertedBlockInfo} - the converted & original block information + * @private + */ + _convertForScratchBlocks (blockInfo, categoryInfo) { + if (blockInfo === '---') { + return this._convertSeparatorForScratchBlocks(blockInfo); + } + + if (blockInfo.blockType === BlockType.BUTTON) { + return this._convertButtonForScratchBlocks(blockInfo); + } + + return this._convertBlockForScratchBlocks(blockInfo, categoryInfo); + } + /** * Convert ExtensionBlockMetadata into scratch-blocks JSON & XML, and generate a proxy function. * @param {ExtensionBlockMetadata} blockInfo - the block to convert @@ -993,7 +1000,7 @@ class Runtime extends EventEmitter { * @returns {ConvertedBlockInfo} - the converted & original block information * @private */ - _convertForScratchBlocks (blockInfo, categoryInfo) { + _convertBlockForScratchBlocks (blockInfo, categoryInfo) { const extendedOpcode = `${categoryInfo.id}_${blockInfo.opcode}`; const blockJSON = { @@ -1135,6 +1142,43 @@ class Runtime extends EventEmitter { }; } + /** + * Generate a separator between blocks categories or sub-categories. + * @param {ExtensionBlockMetadata} blockInfo - the block to convert + * @param {CategoryInfo} categoryInfo - the category for this block + * @returns {ConvertedBlockInfo} - the converted & original block information + * @private + */ + _convertSeparatorForScratchBlocks (blockInfo) { + return { + info: blockInfo, + xml: '' + }; + } + + /** + * Convert a button for scratch-blocks. A button has no opcode but specifies a callback name in the `func` field. + * @param {ExtensionBlockMetadata} buttonInfo - the button to convert + * @property {string} func - the callback name + * @param {CategoryInfo} categoryInfo - the category for this button + * @returns {ConvertedBlockInfo} - the converted & original button information + * @private + */ + _convertButtonForScratchBlocks (buttonInfo) { + // for now we only support these pre-defined callbacks handled in scratch-blocks + const supportedCallbackKeys = ['MAKE_A_LIST', 'MAKE_A_PROCEDURE', 'MAKE_A_VARIABLE']; + if (supportedCallbackKeys.indexOf(buttonInfo.func) < 0) { + log.error(`Custom button callbacks not supported yet: ${buttonInfo.func}`); + } + + const extensionMessageContext = this.makeMessageContextForTarget(); + const buttonText = maybeFormatMessage(buttonInfo.text, extensionMessageContext); + return { + info: buttonInfo, + xml: `` + }; + } + /** * Helper for _convertForScratchBlocks which handles linearization of argument placeholders. Called as a callback * from string#replace. In addition to the return value the JSON and XML items in the context will be filled. diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 99fa83bf1c..d3ced5df16 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -375,16 +375,28 @@ class ExtensionManager { blockAllThreads: false, arguments: {} }, blockInfo); - blockInfo.opcode = this._sanitizeID(blockInfo.opcode); + blockInfo.opcode = blockInfo.opcode && this._sanitizeID(blockInfo.opcode); blockInfo.text = blockInfo.text || blockInfo.opcode; - if (blockInfo.blockType !== BlockType.EVENT) { + switch (blockInfo.blockType) { + case BlockType.EVENT: + if (blockInfo.func) { + log.warn(`Ignoring function "${blockInfo.func}" for event block ${blockInfo.opcode}`); + } + break; + case BlockType.BUTTON: + if (blockInfo.opcode) { + log.warn(`Ignoring opcode "${blockInfo.opcode}" for button with text: ${blockInfo.text}`); + } + break; + default: + if (!blockInfo.opcode) { + throw new Error('Missing opcode for block'); + } + blockInfo.func = blockInfo.func ? this._sanitizeID(blockInfo.func) : blockInfo.opcode; - /** - * This is only here because the VM performs poorly when blocks return promises. - * @TODO make it possible for the VM to resolve a promise and continue during the same Scratch "tick" - */ + // Avoid promise overhead if possible if (dispatch._isRemoteService(serviceName)) { blockInfo.func = dispatch.call.bind(dispatch, serviceName, blockInfo.func); } else { @@ -392,12 +404,11 @@ class ExtensionManager { const func = serviceObject[blockInfo.func]; if (func) { blockInfo.func = func.bind(serviceObject); - } else if (blockInfo.blockType !== BlockType.EVENT) { + } else { throw new Error(`Could not find extension block function called ${blockInfo.func}`); } } - } else if (blockInfo.func) { - log.warn(`Ignoring function "${blockInfo.func}" for event block ${blockInfo.opcode}`); + break; } return blockInfo; From 3e712a0a2bddafd40f4d41db692ebf5267aa9b68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20N=C3=B8rby=20Andersen?= Date: Wed, 3 Apr 2019 19:05:37 -0400 Subject: [PATCH 1758/1971] Merge pull request #2061 from knandersen/boostextension Add LEGO BOOST Scratch 3.0 extension --- packages/scratch-vm/src/extension-support/extension-manager.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index d3ced5df16..bf666a28c3 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -23,6 +23,7 @@ const builtinExtensions = { speech2text: () => require('../extensions/scratch3_speech2text'), ev3: () => require('../extensions/scratch3_ev3'), makeymakey: () => require('../extensions/scratch3_makeymakey'), + boost: () => require('../extensions/scratch3_boost'), gdxfor: () => require('../extensions/scratch3_gdx_for') }; From 92219afa8b68827fc1148eb5c96b1207455ac666 Mon Sep 17 00:00:00 2001 From: Katie Broida Date: Tue, 16 Apr 2019 14:50:42 -0400 Subject: [PATCH 1759/1971] Merge pull request #2041 from ktbee/use-empty-bitmap-size Set height and width to zero for the canvas and costume size if bitmap's sourceHeight or sourceWidth are zero --- packages/scratch-vm/src/virtual-machine.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 82375c429c..3320238f4e 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -859,10 +859,13 @@ class VirtualMachine extends EventEmitter { costume.rotationCenterX = rotationCenterX; costume.rotationCenterY = rotationCenterY; + // If the bitmap originally had a zero width or height, use that value + const bitmapWidth = bitmap.sourceWidth === 0 ? 0 : bitmap.width; + const bitmapHeight = bitmap.sourceHeight === 0 ? 0 : bitmap.height; // @todo: updateBitmapSkin does not take ImageData const canvas = document.createElement('canvas'); - canvas.width = bitmap.width; - canvas.height = bitmap.height; + canvas.width = bitmapWidth; + canvas.height = bitmapHeight; const context = canvas.getContext('2d'); context.putImageData(bitmap, 0, 0); @@ -882,7 +885,7 @@ class VirtualMachine extends EventEmitter { const storage = this.runtime.storage; costume.dataFormat = storage.DataFormat.PNG; costume.bitmapResolution = bitmapResolution; - costume.size = [bitmap.width, bitmap.height]; + costume.size = [bitmapWidth, bitmapHeight]; costume.asset = storage.createAsset( storage.AssetType.ImageBitmap, costume.dataFormat, @@ -894,7 +897,10 @@ class VirtualMachine extends EventEmitter { costume.md5 = `${costume.assetId}.${costume.dataFormat}`; this.emitTargetsUpdate(); }); - reader.readAsArrayBuffer(blob); + // Bitmaps with a zero width or height return null for their blob + if (blob){ + reader.readAsArrayBuffer(blob); + } }); } From f2e9a6a1e034949aeb530fe412cb9a43fe697fc6 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 17 Apr 2019 15:55:47 -0400 Subject: [PATCH 1760/1971] Merge pull request #1930 from mzgoddard/runtime-script-cache Cache hat block information for the runtime --- packages/scratch-vm/src/engine/runtime.js | 89 ++++++++++------------- 1 file changed, 40 insertions(+), 49 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 204cd46335..eebda471ec 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -4,6 +4,7 @@ const escapeHtml = require('escape-html'); const ArgumentType = require('../extension-support/argument-type'); const Blocks = require('./blocks'); +const BlocksRuntimeCache = require('./blocks-runtime-cache'); const BlockType = require('../extension-support/block-type'); const Profiler = require('./profiler'); const Sequencer = require('./sequencer'); @@ -1434,16 +1435,11 @@ class Runtime extends EventEmitter { * @return {!Thread} The newly created thread. */ _pushThread (id, target, opts) { - opts = Object.assign({ - stackClick: false, - updateMonitor: false - }, opts); - const thread = new Thread(id); thread.target = target; - thread.stackClick = opts.stackClick; - thread.updateMonitor = opts.updateMonitor; - thread.blockContainer = opts.updateMonitor ? + thread.stackClick = Boolean(opts && opts.stackClick); + thread.updateMonitor = Boolean(opts && opts.updateMonitor); + thread.blockContainer = thread.updateMonitor ? this.monitorBlocks : target.blocks; @@ -1586,6 +1582,20 @@ class Runtime extends EventEmitter { } } + allScriptsByOpcodeDo (opcode, f, optTarget) { + let targets = this.executableTargets; + if (optTarget) { + targets = [optTarget]; + } + for (let t = targets.length - 1; t >= 0; t--) { + const target = targets[t]; + const scripts = BlocksRuntimeCache.getScripts(target.blocks, opcode); + for (let j = 0; j < scripts.length; j++) { + f(scripts[j], target); + } + } + } + /** * Start all relevant hats. * @param {!string} requestedHatOpcode Opcode of hats to start. @@ -1610,71 +1620,52 @@ class Runtime extends EventEmitter { } // Consider all scripts, looking for hats with opcode `requestedHatOpcode`. - this.allScriptsDo((topBlockId, target) => { - const blocks = target.blocks; - const block = blocks.getBlock(topBlockId); - const potentialHatOpcode = block.opcode; - if (potentialHatOpcode !== requestedHatOpcode) { - // Not the right hat. - return; - } + this.allScriptsByOpcodeDo(requestedHatOpcode, (script, target) => { + const { + blockId: topBlockId, + fieldsOfInputs: hatFields + } = script; // Match any requested fields. // For example: ensures that broadcasts match. // This needs to happen before the block is evaluated // (i.e., before the predicate can be run) because "broadcast and wait" // needs to have a precise collection of started threads. - let hatFields = blocks.getFields(block); - - // If no fields are present, check inputs (horizontal blocks) - if (Object.keys(hatFields).length === 0) { - hatFields = {}; // don't overwrite the block's actual fields list - const hatInputs = blocks.getInputs(block); - for (const input in hatInputs) { - if (!hatInputs.hasOwnProperty(input)) continue; - const id = hatInputs[input].block; - const inpBlock = blocks.getBlock(id); - const fields = blocks.getFields(inpBlock); - Object.assign(hatFields, fields); - } - } - - if (optMatchFields) { - for (const matchField in optMatchFields) { - if (hatFields[matchField].value.toUpperCase() !== - optMatchFields[matchField]) { - // Field mismatch. - return; - } + for (const matchField in optMatchFields) { + if (hatFields[matchField].value !== optMatchFields[matchField]) { + // Field mismatch. + return; } } if (hatMeta.restartExistingThreads) { // If `restartExistingThreads` is true, we should stop // any existing threads starting with the top block. - for (let i = 0; i < instance.threads.length; i++) { - if (instance.threads[i].topBlock === topBlockId && - !instance.threads[i].stackClick && // stack click threads and hat threads can coexist - instance.threads[i].target === target) { - newThreads.push(instance._restartThread(instance.threads[i])); + for (let i = 0; i < this.threads.length; i++) { + if (this.threads[i].target === target && + this.threads[i].topBlock === topBlockId && + // stack click threads and hat threads can coexist + !this.threads[i].stackClick) { + newThreads.push(this._restartThread(this.threads[i])); return; } } } else { // If `restartExistingThreads` is false, we should // give up if any threads with the top block are running. - for (let j = 0; j < instance.threads.length; j++) { - if (instance.threads[j].topBlock === topBlockId && - instance.threads[j].target === target && - !instance.threads[j].stackClick && // stack click threads and hat threads can coexist - instance.threads[j].status !== Thread.STATUS_DONE) { + for (let j = 0; j < this.threads.length; j++) { + if (this.threads[j].target === target && + this.threads[j].topBlock === topBlockId && + // stack click threads and hat threads can coexist + !this.threads[j].stackClick && + this.threads[j].status !== Thread.STATUS_DONE) { // Some thread is already running. return; } } } // Start the thread with this top block. - newThreads.push(instance._pushThread(topBlockId, target)); + newThreads.push(this._pushThread(topBlockId, target)); }, optTarget); // For compatibility with Scratch 2, edge triggered hats need to be processed before // threads are stepped. See ScratchRuntime.as for original implementation From afb01f493336ec5d2ec73a42e746c206c6331356 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 17 Apr 2019 16:06:49 -0400 Subject: [PATCH 1761/1971] Merge pull request #2122 from LLK/revert-1930-runtime-script-cache Revert "Cache hat block information for the runtime" --- packages/scratch-vm/src/engine/runtime.js | 89 +++++++++++++---------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index eebda471ec..204cd46335 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -4,7 +4,6 @@ const escapeHtml = require('escape-html'); const ArgumentType = require('../extension-support/argument-type'); const Blocks = require('./blocks'); -const BlocksRuntimeCache = require('./blocks-runtime-cache'); const BlockType = require('../extension-support/block-type'); const Profiler = require('./profiler'); const Sequencer = require('./sequencer'); @@ -1435,11 +1434,16 @@ class Runtime extends EventEmitter { * @return {!Thread} The newly created thread. */ _pushThread (id, target, opts) { + opts = Object.assign({ + stackClick: false, + updateMonitor: false + }, opts); + const thread = new Thread(id); thread.target = target; - thread.stackClick = Boolean(opts && opts.stackClick); - thread.updateMonitor = Boolean(opts && opts.updateMonitor); - thread.blockContainer = thread.updateMonitor ? + thread.stackClick = opts.stackClick; + thread.updateMonitor = opts.updateMonitor; + thread.blockContainer = opts.updateMonitor ? this.monitorBlocks : target.blocks; @@ -1582,20 +1586,6 @@ class Runtime extends EventEmitter { } } - allScriptsByOpcodeDo (opcode, f, optTarget) { - let targets = this.executableTargets; - if (optTarget) { - targets = [optTarget]; - } - for (let t = targets.length - 1; t >= 0; t--) { - const target = targets[t]; - const scripts = BlocksRuntimeCache.getScripts(target.blocks, opcode); - for (let j = 0; j < scripts.length; j++) { - f(scripts[j], target); - } - } - } - /** * Start all relevant hats. * @param {!string} requestedHatOpcode Opcode of hats to start. @@ -1620,52 +1610,71 @@ class Runtime extends EventEmitter { } // Consider all scripts, looking for hats with opcode `requestedHatOpcode`. - this.allScriptsByOpcodeDo(requestedHatOpcode, (script, target) => { - const { - blockId: topBlockId, - fieldsOfInputs: hatFields - } = script; + this.allScriptsDo((topBlockId, target) => { + const blocks = target.blocks; + const block = blocks.getBlock(topBlockId); + const potentialHatOpcode = block.opcode; + if (potentialHatOpcode !== requestedHatOpcode) { + // Not the right hat. + return; + } // Match any requested fields. // For example: ensures that broadcasts match. // This needs to happen before the block is evaluated // (i.e., before the predicate can be run) because "broadcast and wait" // needs to have a precise collection of started threads. - for (const matchField in optMatchFields) { - if (hatFields[matchField].value !== optMatchFields[matchField]) { - // Field mismatch. - return; + let hatFields = blocks.getFields(block); + + // If no fields are present, check inputs (horizontal blocks) + if (Object.keys(hatFields).length === 0) { + hatFields = {}; // don't overwrite the block's actual fields list + const hatInputs = blocks.getInputs(block); + for (const input in hatInputs) { + if (!hatInputs.hasOwnProperty(input)) continue; + const id = hatInputs[input].block; + const inpBlock = blocks.getBlock(id); + const fields = blocks.getFields(inpBlock); + Object.assign(hatFields, fields); + } + } + + if (optMatchFields) { + for (const matchField in optMatchFields) { + if (hatFields[matchField].value.toUpperCase() !== + optMatchFields[matchField]) { + // Field mismatch. + return; + } } } if (hatMeta.restartExistingThreads) { // If `restartExistingThreads` is true, we should stop // any existing threads starting with the top block. - for (let i = 0; i < this.threads.length; i++) { - if (this.threads[i].target === target && - this.threads[i].topBlock === topBlockId && - // stack click threads and hat threads can coexist - !this.threads[i].stackClick) { - newThreads.push(this._restartThread(this.threads[i])); + for (let i = 0; i < instance.threads.length; i++) { + if (instance.threads[i].topBlock === topBlockId && + !instance.threads[i].stackClick && // stack click threads and hat threads can coexist + instance.threads[i].target === target) { + newThreads.push(instance._restartThread(instance.threads[i])); return; } } } else { // If `restartExistingThreads` is false, we should // give up if any threads with the top block are running. - for (let j = 0; j < this.threads.length; j++) { - if (this.threads[j].target === target && - this.threads[j].topBlock === topBlockId && - // stack click threads and hat threads can coexist - !this.threads[j].stackClick && - this.threads[j].status !== Thread.STATUS_DONE) { + for (let j = 0; j < instance.threads.length; j++) { + if (instance.threads[j].topBlock === topBlockId && + instance.threads[j].target === target && + !instance.threads[j].stackClick && // stack click threads and hat threads can coexist + instance.threads[j].status !== Thread.STATUS_DONE) { // Some thread is already running. return; } } } // Start the thread with this top block. - newThreads.push(this._pushThread(topBlockId, target)); + newThreads.push(instance._pushThread(topBlockId, target)); }, optTarget); // For compatibility with Scratch 2, edge triggered hats need to be processed before // threads are stepped. See ScratchRuntime.as for original implementation From 0bf9941589d680ffcb4fbc3c8fe82b7a45d8accf Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Thu, 18 Apr 2019 14:38:41 -0700 Subject: [PATCH 1762/1971] Merge pull request #2123 from cwillisf/extensions-xml-escape use xmlEscape instead of escape-html for extensions --- packages/scratch-vm/src/engine/runtime.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 204cd46335..b5739579cb 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1,6 +1,5 @@ const EventEmitter = require('events'); const {OrderedMap} = require('immutable'); -const escapeHtml = require('escape-html'); const ArgumentType = require('../extension-support/argument-type'); const Blocks = require('./blocks'); @@ -15,6 +14,7 @@ const log = require('../util/log'); const maybeFormatMessage = require('../util/maybe-format-message'); const StageLayering = require('./stage-layering'); const Variable = require('./variable'); +const xmlEscape = require('../util/xml-escape'); // Virtual I/O devices. const Clock = require('../io/clock'); @@ -747,7 +747,7 @@ class Runtime extends EventEmitter { * @private */ _makeExtensionMenuId (menuName, extensionId) { - return `${extensionId}_menu_${escapeHtml(menuName)}`; + return `${extensionId}_menu_${xmlEscape(menuName)}`; } /** @@ -1207,7 +1207,7 @@ class Runtime extends EventEmitter { const defaultValue = typeof argInfo.defaultValue === 'undefined' ? '' : - escapeHtml(maybeFormatMessage(argInfo.defaultValue, this.makeMessageContextForTarget()).toString()); + xmlEscape(maybeFormatMessage(argInfo.defaultValue, this.makeMessageContextForTarget()).toString()); if (argTypeInfo.check) { argJSON.check = argTypeInfo.check; From 036071d05244ec7c23ccd46b969ad0d650d16c75 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 8 May 2019 16:00:28 -0400 Subject: [PATCH 1763/1971] Merge pull request #2126 from mzgoddard/runtime-script-cache-fix Runtime script cache fix --- packages/scratch-vm/src/engine/runtime.js | 89 ++++++++++------------- 1 file changed, 40 insertions(+), 49 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index b5739579cb..1f856fafa1 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -3,6 +3,7 @@ const {OrderedMap} = require('immutable'); const ArgumentType = require('../extension-support/argument-type'); const Blocks = require('./blocks'); +const BlocksRuntimeCache = require('./blocks-runtime-cache'); const BlockType = require('../extension-support/block-type'); const Profiler = require('./profiler'); const Sequencer = require('./sequencer'); @@ -1434,16 +1435,11 @@ class Runtime extends EventEmitter { * @return {!Thread} The newly created thread. */ _pushThread (id, target, opts) { - opts = Object.assign({ - stackClick: false, - updateMonitor: false - }, opts); - const thread = new Thread(id); thread.target = target; - thread.stackClick = opts.stackClick; - thread.updateMonitor = opts.updateMonitor; - thread.blockContainer = opts.updateMonitor ? + thread.stackClick = Boolean(opts && opts.stackClick); + thread.updateMonitor = Boolean(opts && opts.updateMonitor); + thread.blockContainer = thread.updateMonitor ? this.monitorBlocks : target.blocks; @@ -1586,6 +1582,20 @@ class Runtime extends EventEmitter { } } + allScriptsByOpcodeDo (opcode, f, optTarget) { + let targets = this.executableTargets; + if (optTarget) { + targets = [optTarget]; + } + for (let t = targets.length - 1; t >= 0; t--) { + const target = targets[t]; + const scripts = BlocksRuntimeCache.getScripts(target.blocks, opcode); + for (let j = 0; j < scripts.length; j++) { + f(scripts[j], target); + } + } + } + /** * Start all relevant hats. * @param {!string} requestedHatOpcode Opcode of hats to start. @@ -1610,71 +1620,52 @@ class Runtime extends EventEmitter { } // Consider all scripts, looking for hats with opcode `requestedHatOpcode`. - this.allScriptsDo((topBlockId, target) => { - const blocks = target.blocks; - const block = blocks.getBlock(topBlockId); - const potentialHatOpcode = block.opcode; - if (potentialHatOpcode !== requestedHatOpcode) { - // Not the right hat. - return; - } + this.allScriptsByOpcodeDo(requestedHatOpcode, (script, target) => { + const { + blockId: topBlockId, + fieldsOfInputs: hatFields + } = script; // Match any requested fields. // For example: ensures that broadcasts match. // This needs to happen before the block is evaluated // (i.e., before the predicate can be run) because "broadcast and wait" // needs to have a precise collection of started threads. - let hatFields = blocks.getFields(block); - - // If no fields are present, check inputs (horizontal blocks) - if (Object.keys(hatFields).length === 0) { - hatFields = {}; // don't overwrite the block's actual fields list - const hatInputs = blocks.getInputs(block); - for (const input in hatInputs) { - if (!hatInputs.hasOwnProperty(input)) continue; - const id = hatInputs[input].block; - const inpBlock = blocks.getBlock(id); - const fields = blocks.getFields(inpBlock); - Object.assign(hatFields, fields); - } - } - - if (optMatchFields) { - for (const matchField in optMatchFields) { - if (hatFields[matchField].value.toUpperCase() !== - optMatchFields[matchField]) { - // Field mismatch. - return; - } + for (const matchField in optMatchFields) { + if (hatFields[matchField].value !== optMatchFields[matchField]) { + // Field mismatch. + return; } } if (hatMeta.restartExistingThreads) { // If `restartExistingThreads` is true, we should stop // any existing threads starting with the top block. - for (let i = 0; i < instance.threads.length; i++) { - if (instance.threads[i].topBlock === topBlockId && - !instance.threads[i].stackClick && // stack click threads and hat threads can coexist - instance.threads[i].target === target) { - newThreads.push(instance._restartThread(instance.threads[i])); + for (let i = 0; i < this.threads.length; i++) { + if (this.threads[i].target === target && + this.threads[i].topBlock === topBlockId && + // stack click threads and hat threads can coexist + !this.threads[i].stackClick) { + newThreads.push(this._restartThread(this.threads[i])); return; } } } else { // If `restartExistingThreads` is false, we should // give up if any threads with the top block are running. - for (let j = 0; j < instance.threads.length; j++) { - if (instance.threads[j].topBlock === topBlockId && - instance.threads[j].target === target && - !instance.threads[j].stackClick && // stack click threads and hat threads can coexist - instance.threads[j].status !== Thread.STATUS_DONE) { + for (let j = 0; j < this.threads.length; j++) { + if (this.threads[j].target === target && + this.threads[j].topBlock === topBlockId && + // stack click threads and hat threads can coexist + !this.threads[j].stackClick && + this.threads[j].status !== Thread.STATUS_DONE) { // Some thread is already running. return; } } } // Start the thread with this top block. - newThreads.push(instance._pushThread(topBlockId, target)); + newThreads.push(this._pushThread(topBlockId, target)); }, optTarget); // For compatibility with Scratch 2, edge triggered hats need to be processed before // threads are stepped. See ScratchRuntime.as for original implementation From dd797283d65af2dde48beb5b89e7bcfd07336263 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 15 May 2019 11:25:15 -0400 Subject: [PATCH 1764/1971] Merge pull request #1648 from mzgoddard/stop-all-next-tick Stop all next tick --- packages/scratch-vm/src/engine/runtime.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 1f856fafa1..3979b415e2 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1844,8 +1844,12 @@ class Runtime extends EventEmitter { } } this.targets = newTargets; - // Dispose all threads. - this.threads.forEach(thread => this._stopThread(thread)); + // Dispose of the active thread. + if (this.sequencer.activeThread !== null) { + this._stopThread(this.sequencer.activeThread); + } + // Remove all remaining threads from executing in the next tick. + this.threads = []; } /** From 952372e2584a03aaabc39ca3a7d2630b2b7285df Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 20 May 2019 13:19:42 -0400 Subject: [PATCH 1765/1971] Merge pull request #2175 from ericrosenbaum/hotfix/do-not-load-speech2text Do not load speech2text extension --- packages/scratch-vm/src/extension-support/extension-manager.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index bf666a28c3..b27b0d6428 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -20,7 +20,6 @@ const builtinExtensions = { text2speech: () => require('../extensions/scratch3_text2speech'), translate: () => require('../extensions/scratch3_translate'), videoSensing: () => require('../extensions/scratch3_video_sensing'), - speech2text: () => require('../extensions/scratch3_speech2text'), ev3: () => require('../extensions/scratch3_ev3'), makeymakey: () => require('../extensions/scratch3_makeymakey'), boost: () => require('../extensions/scratch3_boost'), From 49af15d23aa3e5698ecb9ce1b84d187bbcb28b1c Mon Sep 17 00:00:00 2001 From: Carmelo Presicce Date: Thu, 23 May 2019 20:22:03 -0400 Subject: [PATCH 1766/1971] synth extension --- packages/scratch-vm/src/extension-support/extension-manager.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index b27b0d6428..6b3913e7e7 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -23,7 +23,8 @@ const builtinExtensions = { ev3: () => require('../extensions/scratch3_ev3'), makeymakey: () => require('../extensions/scratch3_makeymakey'), boost: () => require('../extensions/scratch3_boost'), - gdxfor: () => require('../extensions/scratch3_gdx_for') + gdxfor: () => require('../extensions/scratch3_gdx_for'), + synth: () => require('../extensions/scratch3_synth'), }; /** From 77af7ddaa189f63197de14821077deee0988d762 Mon Sep 17 00:00:00 2001 From: Carmelo Presicce Date: Thu, 23 May 2019 21:26:57 -0400 Subject: [PATCH 1767/1971] lightplay extension stub --- packages/scratch-vm/src/extension-support/extension-manager.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 6b3913e7e7..36b19bdb77 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -25,6 +25,7 @@ const builtinExtensions = { boost: () => require('../extensions/scratch3_boost'), gdxfor: () => require('../extensions/scratch3_gdx_for'), synth: () => require('../extensions/scratch3_synth'), + lightplay: () => require('../extensions/scratch3_lightplay'), }; /** From 0896559ee74eb0d017d4b7ee9047e0cc31929ae3 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 24 May 2019 11:33:03 -0400 Subject: [PATCH 1768/1971] Merge pull request #2181 from kchadha/revert-unwanted-changes Revert unwanted changes --- .../scratch-vm/src/extension-support/extension-manager.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 36b19bdb77..b27b0d6428 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -23,9 +23,7 @@ const builtinExtensions = { ev3: () => require('../extensions/scratch3_ev3'), makeymakey: () => require('../extensions/scratch3_makeymakey'), boost: () => require('../extensions/scratch3_boost'), - gdxfor: () => require('../extensions/scratch3_gdx_for'), - synth: () => require('../extensions/scratch3_synth'), - lightplay: () => require('../extensions/scratch3_lightplay'), + gdxfor: () => require('../extensions/scratch3_gdx_for') }; /** From 0bf7e1606d3825a05c073083f56d8da9cb0b732d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 4 Jun 2019 10:11:22 -0400 Subject: [PATCH 1769/1971] Merge pull request #2182 from paulkaplan/scratch-link-socket Initial prototype of configurable scratch link socket --- packages/scratch-vm/src/engine/runtime.js | 29 ++++++++++++++++++++++ packages/scratch-vm/src/virtual-machine.js | 8 ++++++ 2 files changed, 37 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 3979b415e2..15f59556ac 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -16,6 +16,7 @@ const maybeFormatMessage = require('../util/maybe-format-message'); const StageLayering = require('./stage-layering'); const Variable = require('./variable'); const xmlEscape = require('../util/xml-escape'); +const ScratchLinkWebSocket = require('../util/scratch-link-websocket'); // Virtual I/O devices. const Clock = require('../io/clock'); @@ -1289,6 +1290,34 @@ class Runtime extends EventEmitter { (result, categoryInfo) => result.concat(categoryInfo.blocks.map(blockInfo => blockInfo.json)), []); } + /** + * Get a scratch link socket. + * @param {string} type Either BLE or BT + * @returns {ScratchLinkSocket} The scratch link socket. + */ + getScratchLinkSocket (type) { + const factory = this._linkSocketFactory || this._defaultScratchLinkSocketFactory; + return factory(type); + } + + /** + * Configure how ScratchLink sockets are created. Factory must consume a "type" parameter + * either BT or BLE. + * @param {Function} factory The new factory for creating ScratchLink sockets. + */ + configureScratchLinkSocketFactory (factory) { + this._linkSocketFactory = factory; + } + + /** + * The default scratch link socket creator, using websockets to the installed device manager. + * @param {string} type Either BLE or BT + * @returns {ScratchLinkSocket} The new scratch link socket (a WebSocket object) + */ + _defaultScratchLinkSocketFactory (type) { + return new ScratchLinkWebSocket(type); + } + /** * Register an extension that communications with a hardware peripheral by id, * to have access to it and its peripheral functions in the future. diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 3320238f4e..deb1471e1c 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -1525,6 +1525,14 @@ class VirtualMachine extends EventEmitter { } return null; } + + /** + * Allow VM consumer to configure the ScratchLink socket creator. + * @param {Function} factory The custom ScratchLink socket factory. + */ + configureScratchLinkSocketFactory (factory) { + this.runtime.configureScratchLinkSocketFactory(factory); + } } module.exports = VirtualMachine; From 1e71c6e3eaf9c6e81295763aedb5853082a380c9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 6 Jun 2019 14:05:44 -0400 Subject: [PATCH 1770/1971] Merge pull request #2197 from ericrosenbaum/bugfix/update-sound-duration Update sound duration --- packages/scratch-vm/src/virtual-machine.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index deb1471e1c..c53be8a22c 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -795,6 +795,8 @@ class VirtualMachine extends EventEmitter { sound.assetId = sound.asset.assetId; sound.dataFormat = storage.DataFormat.WAV; sound.md5 = `${sound.assetId}.${sound.dataFormat}`; + sound.sampleCount = newBuffer.length; + sound.rate = newBuffer.sampleRate; } // If soundEncoding is null, it's because gui had a problem // encoding the updated sound. We don't want to store anything in this From 86a54be1e8e84f82a3e377c1295b1956710e9430 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Tue, 18 Jun 2019 18:53:20 -0400 Subject: [PATCH 1771/1971] Merge pull request #2161 from LLK/e16n Supporting VM changes for extensionification --- packages/scratch-vm/src/engine/runtime.js | 89 ++++++++++++------- .../extension-support/extension-manager.js | 37 +++++--- packages/scratch-vm/src/virtual-machine.js | 11 ++- 3 files changed, 89 insertions(+), 48 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 15f59556ac..792455c914 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -42,6 +42,8 @@ const defaultBlockPackages = { scratch3_procedures: require('../blocks/scratch3_procedures') }; +const defaultExtensionColors = ['#0FBD8C', '#0DA57A', '#0B8E69']; + /** * Information used for converting Scratch argument types into scratch-blocks data. * @type {object.} @@ -509,6 +511,14 @@ class Runtime extends EventEmitter { return 'PROJECT_CHANGED'; } + /** + * Event name for report that a change was made to an extension in the toolbox. + * @const {string} + */ + static get TOOLBOX_EXTENSIONS_NEED_UPDATE () { + return 'TOOLBOX_EXTENSIONS_NEED_UPDATE'; + } + /** * Event name for targets update report. * @const {string} @@ -777,23 +787,28 @@ class Runtime extends EventEmitter { showStatusButton: extensionInfo.showStatusButton, blockIconURI: extensionInfo.blockIconURI, menuIconURI: extensionInfo.menuIconURI, - color1: extensionInfo.colour || '#0FBD8C', - color2: extensionInfo.colourSecondary || '#0DA57A', - color3: extensionInfo.colourTertiary || '#0B8E69', customFieldTypes: {}, blocks: [], menus: [] }; + if (extensionInfo.color1) { + categoryInfo.color1 = extensionInfo.color1; + categoryInfo.color2 = extensionInfo.color2; + categoryInfo.color3 = extensionInfo.color3; + } else { + categoryInfo.color1 = defaultExtensionColors[0]; + categoryInfo.color2 = defaultExtensionColors[1]; + categoryInfo.color3 = defaultExtensionColors[2]; + } + this._blockInfo.push(categoryInfo); this._fillExtensionCategory(categoryInfo, extensionInfo); - const fieldTypeDefinitionsForScratch = []; for (const fieldTypeName in categoryInfo.customFieldTypes) { if (extensionInfo.customFieldTypes.hasOwnProperty(fieldTypeName)) { const fieldTypeInfo = categoryInfo.customFieldTypes[fieldTypeName]; - fieldTypeDefinitionsForScratch.push(fieldTypeInfo.scratchBlocksDefinition); // Emit events for custom field types from extension this.emit(Runtime.EXTENSION_FIELD_ADDED, { @@ -803,9 +818,7 @@ class Runtime extends EventEmitter { } } - const allBlocks = fieldTypeDefinitionsForScratch.concat(categoryInfo.blocks).concat(categoryInfo.menus); - - this.emit(Runtime.EXTENSION_ADDED, allBlocks); + this.emit(Runtime.EXTENSION_ADDED, categoryInfo); } /** @@ -814,18 +827,15 @@ class Runtime extends EventEmitter { * @private */ _refreshExtensionPrimitives (extensionInfo) { - let extensionBlocks = []; - for (const categoryInfo of this._blockInfo) { - if (extensionInfo.id === categoryInfo.id) { - categoryInfo.name = maybeFormatMessage(extensionInfo.name); - categoryInfo.blocks = []; - categoryInfo.menus = []; - this._fillExtensionCategory(categoryInfo, extensionInfo); - extensionBlocks = extensionBlocks.concat(categoryInfo.blocks, categoryInfo.menus); - } - } + const categoryInfo = this._blockInfo.find(info => info.id === extensionInfo.id); + if (categoryInfo) { + categoryInfo.name = maybeFormatMessage(extensionInfo.name); + categoryInfo.blocks = []; + categoryInfo.menus = []; + this._fillExtensionCategory(categoryInfo, extensionInfo); - this.emit(Runtime.BLOCKSINFO_UPDATE, extensionBlocks); + this.emit(Runtime.BLOCKSINFO_UPDATE, categoryInfo); + } } /** @@ -1011,8 +1021,7 @@ class Runtime extends EventEmitter { category: categoryInfo.name, colour: categoryInfo.color1, colourSecondary: categoryInfo.color2, - colourTertiary: categoryInfo.color3, - extensions: ['scratch_extension'] + colourTertiary: categoryInfo.color3 }; const context = { // TODO: store this somewhere so that we can map args appropriately after translation. @@ -1032,6 +1041,7 @@ class Runtime extends EventEmitter { const iconURI = blockInfo.blockIconURI || categoryInfo.blockIconURI; if (iconURI) { + blockJSON.extensions = ['scratch_extension']; blockJSON.message0 = '%1 %2'; const iconJSON = { type: 'field_image', @@ -1135,7 +1145,9 @@ class Runtime extends EventEmitter { ++outLineNum; } - const blockXML = `${context.inputList.join('')}`; + const mutation = blockInfo.isDynamic ? `` : ''; + const inputs = context.inputList.join(''); + const blockXML = `${mutation}${inputs}`; return { info: context.blockInfo, @@ -1249,11 +1261,12 @@ class Runtime extends EventEmitter { } /** - * @returns {string} scratch-blocks XML description for all dynamic blocks, wrapped in elements. + * @returns {Array.} scratch-blocks XML for each category of extension blocks, in category order. + * @property {string} id - the category / extension ID + * @property {string} xml - the XML text for this category, starting with `` and ending with `` */ getBlocksXML () { - const xmlParts = []; - for (const categoryInfo of this._blockInfo) { + return this._blockInfo.map(categoryInfo => { const {name, color1, color2} = categoryInfo; const paletteBlocks = categoryInfo.blocks.filter(block => !block.info.hideFromPalette); const colorXML = `colour="${color1}" secondaryColour="${color2}"`; @@ -1274,12 +1287,12 @@ class Runtime extends EventEmitter { statusButtonXML = 'showStatusButton="true"'; } - xmlParts.push(``); - xmlParts.push.apply(xmlParts, paletteBlocks.map(block => block.xml)); - xmlParts.push(''); - } - return xmlParts.join('\n'); + return { + id: categoryInfo.id, + xml: `${ + paletteBlocks.map(block => block.xml).join('')}` + }; + }); } /** @@ -1981,10 +1994,15 @@ class Runtime extends EventEmitter { * @param {!Target} editingTarget New editing target. */ setEditingTarget (editingTarget) { + const oldEditingTarget = this._editingTarget; this._editingTarget = editingTarget; // Script glows must be cleared. this._scriptGlowsPreviousFrame = []; this._updateGlows(); + + if (oldEditingTarget !== this._editingTarget) { + this.requestToolboxExtensionsUpdate(); + } } /** @@ -2402,12 +2420,19 @@ class Runtime extends EventEmitter { } /** - * Emit an event that indicate that the blocks on the workspace need updating. + * Emit an event that indicates that the blocks on the workspace need updating. */ requestBlocksUpdate () { this.emit(Runtime.BLOCKS_NEED_UPDATE); } + /** + * Emit an event that indicates that the toolbox extension blocks need updating. + */ + requestToolboxExtensionsUpdate () { + this.emit(Runtime.TOOLBOX_EXTENSIONS_NEED_UPDATE); + } + /** * Set up timers to repeatedly step in a browser. */ diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index b27b0d6428..71eb5f1269 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -389,27 +389,40 @@ class ExtensionManager { log.warn(`Ignoring opcode "${blockInfo.opcode}" for button with text: ${blockInfo.text}`); } break; - default: + default: { if (!blockInfo.opcode) { throw new Error('Missing opcode for block'); } - blockInfo.func = blockInfo.func ? this._sanitizeID(blockInfo.func) : blockInfo.opcode; + const funcName = blockInfo.func ? this._sanitizeID(blockInfo.func) : blockInfo.opcode; - // Avoid promise overhead if possible - if (dispatch._isRemoteService(serviceName)) { - blockInfo.func = dispatch.call.bind(dispatch, serviceName, blockInfo.func); - } else { + const getBlockInfo = blockInfo.isDynamic ? + args => args && args.mutation && args.mutation.blockInfo : + () => blockInfo; + const callBlockFunc = (() => { + if (dispatch._isRemoteService(serviceName)) { + return (args, util, realBlockInfo) => + dispatch.call(serviceName, funcName, args, util, realBlockInfo); + } + + // avoid promise latency if we can call direct const serviceObject = dispatch.services[serviceName]; - const func = serviceObject[blockInfo.func]; - if (func) { - blockInfo.func = func.bind(serviceObject); - } else { - throw new Error(`Could not find extension block function called ${blockInfo.func}`); + if (!serviceObject[funcName]) { + // The function might show up later as a dynamic property of the service object + log.warn(`Could not find extension block function called ${funcName}`); } - } + return (args, util, realBlockInfo) => + serviceObject[funcName](args, util, realBlockInfo); + })(); + + blockInfo.func = (args, util) => { + const realBlockInfo = getBlockInfo(args); + // TODO: filter args using the keys of realBlockInfo.arguments? maybe only if sandboxed? + return callBlockFunc(args, util, realBlockInfo); + }; break; } + } return blockInfo; } diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index c53be8a22c..ea26476763 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -109,18 +109,21 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.BLOCK_DRAG_END, (blocks, topBlockId) => { this.emit(Runtime.BLOCK_DRAG_END, blocks, topBlockId); }); - this.runtime.on(Runtime.EXTENSION_ADDED, blocksInfo => { - this.emit(Runtime.EXTENSION_ADDED, blocksInfo); + this.runtime.on(Runtime.EXTENSION_ADDED, categoryInfo => { + this.emit(Runtime.EXTENSION_ADDED, categoryInfo); }); this.runtime.on(Runtime.EXTENSION_FIELD_ADDED, (fieldName, fieldImplementation) => { this.emit(Runtime.EXTENSION_FIELD_ADDED, fieldName, fieldImplementation); }); - this.runtime.on(Runtime.BLOCKSINFO_UPDATE, blocksInfo => { - this.emit(Runtime.BLOCKSINFO_UPDATE, blocksInfo); + this.runtime.on(Runtime.BLOCKSINFO_UPDATE, categoryInfo => { + this.emit(Runtime.BLOCKSINFO_UPDATE, categoryInfo); }); this.runtime.on(Runtime.BLOCKS_NEED_UPDATE, () => { this.emitWorkspaceUpdate(); }); + this.runtime.on(Runtime.TOOLBOX_EXTENSIONS_NEED_UPDATE, () => { + this.extensionManager.refreshBlocks(); + }); this.runtime.on(Runtime.PERIPHERAL_LIST_UPDATE, info => { this.emit(Runtime.PERIPHERAL_LIST_UPDATE, info); }); From c84b1399d602e247f36afdd988aae8c53678d48e Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Tue, 18 Jun 2019 21:18:10 -0700 Subject: [PATCH 1772/1971] Merge pull request #2143 from cwillisf/non-droppable-extension-menus support non-droppable menus in extensions --- packages/scratch-vm/src/engine/runtime.js | 103 ++++++++++++------ .../extension-support/extension-manager.js | 35 +++--- 2 files changed, 91 insertions(+), 47 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 792455c914..a3976e0e31 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -786,10 +786,7 @@ class Runtime extends EventEmitter { name: maybeFormatMessage(extensionInfo.name), showStatusButton: extensionInfo.showStatusButton, blockIconURI: extensionInfo.blockIconURI, - menuIconURI: extensionInfo.menuIconURI, - customFieldTypes: {}, - blocks: [], - menus: [] + menuIconURI: extensionInfo.menuIconURI }; if (extensionInfo.color1) { @@ -830,8 +827,6 @@ class Runtime extends EventEmitter { const categoryInfo = this._blockInfo.find(info => info.id === extensionInfo.id); if (categoryInfo) { categoryInfo.name = maybeFormatMessage(extensionInfo.name); - categoryInfo.blocks = []; - categoryInfo.menus = []; this._fillExtensionCategory(categoryInfo, extensionInfo); this.emit(Runtime.BLOCKSINFO_UPDATE, categoryInfo); @@ -839,18 +834,24 @@ class Runtime extends EventEmitter { } /** - * Read extension information, convert menus, blocks and custom field types + * Read extension information, convert menus, blocks and custom field types * and store the results in the provided category object. * @param {CategoryInfo} categoryInfo - the category to be filled * @param {ExtensionMetadata} extensionInfo - the extension metadata to read * @private */ _fillExtensionCategory (categoryInfo, extensionInfo) { + categoryInfo.blocks = []; + categoryInfo.customFieldTypes = {}; + categoryInfo.menus = []; + categoryInfo.menuInfo = {}; + for (const menuName in extensionInfo.menus) { if (extensionInfo.menus.hasOwnProperty(menuName)) { - const menuItems = extensionInfo.menus[menuName]; - const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuItems, categoryInfo); + const menuInfo = extensionInfo.menus[menuName]; + const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuInfo, categoryInfo); categoryInfo.menus.push(convertedMenu); + categoryInfo.menuInfo[menuName] = menuInfo; } } for (const fieldTypeName in extensionInfo.customFieldTypes) { @@ -890,21 +891,16 @@ class Runtime extends EventEmitter { } /** - * Build the scratch-blocks JSON for a menu. Note that scratch-blocks treats menus as a special kind of block. - * @param {string} menuName - the name of the menu - * @param {array} menuItems - the list of items for this menu - * @param {CategoryInfo} categoryInfo - the category for this block - * @returns {object} - a JSON-esque object ready for scratch-blocks' consumption + * Convert the given extension menu items into the scratch-blocks style of list of pairs. + * If the menu is dynamic (e.g. the passed in argument is a function), return the input unmodified. + * @param {object} menuItems - an array of menu items or a function to retrieve such an array + * @returns {object} - an array of 2 element arrays or the original input function * @private */ - _buildMenuForScratchBlocks (menuName, menuItems, categoryInfo) { - const menuId = this._makeExtensionMenuId(menuName, categoryInfo.id); - let options = null; - if (typeof menuItems === 'function') { - options = menuItems; - } else { + _convertMenuItems (menuItems) { + if (typeof menuItems !== 'function') { const extensionMessageContext = this.makeMessageContextForTarget(); - options = menuItems.map(item => { + return menuItems.map(item => { const formattedItem = maybeFormatMessage(item, extensionMessageContext); switch (typeof formattedItem) { case 'string': @@ -916,6 +912,22 @@ class Runtime extends EventEmitter { } }); } + return menuItems; + } + + /** + * Build the scratch-blocks JSON for a menu. Note that scratch-blocks treats menus as a special kind of block. + * @param {string} menuName - the name of the menu + * @param {object} menuInfo - a description of this menu and its items + * @property {*} items - an array of menu items or a function to retrieve such an array + * @property {boolean} [acceptReporters] - if true, allow dropping reporters onto this menu + * @param {CategoryInfo} categoryInfo - the category for this block + * @returns {object} - a JSON-esque object ready for scratch-blocks' consumption + * @private + */ + _buildMenuForScratchBlocks (menuName, menuInfo, categoryInfo) { + const menuId = this._makeExtensionMenuId(menuName, categoryInfo.id); + const menuItems = this._convertMenuItems(menuInfo.items); return { json: { message0: '%1', @@ -925,12 +937,13 @@ class Runtime extends EventEmitter { colour: categoryInfo.color1, colourSecondary: categoryInfo.color2, colourTertiary: categoryInfo.color3, - outputShape: ScratchBlocksConstants.OUTPUT_SHAPE_ROUND, + outputShape: menuInfo.acceptReporters ? + ScratchBlocksConstants.OUTPUT_SHAPE_ROUND : ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE, args0: [ { type: 'field_dropdown', name: menuName, - options: options + options: menuItems } ] } @@ -1227,29 +1240,51 @@ class Runtime extends EventEmitter { argJSON.check = argTypeInfo.check; } - const shadowType = (argInfo.menu ? - this._makeExtensionMenuId(argInfo.menu, context.categoryInfo.id) : - argTypeInfo.shadowType); - const fieldType = argInfo.menu || argTypeInfo.fieldType; + let valueName; + let shadowType; + let fieldName; + if (argInfo.menu) { + const menuInfo = context.categoryInfo.menuInfo[argInfo.menu]; + if (menuInfo.acceptReporters) { + valueName = placeholder; + shadowType = this._makeExtensionMenuId(argInfo.menu, context.categoryInfo.id); + fieldName = argInfo.menu; + } else { + argJSON.type = 'field_dropdown'; + argJSON.options = this._convertMenuItems(menuInfo.items); + valueName = null; + shadowType = null; + fieldName = placeholder; + } + } else { + valueName = placeholder; + shadowType = argTypeInfo.shadowType; + fieldName = argTypeInfo.fieldType; + } // is the ScratchBlocks name for a block input. - context.inputList.push(``); + if (valueName) { + context.inputList.push(``); + } // The is a placeholder for a reporter and is visible when there's no reporter in this input. // Boolean inputs don't need to specify a shadow in the XML. if (shadowType) { context.inputList.push(``); + } - // is a text field that the user can type into. Some shadows, like the color picker, don't allow - // text input and therefore don't need a field element. - if (fieldType) { - context.inputList.push(`${defaultValue}`); - } + // A displays a dynamic value: a user-editable text field, a drop-down menu, etc. + if (fieldName) { + context.inputList.push(`${defaultValue}`); + } + if (shadowType) { context.inputList.push(''); } - context.inputList.push(''); + if (valueName) { + context.inputList.push(''); + } const argsName = `args${context.outLineNum}`; const blockArgs = (context.blockJSON[argsName] = context.blockJSON[argsName] || []); diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 71eb5f1269..7cb556c5d0 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -294,7 +294,7 @@ class ExtensionManager { } return results; }, []); - extensionInfo.menus = extensionInfo.menus || []; + extensionInfo.menus = extensionInfo.menus || {}; extensionInfo.menus = this._prepareMenuInfo(serviceName, extensionInfo.menus); return extensionInfo; } @@ -309,15 +309,24 @@ class ExtensionManager { _prepareMenuInfo (serviceName, menus) { const menuNames = Object.getOwnPropertyNames(menus); for (let i = 0; i < menuNames.length; i++) { - const item = menuNames[i]; - // If the value is a string, it should be the name of a function in the - // extension object to call to populate the menu whenever it is opened. - // Set up the binding for the function object here so - // we can use it later when converting the menu for Scratch Blocks. - if (typeof menus[item] === 'string') { + const menuName = menuNames[i]; + let menuInfo = menus[menuName]; + + // If the menu description is in short form (items only) then normalize it to general form: an object with + // its items listed in an `items` property. + if (!menuInfo.items) { + menuInfo = { + items: menuInfo + }; + menus[menuName] = menuInfo; + } + // If `items` is a string, it should be the name of a function in the extension object. Calling the + // function should return an array of items to populate the menu when it is opened. + if (typeof menuInfo.items === 'string') { + const menuItemFunctionName = menuInfo.items; const serviceObject = dispatch.services[serviceName]; - const menuName = menus[item]; - menus[item] = this._getExtensionMenuItems.bind(this, serviceObject, menuName); + // Bind the function here so we can pass a simple item generation function to Scratch Blocks later. + menuInfo.items = this._getExtensionMenuItems.bind(this, serviceObject, menuItemFunctionName); } } return menus; @@ -326,11 +335,11 @@ class ExtensionManager { /** * Fetch the items for a particular extension menu, providing the target ID for context. * @param {object} extensionObject - the extension object providing the menu. - * @param {string} menuName - the name of the menu function to call. + * @param {string} menuItemFunctionName - the name of the menu function to call. * @returns {Array} menu items ready for scratch-blocks. * @private */ - _getExtensionMenuItems (extensionObject, menuName) { + _getExtensionMenuItems (extensionObject, menuItemFunctionName) { // Fetch the items appropriate for the target currently being edited. This assumes that menus only // collect items when opened by the user while editing a particular target. const editingTarget = this.runtime.getEditingTarget() || this.runtime.getTargetForStage(); @@ -338,7 +347,7 @@ class ExtensionManager { const extensionMessageContext = this.runtime.makeMessageContextForTarget(editingTarget); // TODO: Fix this to use dispatch.call when extensions are running in workers. - const menuFunc = extensionObject[menuName]; + const menuFunc = extensionObject[menuItemFunctionName]; const menuItems = menuFunc.call(extensionObject, editingTargetID).map( item => { item = maybeFormatMessage(item, extensionMessageContext); @@ -356,7 +365,7 @@ class ExtensionManager { }); if (!menuItems || menuItems.length < 1) { - throw new Error(`Extension menu returned no items: ${menuName}`); + throw new Error(`Extension menu returned no items: ${menuItemFunctionName}`); } return menuItems; } From a0926a69b9c1d3d3ba5d1295b2504301d6f8b9a0 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 17 Jul 2019 16:12:37 -0400 Subject: [PATCH 1773/1971] Merge pull request #2145 from mzgoddard/raise-params Raise params to the next frame when pushing --- packages/scratch-vm/src/engine/runtime.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index a3976e0e31..57961940e0 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -724,7 +724,7 @@ class Runtime extends EventEmitter { if (packageObject.getPrimitives) { const packagePrimitives = packageObject.getPrimitives(); for (const op in packagePrimitives) { - if (packagePrimitives.hasOwnProperty(op)) { + if (typeof packagePrimitives[op] === 'function') { this._primitives[op] = packagePrimitives[op].bind(packageObject); } @@ -1567,7 +1567,7 @@ class Runtime extends EventEmitter { isActiveThread (thread) { return ( ( - thread.stack.length > 0 && + thread.stackFrame !== null && thread.status !== Thread.STATUS_DONE) && this.threads.indexOf(thread) > -1); } @@ -1744,11 +1744,20 @@ class Runtime extends EventEmitter { // Start the thread with this top block. newThreads.push(this._pushThread(topBlockId, target)); }, optTarget); - // For compatibility with Scratch 2, edge triggered hats need to be processed before - // threads are stepped. See ScratchRuntime.as for original implementation + // For compatibility with Scratch 2, edge triggered hats need to be + // processed before threads are stepped. See ScratchRuntime.as for + // original implementation. + // + // TODO: Move the execute call to sequencer. Maybe in a method call + // stepHat or stepOne. newThreads.forEach(thread => { execute(this.sequencer, thread); - thread.goToNextBlock(); + if (thread.status !== Thread.STATUS_DONE) { + thread.goToNextBlock(); + if (thread.stackFrame === null) { + this.sequencer.retireThread(thread); + } + } }); return newThreads; } From 3ec822904b45f951b0e8ba9bbf4ac567d19271d1 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 22 Jul 2019 13:30:28 -0400 Subject: [PATCH 1774/1971] Merge pull request #2240 from LLK/revert-2145-raise-params Revert "Raise params to the next frame when pushing" --- packages/scratch-vm/src/engine/runtime.js | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 57961940e0..a3976e0e31 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -724,7 +724,7 @@ class Runtime extends EventEmitter { if (packageObject.getPrimitives) { const packagePrimitives = packageObject.getPrimitives(); for (const op in packagePrimitives) { - if (typeof packagePrimitives[op] === 'function') { + if (packagePrimitives.hasOwnProperty(op)) { this._primitives[op] = packagePrimitives[op].bind(packageObject); } @@ -1567,7 +1567,7 @@ class Runtime extends EventEmitter { isActiveThread (thread) { return ( ( - thread.stackFrame !== null && + thread.stack.length > 0 && thread.status !== Thread.STATUS_DONE) && this.threads.indexOf(thread) > -1); } @@ -1744,20 +1744,11 @@ class Runtime extends EventEmitter { // Start the thread with this top block. newThreads.push(this._pushThread(topBlockId, target)); }, optTarget); - // For compatibility with Scratch 2, edge triggered hats need to be - // processed before threads are stepped. See ScratchRuntime.as for - // original implementation. - // - // TODO: Move the execute call to sequencer. Maybe in a method call - // stepHat or stepOne. + // For compatibility with Scratch 2, edge triggered hats need to be processed before + // threads are stepped. See ScratchRuntime.as for original implementation newThreads.forEach(thread => { execute(this.sequencer, thread); - if (thread.status !== Thread.STATUS_DONE) { - thread.goToNextBlock(); - if (thread.stackFrame === null) { - this.sequencer.retireThread(thread); - } - } + thread.goToNextBlock(); }); return newThreads; } From 35fc42c5e64abfb5e538c060333d98b971468246 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Tue, 13 Aug 2019 15:22:10 -0400 Subject: [PATCH 1775/1971] Merge pull request #2252 from chrisgarrity/custom-mimetype Add custom mime-type to exported sb3 and sprite --- packages/scratch-vm/src/virtual-machine.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index ea26476763..4fbd202815 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -385,6 +385,7 @@ class VirtualMachine extends EventEmitter { return zip.generateAsync({ type: 'blob', + mimeType: 'application/x.scratch.sb3', compression: 'DEFLATE', compressionOptions: { level: 6 // Tradeoff between best speed (1) and best compression (9) @@ -435,6 +436,7 @@ class VirtualMachine extends EventEmitter { return zip.generateAsync({ type: typeof optZipType === 'string' ? optZipType : 'blob', + mimeType: 'application/x.scratch.sprite3', compression: 'DEFLATE', compressionOptions: { level: 6 From 588b04a5228832fd51965bcb7e89dc809a5c1ca9 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 18 Oct 2019 10:20:16 -0400 Subject: [PATCH 1776/1971] Merge pull request #2280 from kchadha/inline-images-in-extensions Inline Images in Extensions --- packages/scratch-vm/src/engine/runtime.js | 188 +++++++++++++++------- 1 file changed, 127 insertions(+), 61 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index a3976e0e31..fb072426d5 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -51,30 +51,55 @@ const defaultExtensionColors = ['#0FBD8C', '#0DA57A', '#0B8E69']; const ArgumentTypeMap = (() => { const map = {}; map[ArgumentType.ANGLE] = { - shadowType: 'math_angle', - fieldType: 'NUM' + shadow: { + type: 'math_angle', + // We specify fieldNames here so that we can pick + // create and populate a field with the defaultValue + // specified in the extension. + // When the `fieldName` property is not specified, + // the will be left out of the XML and + // the scratch-blocks defaults for that field will be + // used instead (e.g. default of 0 for number fields) + fieldName: 'NUM' + } }; map[ArgumentType.COLOR] = { - shadowType: 'colour_picker' + shadow: { + type: 'colour_picker', + fieldName: 'COLOUR' + } }; map[ArgumentType.NUMBER] = { - shadowType: 'math_number', - fieldType: 'NUM' + shadow: { + type: 'math_number', + fieldName: 'NUM' + } }; map[ArgumentType.STRING] = { - shadowType: 'text', - fieldType: 'TEXT' + shadow: { + type: 'text', + fieldName: 'TEXT' + } }; map[ArgumentType.BOOLEAN] = { check: 'Boolean' }; map[ArgumentType.MATRIX] = { - shadowType: 'matrix', - fieldType: 'MATRIX' + shadow: { + type: 'matrix', + fieldName: 'MATRIX' + } }; map[ArgumentType.NOTE] = { - shadowType: 'note', - fieldType: 'NOTE' + shadow: { + type: 'note', + fieldName: 'NOTE' + } + }; + map[ArgumentType.IMAGE] = { + // Inline images are weird because they're not actually "arguments". + // They are more analagous to the label on a block. + fieldType: 'field_image' }; return map; })(); @@ -1152,7 +1177,7 @@ class Runtime extends EventEmitter { src: './static/blocks-media/repeat.svg', // TODO: use a constant or make this configurable? width: 24, height: 24, - alt: '*', + alt: '*', // TODO remove this since we don't use collapsed blocks in scratch flip_rtl: true }]; ++outLineNum; @@ -1206,6 +1231,29 @@ class Runtime extends EventEmitter { }; } + /** + * Helper for _convertPlaceholdes which handles inline images which are a specialized case of block "arguments". + * @param {object} argInfo Metadata about the inline image as specified by the extension + * @return {object} JSON blob for a scratch-blocks image field. + * @private + */ + _constructInlineImageJson (argInfo) { + if (!argInfo.dataURI) { + log.warn('Missing data URI in extension block with argument type IMAGE'); + } + return { + type: 'field_image', + src: argInfo.dataURI || '', + // TODO these probably shouldn't be hardcoded...? + width: 24, + height: 24, + // Whether or not the inline image should be flipped horizontally + // in RTL languages. Defaults to false, indicating that the + // image will not be flipped. + flip_rtl: argInfo.flipRTL || false + }; + } + /** * Helper for _convertForScratchBlocks which handles linearization of argument placeholders. Called as a callback * from string#replace. In addition to the return value the JSON and XML items in the context will be filled. @@ -1219,11 +1267,7 @@ class Runtime extends EventEmitter { // Sanitize the placeholder to ensure valid XML placeholder = placeholder.replace(/[<"&]/, '_'); - const argJSON = { - type: 'input_value', - name: placeholder - }; - + // Determine whether the argument type is one of the known standard field types const argInfo = context.blockInfo.arguments[placeholder] || {}; let argTypeInfo = ArgumentTypeMap[argInfo.type] || {}; @@ -1232,63 +1276,85 @@ class Runtime extends EventEmitter { argTypeInfo = context.categoryInfo.customFieldTypes[argInfo.type].argumentTypeInfo; } - const defaultValue = - typeof argInfo.defaultValue === 'undefined' ? '' : - xmlEscape(maybeFormatMessage(argInfo.defaultValue, this.makeMessageContextForTarget()).toString()); + // Start to construct the scratch-blocks style JSON defining how the block should be + // laid out + let argJSON; - if (argTypeInfo.check) { - argJSON.check = argTypeInfo.check; - } + // Most field types are inputs (slots on the block that can have other blocks plugged into them) + // check if this is not one of those cases. E.g. an inline image on a block. + if (argTypeInfo.fieldType === 'field_image') { + argJSON = this._constructInlineImageJson(argInfo); + } else { + // Construct input value - let valueName; - let shadowType; - let fieldName; - if (argInfo.menu) { - const menuInfo = context.categoryInfo.menuInfo[argInfo.menu]; - if (menuInfo.acceptReporters) { - valueName = placeholder; - shadowType = this._makeExtensionMenuId(argInfo.menu, context.categoryInfo.id); - fieldName = argInfo.menu; + // Layout a block argument (e.g. an input slot on the block) + argJSON = { + type: 'input_value', + name: placeholder + }; + + const defaultValue = + typeof argInfo.defaultValue === 'undefined' ? '' : + xmlEscape(maybeFormatMessage(argInfo.defaultValue, this.makeMessageContextForTarget()).toString()); + + if (argTypeInfo.check) { + // Right now the only type of 'check' we have specifies that the + // input slot on the block accepts Boolean reporters, so it should be + // shaped like a hexagon + argJSON.check = argTypeInfo.check; + } + + let valueName; + let shadowType; + let fieldName; + if (argInfo.menu) { + const menuInfo = context.categoryInfo.menuInfo[argInfo.menu]; + if (menuInfo.acceptReporters) { + valueName = placeholder; + shadowType = this._makeExtensionMenuId(argInfo.menu, context.categoryInfo.id); + fieldName = argInfo.menu; + } else { + argJSON.type = 'field_dropdown'; + argJSON.options = this._convertMenuItems(menuInfo.items); + valueName = null; + shadowType = null; + fieldName = placeholder; + } } else { - argJSON.type = 'field_dropdown'; - argJSON.options = this._convertMenuItems(menuInfo.items); - valueName = null; - shadowType = null; - fieldName = placeholder; + valueName = placeholder; + shadowType = (argTypeInfo.shadow && argTypeInfo.shadow.type) || null; + fieldName = (argTypeInfo.shadow && argTypeInfo.shadow.fieldName) || null; } - } else { - valueName = placeholder; - shadowType = argTypeInfo.shadowType; - fieldName = argTypeInfo.fieldType; - } - // is the ScratchBlocks name for a block input. - if (valueName) { - context.inputList.push(``); - } + // is the ScratchBlocks name for a block input. + if (valueName) { + context.inputList.push(``); + } - // The is a placeholder for a reporter and is visible when there's no reporter in this input. - // Boolean inputs don't need to specify a shadow in the XML. - if (shadowType) { - context.inputList.push(``); - } + // The is a placeholder for a reporter and is visible when there's no reporter in this input. + // Boolean inputs don't need to specify a shadow in the XML. + if (shadowType) { + context.inputList.push(``); + } - // A displays a dynamic value: a user-editable text field, a drop-down menu, etc. - if (fieldName) { - context.inputList.push(`${defaultValue}`); - } + // A displays a dynamic value: a user-editable text field, a drop-down menu, etc. + // Leave out the field if defaultValue or fieldName are not specified + if (defaultValue && fieldName) { + context.inputList.push(`${defaultValue}`); + } - if (shadowType) { - context.inputList.push(''); - } + if (shadowType) { + context.inputList.push(''); + } - if (valueName) { - context.inputList.push(''); + if (valueName) { + context.inputList.push(''); + } } const argsName = `args${context.outLineNum}`; const blockArgs = (context.blockJSON[argsName] = context.blockJSON[argsName] || []); - blockArgs.push(argJSON); + if (argJSON) blockArgs.push(argJSON); const argNum = blockArgs.length; context.argsMap[placeholder] = argNum; From a8873abb9e72927510b8182bfdc2bd3f7af942f5 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 19 Nov 2019 15:33:21 -0500 Subject: [PATCH 1777/1971] Merge pull request #2203 from mzgoddard/render-update-drawable Use new updateDrawable* methods --- .../scratch-vm/src/sprites/rendered-target.js | 77 ++++++++----------- 1 file changed, 30 insertions(+), 47 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 12a51f2abc..018f7cd16b 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -274,9 +274,7 @@ class RenderedTarget extends Target { this.x = position[0]; this.y = position[1]; - this.renderer.updateDrawableProperties(this.drawableID, { - position: position - }); + this.renderer.updateDrawablePosition(this.drawableID, position); if (this.visible) { this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); @@ -323,11 +321,8 @@ class RenderedTarget extends Target { // Keep direction between -179 and +180. this.direction = MathUtil.wrapClamp(direction, -179, 180); if (this.renderer) { - const renderedDirectionScale = this._getRenderedDirectionAndScale(); - this.renderer.updateDrawableProperties(this.drawableID, { - direction: renderedDirectionScale.direction, - scale: renderedDirectionScale.scale - }); + const {direction: renderedDirection, scale} = this._getRenderedDirectionAndScale(); + this.renderer.updateDrawableDirectionScale(this.drawableID, renderedDirection, scale); if (this.visible) { this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); @@ -373,9 +368,7 @@ class RenderedTarget extends Target { } this.visible = !!visible; if (this.renderer) { - this.renderer.updateDrawableProperties(this.drawableID, { - visible: this.visible - }); + this.renderer.updateDrawableVisible(this.drawableID, this.visible); if (this.visible) { this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); @@ -404,11 +397,8 @@ class RenderedTarget extends Target { (1.5 * this.runtime.constructor.STAGE_HEIGHT) / origH ); this.size = MathUtil.clamp(size / 100, minScale, maxScale) * 100; - const renderedDirectionScale = this._getRenderedDirectionAndScale(); - this.renderer.updateDrawableProperties(this.drawableID, { - direction: renderedDirectionScale.direction, - scale: renderedDirectionScale.scale - }); + const {direction, scale} = this._getRenderedDirectionAndScale(); + this.renderer.updateDrawableDirectionScale(this.drawableID, direction, scale); if (this.visible) { this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); @@ -426,9 +416,7 @@ class RenderedTarget extends Target { if (!this.effects.hasOwnProperty(effectName)) return; this.effects[effectName] = value; if (this.renderer) { - const props = {}; - props[effectName] = this.effects[effectName]; - this.renderer.updateDrawableProperties(this.drawableID, props); + this.renderer.updateDrawableEffect(this.drawableID, effectName, value); if (this.visible) { this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); @@ -445,7 +433,10 @@ class RenderedTarget extends Target { this.effects[effectName] = 0; } if (this.renderer) { - this.renderer.updateDrawableProperties(this.drawableID, this.effects); + for (const effectName in this.effects) { + if (!this.effects.hasOwnProperty(effectName)) continue; + this.renderer.updateDrawableEffect(this.drawableID, effectName, 0); + } if (this.visible) { this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); @@ -467,21 +458,20 @@ class RenderedTarget extends Target { ); if (this.renderer) { const costume = this.getCostumes()[this.currentCostume]; - const drawableProperties = { - skinId: costume.skinId, - costumeResolution: costume.bitmapResolution - }; if ( typeof costume.rotationCenterX !== 'undefined' && typeof costume.rotationCenterY !== 'undefined' ) { const scale = costume.bitmapResolution || 2; - drawableProperties.rotationCenter = [ + const rotationCenter = [ costume.rotationCenterX / scale, costume.rotationCenterY / scale ]; + this.renderer.updateDrawableSkinIdRotationCenter(this.drawableID, costume.skinId, rotationCenter); + } else { + this.renderer.updateDrawableSkinId(this.drawableID, costume.skinId); } - this.renderer.updateDrawableProperties(this.drawableID, drawableProperties); + if (this.visible) { this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); @@ -618,11 +608,8 @@ class RenderedTarget extends Target { this.rotationStyle = RenderedTarget.ROTATION_STYLE_LEFT_RIGHT; } if (this.renderer) { - const renderedDirectionScale = this._getRenderedDirectionAndScale(); - this.renderer.updateDrawableProperties(this.drawableID, { - direction: renderedDirectionScale.direction, - scale: renderedDirectionScale.scale - }); + const {direction, scale} = this._getRenderedDirectionAndScale(); + this.renderer.updateDrawableDirectionScale(this.drawableID, direction, scale); if (this.visible) { this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); @@ -716,27 +703,23 @@ class RenderedTarget extends Target { */ updateAllDrawableProperties () { if (this.renderer) { - const renderedDirectionScale = this._getRenderedDirectionAndScale(); + const {direction, scale} = this._getRenderedDirectionAndScale(); + this.renderer.updateDrawablePosition(this.drawableID, [this.x, this.y]); + this.renderer.updateDrawableDirectionScale(this.drawableID, direction, scale); + this.renderer.updateDrawableVisible(this.drawableID, this.visible); + const costume = this.getCostumes()[this.currentCostume]; const bitmapResolution = costume.bitmapResolution || 2; - const props = { - position: [this.x, this.y], - direction: renderedDirectionScale.direction, - draggable: this.draggable, - scale: renderedDirectionScale.scale, - visible: this.visible, - skinId: costume.skinId, - costumeResolution: bitmapResolution, - rotationCenter: [ - costume.rotationCenterX / bitmapResolution, - costume.rotationCenterY / bitmapResolution - ] - }; + this.renderer.updateDrawableSkinIdRotationCenter(this.drawableID, costume.skinId, [ + costume.rotationCenterX / bitmapResolution, + costume.rotationCenterY / bitmapResolution + ]); + for (const effectName in this.effects) { if (!this.effects.hasOwnProperty(effectName)) continue; - props[effectName] = this.effects[effectName]; + this.renderer.updateDrawableEffect(this.drawableID, effectName, this.effects[effectName]); } - this.renderer.updateDrawableProperties(this.drawableID, props); + if (this.visible) { this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); this.runtime.requestRedraw(); From b05dd6c70b64d1266eed8a34fe123093090d591b Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 27 Dec 2019 11:44:00 -0500 Subject: [PATCH 1778/1971] Merge pull request #2302 from ErikMejerHansen/develop Fix custom field types --- packages/scratch-vm/src/engine/runtime.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index fb072426d5..908a30821d 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -981,8 +981,10 @@ class Runtime extends EventEmitter { fieldName: fieldName, extendedName: extendedName, argumentTypeInfo: { - shadowType: extendedName, - fieldType: `field_${extendedName}` + shadow: { + type: extendedName, + fieldName: `field_${extendedName}` + } }, scratchBlocksDefinition: this._buildCustomFieldTypeForScratchBlocks( extendedName, From 91edc3785492a5570092ad5ba0580f05e7cb5f7e Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 2 Apr 2020 14:23:02 -0400 Subject: [PATCH 1779/1971] Merge pull request #2314 from adroitwhiz/dont-update-rotation-center Remove calls to updateDrawableSkinIdRotationCenter --- .../scratch-vm/src/sprites/rendered-target.js | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 018f7cd16b..40b774fb29 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -458,19 +458,7 @@ class RenderedTarget extends Target { ); if (this.renderer) { const costume = this.getCostumes()[this.currentCostume]; - if ( - typeof costume.rotationCenterX !== 'undefined' && - typeof costume.rotationCenterY !== 'undefined' - ) { - const scale = costume.bitmapResolution || 2; - const rotationCenter = [ - costume.rotationCenterX / scale, - costume.rotationCenterY / scale - ]; - this.renderer.updateDrawableSkinIdRotationCenter(this.drawableID, costume.skinId, rotationCenter); - } else { - this.renderer.updateDrawableSkinId(this.drawableID, costume.skinId); - } + this.renderer.updateDrawableSkinId(this.drawableID, costume.skinId); if (this.visible) { this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); @@ -709,11 +697,7 @@ class RenderedTarget extends Target { this.renderer.updateDrawableVisible(this.drawableID, this.visible); const costume = this.getCostumes()[this.currentCostume]; - const bitmapResolution = costume.bitmapResolution || 2; - this.renderer.updateDrawableSkinIdRotationCenter(this.drawableID, costume.skinId, [ - costume.rotationCenterX / bitmapResolution, - costume.rotationCenterY / bitmapResolution - ]); + this.renderer.updateDrawableSkinId(this.drawableID, costume.skinId); for (const effectName in this.effects) { if (!this.effects.hasOwnProperty(effectName)) continue; From e1726a4e908aa011c0f3703f63d05e612f5d2a97 Mon Sep 17 00:00:00 2001 From: adroitwhiz Date: Tue, 19 May 2020 15:48:33 -0400 Subject: [PATCH 1780/1971] Merge pull request #2415 from adroitwhiz/no-setsay Remove RenderedTarget.setSay + its test --- .../scratch-vm/src/sprites/rendered-target.js | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 40b774fb29..b0c1ebfed9 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -1,4 +1,3 @@ -const log = require('../util/log'); const MathUtil = require('../util/math-util'); const StringUtil = require('../util/string-util'); const Cast = require('../util/cast'); @@ -341,23 +340,6 @@ class RenderedTarget extends Target { this.runtime.requestTargetsUpdate(this); } - /** - * Set a say bubble. - * @param {?string} type Type of say bubble: "say", "think", or null. - * @param {?string} message Message to put in say bubble. - */ - setSay (type, message) { - if (this.isStage) { - return; - } - // @todo: Render to stage. - if (!type || !message) { - log.info('Clearing say bubble'); - return; - } - log.info('Setting say bubble:', type, message); - } - /** * Set visibility; i.e., whether it's shown or hidden. * @param {!boolean} visible True if should be shown. From b346280bd7ddc6a442c9a1af7cd433cc17eefd16 Mon Sep 17 00:00:00 2001 From: adroitwhiz Date: Tue, 16 Jun 2020 16:01:51 -0400 Subject: [PATCH 1781/1971] Merge pull request #2442 from adroitwhiz/filter-extension-blocks Support extension block filtering per-target --- packages/scratch-vm/src/engine/runtime.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 908a30821d..369c88f9ab 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1365,13 +1365,28 @@ class Runtime extends EventEmitter { /** * @returns {Array.} scratch-blocks XML for each category of extension blocks, in category order. + * @param {?Target} [target] - the active editing target (optional) * @property {string} id - the category / extension ID * @property {string} xml - the XML text for this category, starting with `` and ending with `` */ - getBlocksXML () { + getBlocksXML (target) { return this._blockInfo.map(categoryInfo => { const {name, color1, color2} = categoryInfo; - const paletteBlocks = categoryInfo.blocks.filter(block => !block.info.hideFromPalette); + // Filter out blocks that aren't supposed to be shown on this target, as determined by the block info's + // `hideFromPalette` and `filter` properties. + const paletteBlocks = categoryInfo.blocks.filter(block => { + let blockFilterIncludesTarget = true; + // If an editing target is not passed, include all blocks + // If the block info doesn't include a `filter` property, always include it + if (target && block.info.filter) { + blockFilterIncludesTarget = block.info.filter.includes( + target.isStage ? TargetType.STAGE : TargetType.SPRITE + ); + } + // If the block info's `hideFromPalette` is true, then filter out this block + return blockFilterIncludesTarget && !block.info.hideFromPalette; + }); + const colorXML = `colour="${color1}" secondaryColour="${color2}"`; // Use a menu icon if there is one. Otherwise, use the block icon. If there's no icon, From a5808edf91ca57d23e470283d1c251967e711958 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 22 Jun 2020 10:25:41 -0400 Subject: [PATCH 1782/1971] Merge pull request #2341 from apple502j/patch-9 Clear some stats when disposing runtime --- packages/scratch-vm/src/engine/runtime.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 369c88f9ab..92cd2f6b4e 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -1850,6 +1850,7 @@ class Runtime extends EventEmitter { this.targets.map(this.disposeTarget, this); this._monitorState = OrderedMap({}); this.emit(Runtime.RUNTIME_DISPOSED); + this.ioDevices.clock.resetProjectTimer(); // @todo clear out extensions? turboMode? etc. // *********** Cloud ******************* From fc09fc2817f390402aa5a57c7a29c8d9c8871b6e Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Fri, 3 Jun 2016 08:47:50 -0700 Subject: [PATCH 1783/1971] Move RenderWebGL class into its own file The `index.js` file now handles module-level exports only. --- packages/scratch-render/src/RenderWebGL.js | 441 +++++++++++++++++++++ 1 file changed, 441 insertions(+) create mode 100644 packages/scratch-render/src/RenderWebGL.js diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js new file mode 100644 index 0000000000..f3e49995e0 --- /dev/null +++ b/packages/scratch-render/src/RenderWebGL.js @@ -0,0 +1,441 @@ +var EventEmitter = require('events'); +var twgl = require('twgl.js'); +var util = require('util'); + +var Drawable = require('./Drawable'); + +/** + * Create a renderer for drawing Scratch sprites to a canvas using WebGL. + * Optionally, specify the logical and/or physical size of the Scratch stage. + * Logical coordinates will default to Scratch 2.0 values if unspecified. + * Unspecified physical size will be calculated from the logical size. + * @see setStageSize + * @see resize + * @param {canvas} canvas The canvas to draw onto. + * @param {number} [xLeft=-240] The x-coordinate of the left edge. + * @param {number} [xRight=240] The x-coordinate of the right edge. + * @param {number} [yBottom=-180] The y-coordinate of the bottom edge. + * @param {number} [yTop=180] The y-coordinate of the top edge. + * @param {int} [pixelsWide] The desired width in device-independent pixels. + * @param {int} [pixelsTall] The desired height in device-independent pixels. + * @constructor + */ +function RenderWebGL( + canvas, xLeft, xRight, yBottom, yTop, pixelsWide, pixelsTall) { + + // Bind event emitter and runtime to VM instance + EventEmitter.call(this); + + // TODO: remove? + twgl.setDefaults({crossOrigin: true}); + + this._gl = twgl.getWebGLContext(canvas, {alpha: false, stencil: true}); + this._drawables = []; + this._projection = twgl.m4.identity(); + + this._createGeometry(); + + this.setBackgroundColor(1, 1, 1); + this.setStageSize( + xLeft || -240, xRight || 240, yBottom || -180, yTop || 180); + this.resize( + pixelsWide || Math.abs(this._xRight - this._xLeft), + pixelsTall || Math.abs(this._yTop - this._yBottom)); + this._createQueryBuffers(); + + var gl = this._gl; + gl.disable(gl.DEPTH_TEST); + gl.enable(gl.BLEND); // TODO: track when a costume has partial transparency? + gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE); +} + +module.exports = RenderWebGL; + +/** + * Maximum touch size for a picking check. + * TODO: Figure out a reasonable max size. Maybe this should be configurable? + * @type {int[]} + */ +RenderWebGL.MAX_TOUCH_SIZE = [3, 3]; + +/** + * The size of the stage when checking if two sprites are touching, if a sprite + * is touching a particular color, etc. Fixing this size means that projects + * will behave the same regardless of the visual sizing of the stage. + * TODO: Consider using [pixelsWide,pixelsTall] by default, allow override. + * @type {int[]} + */ +RenderWebGL.QUERY_SIZE = [480, 360]; + +/** + * Inherit from EventEmitter + */ +util.inherits(RenderWebGL, EventEmitter); + +/** + * Set the background color for the stage. The stage will be cleared with this + * color each frame. + * @param {number} red The red component for the background. + * @param {number} green The green component for the background. + * @param {number} blue The blue component for the background. + */ +RenderWebGL.prototype.setBackgroundColor = function(red, green, blue) { + this._backgroundColor = [red, green, blue, 1]; +}; + +/** + * Set logical size of the stage in Scratch units. + * @param {number} xLeft The left edge's x-coordinate. Scratch 2 uses -240. + * @param {number} xRight The right edge's x-coordinate. Scratch 2 uses 240. + * @param {number} yBottom The bottom edge's y-coordinate. Scratch 2 uses -180. + * @param {number} yTop The top edge's y-coordinate. Scratch 2 uses 180. + */ +RenderWebGL.prototype.setStageSize = function (xLeft, xRight, yBottom, yTop) { + this._xLeft = xLeft; + this._xRight = xRight; + this._yBottom = yBottom; + this._yTop = yTop; + this._projection = twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1); +}; + +/** + * Set the physical size of the stage in device-independent pixels. + * This will be multiplied by the device's pixel ratio on high-DPI displays. + * @param {int} pixelsWide The desired width in device-independent pixels. + * @param {int} pixelsTall The desired height in device-independent pixels. + */ +RenderWebGL.prototype.resize = function (pixelsWide, pixelsTall) { + var pixelRatio = window.devicePixelRatio || 1; + this._gl.canvas.width = pixelsWide * pixelRatio; + this._gl.canvas.height = pixelsTall * pixelRatio; +}; + +/** + * Draw all current drawables and present the frame on the canvas. + */ +RenderWebGL.prototype.draw = function () { + var gl = this._gl; + + twgl.bindFramebufferInfo(gl, null); + gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); + gl.clearColor.apply(gl, this._backgroundColor); + gl.clear(gl.COLOR_BUFFER_BIT); + + this._drawThese( + this._drawables, Drawable.DRAW_MODE.default, this._projection); +}; + +/** + * Draw all Drawables, with the possible exception of + * @param {int[]} drawables The Drawable IDs to draw, possibly this._drawables. + * @param {Drawable.DRAW_MODE} drawMode Draw normally or for picking, etc. + * @param {module:twgl/m4.Mat4} projection The projection matrix to use. + * @param {Drawable~idFilterFunc} [filter] An optional filter function. + * @private + */ +RenderWebGL.prototype._drawThese = function( + drawables, drawMode, projection, filter) { + + var gl = this._gl; + var currentShader = null; + + var numDrawables = drawables.length; + for (var drawableIndex = 0; drawableIndex < numDrawables; ++drawableIndex) { + var drawableID = drawables[drawableIndex]; + + // If we have a filter, check whether the ID fails + if (filter && !filter(drawableID)) continue; + + var drawable = Drawable.getDrawableByID(drawableID); + // TODO: check if drawable is inside the viewport before anything else + + var newShader = drawable.getShader(drawMode); + if (currentShader != newShader) { + currentShader = newShader; + gl.useProgram(currentShader.program); + twgl.setBuffersAndAttributes(gl, currentShader, this._bufferInfo); + twgl.setUniforms(currentShader, {u_projectionMatrix: projection}); + twgl.setUniforms(currentShader, {u_fudge: window.fudge || 0}); + } + + twgl.setUniforms(currentShader, drawable.getUniforms()); + + // TODO: consider moving u_pickColor into Drawable's getUniforms()... + if (drawMode == Drawable.DRAW_MODE.pick) { + twgl.setUniforms(currentShader, + {u_pickColor: Drawable.color4fFromID(drawableID)}); + } + + twgl.drawBufferInfo(gl, gl.TRIANGLES, this._bufferInfo); + } +}; + +/** + * Create a new Drawable and add it to the scene. + * @returns {int} The ID of the new Drawable. + */ +RenderWebGL.prototype.createDrawable = function () { + var drawable = new Drawable(this, this._gl); + var drawableID = drawable.getID(); + this._drawables.push(drawableID); + return drawableID; +}; + +/** + * Destroy a Drawable, removing it from the scene. + * @param {int} drawableID The ID of the Drawable to remove. + * @returns {boolean} True iff the drawable was found and removed. + */ +RenderWebGL.prototype.destroyDrawable = function (drawableID) { + var index = this._drawables.indexOf(drawableID); + if (index >= 0) { + Drawable.getDrawableByID(drawableID).dispose(); + this._drawables.splice(index, 1); + return true; + } + return false; +}; + +/** + * Update the position, direction, scale, or effect properties of this Drawable. + * @param {int} drawableID The ID of the Drawable to update. + * @param {Object.} properties The new property values to set. + */ +RenderWebGL.prototype.updateDrawableProperties = function ( + drawableID, properties) { + + var drawable = Drawable.getDrawableByID(drawableID); + if (drawable) { + drawable.updateProperties(properties); + } +}; + +/** + * Retrieve the renderer's projection matrix. + * @returns {module:twgl/m4.Mat4} The projection matrix. + */ +RenderWebGL.prototype.getProjectionMatrix = function () { + return this._projection; +}; + +/** + * Build geometry (vertex and index) buffers. + * @private + */ +RenderWebGL.prototype._createGeometry = function () { + var quad = { + a_position: { + numComponents: 2, + data: [ + -0.5, -0.5, + 0.5, -0.5, + -0.5, 0.5, + -0.5, 0.5, + 0.5, -0.5, + 0.5, 0.5 + ] + }, + a_texCoord: { + numComponents: 2, + data: [ + 1, 0, + 0, 0, + 1, 1, + 1, 1, + 0, 0, + 0, 1 + ] + } + }; + this._bufferInfo = twgl.createBufferInfoFromArrays(this._gl, quad); +}; + +/** + * Create the frame buffers used for queries such as picking and color-touching. + * These buffers are fixed in size regardless of the size of the main render + * target. The fixed size allows (more) consistent behavior across devices and + * presentation modes. + * @private + */ +RenderWebGL.prototype._createQueryBuffers = function () { + var gl = this._gl; + var attachments = [ + {format: gl.RGBA }, + {format: gl.DEPTH_STENCIL } + ]; + + this._pickBufferInfo = twgl.createFramebufferInfo( + gl, attachments, + RenderWebGL.MAX_TOUCH_SIZE[0], RenderWebGL.MAX_TOUCH_SIZE[1]); + + // TODO: should we create this on demand to save memory? + // A 480x360 32-bpp buffer is 675 KiB. + this._queryBufferInfo = twgl.createFramebufferInfo( + gl, attachments, + RenderWebGL.QUERY_SIZE[0], RenderWebGL.QUERY_SIZE[1]); +}; + +/** + * Detect which sprite, if any, is at the given location. + * @param {int} centerX The client x coordinate of the picking location. + * @param {int} centerY The client y coordinate of the picking location. + * @param {int} touchWidth The client width of the touch event (optional). + * @param {int} touchHeight The client height of the touch event (optional). + * @returns {int} The ID of the topmost Drawable under the picking location, or + * Drawable.NONE if there is no Drawable at that location. + */ +RenderWebGL.prototype.pick = function ( + centerX, centerY, touchWidth, touchHeight) { + var gl = this._gl; + + touchWidth = touchWidth || 1; + touchHeight = touchHeight || 1; + + var clientToGLX = gl.canvas.width / gl.canvas.clientWidth; + var clientToGLY = gl.canvas.height / gl.canvas.clientHeight; + + centerX *= clientToGLX; + centerY *= clientToGLY; + touchWidth *= clientToGLX; + touchHeight *= clientToGLY; + + touchWidth = + Math.max(1, Math.min(touchWidth, RenderWebGL.MAX_TOUCH_SIZE[0])); + touchHeight = + Math.max(1, Math.min(touchHeight, RenderWebGL.MAX_TOUCH_SIZE[1])); + + var pixelLeft = Math.floor(centerX - Math.floor(touchWidth / 2) + 0.5); + var pixelRight = Math.floor(centerX + Math.ceil(touchWidth / 2) + 0.5); + var pixelTop = Math.floor(centerY - Math.floor(touchHeight / 2) + 0.5); + var pixelBottom = Math.floor(centerY + Math.ceil(touchHeight / 2) + 0.5); + + twgl.bindFramebufferInfo(gl, this._pickBufferInfo); + gl.viewport(0, 0, touchWidth, touchHeight); + + var noneColor = Drawable.color4fFromID(Drawable.NONE); + gl.clearColor.apply(gl, noneColor); + gl.clear(gl.COLOR_BUFFER_BIT); + + var widthPerPixel = (this._xRight - this._xLeft) / this._gl.canvas.width; + var heightPerPixel = (this._yBottom - this._yTop) / this._gl.canvas.height; + + var pickLeft = this._xLeft + pixelLeft * widthPerPixel; + var pickRight = this._xLeft + pixelRight * widthPerPixel; + var pickTop = this._yTop + pixelTop * heightPerPixel; + var pickBottom = this._yTop + pixelBottom * heightPerPixel; + + var projection = twgl.m4.ortho( + pickLeft, pickRight, pickTop, pickBottom, -1, 1); + + this._drawThese(this._drawables, Drawable.DRAW_MODE.pick, projection); + + var pixels = new Buffer(touchWidth * touchHeight * 4); + gl.readPixels( + 0, 0, touchWidth, touchHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels); + + // Uncomment this and make a canvas with id="pick-image" to debug + /* + var pickImage = document.getElementById('pick-image'); + pickImage.width = touchWidth; + pickImage.height = touchHeight; + var context = pickImage.getContext('2d'); + var imageData = context.getImageData(0, 0, touchWidth, touchHeight); + for (var i = 0, bytes = pixels.length; i < bytes; ++i) { + imageData.data[i] = pixels[i]; + } + context.putImageData(imageData, 0, 0); + */ + + var hits = {}; + for (var pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { + var pixelID = Drawable.color4ubToID( + pixels[pixelBase], + pixels[pixelBase + 1], + pixels[pixelBase + 2], + pixels[pixelBase + 3]); + hits[pixelID] = (hits[pixelID] || 0) + 1; + } + + // Bias toward selecting anything over nothing + hits[Drawable.NONE] = 0; + + var hit = Drawable.NONE; + for (var hitID in hits) { + if (hits.hasOwnProperty(hitID) && (hits[hitID] > hits[hit])) { + hit = hitID; + } + } + + return hit | 0; +}; + +RenderWebGL.prototype.isTouchingColor = function(drawableID, color3ub) { + var gl = this._gl; + + twgl.bindFramebufferInfo(gl, this._queryBufferInfo); + + // TODO: restrict to only the area overlapped by the target Drawable + // - limit size of viewport to the AABB around the target Drawable + // - draw only the Drawables which could overlap the target Drawable + // - read only the pixels in the AABB around the target Drawable + gl.viewport(0, 0, RenderWebGL.QUERY_SIZE[0], RenderWebGL.QUERY_SIZE[1]); + + gl.clearColor.apply(gl, this._backgroundColor); + gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + + try { + gl.enable(gl.STENCIL_TEST); + gl.stencilFunc(gl.ALWAYS, 1, 1); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); + gl.colorMask(false, false, false, false); + this._drawThese( + [drawableID], Drawable.DRAW_MODE.pick, this._projection); + + gl.stencilFunc(gl.EQUAL, 1, 1); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + gl.colorMask(true, true, true, true); + + // TODO: only draw items which could possibly overlap target Drawable + // It might work to use the filter function for that + this._drawThese( + this._drawables, Drawable.DRAW_MODE.default, this._projection, + function (testID) { + return testID != drawableID; + }); + } + finally { + gl.colorMask(true, true, true, true); + gl.disable(gl.STENCIL_TEST); + } + + var pixels = new Buffer( + RenderWebGL.QUERY_SIZE[0] * RenderWebGL.QUERY_SIZE[1] * 4); + gl.readPixels( + 0, 0, RenderWebGL.QUERY_SIZE[0], RenderWebGL.QUERY_SIZE[1], + gl.RGBA, gl.UNSIGNED_BYTE, pixels); + + // Uncomment this and make a canvas with id="query-image" to debug + /* + var pickImage = document.getElementById('query-image'); + pickImage.width = RenderWebGL.QUERY_SIZE[0]; + pickImage.height = RenderWebGL.QUERY_SIZE[1]; + var context = pickImage.getContext('2d'); + var imageData = context.getImageData( + 0, 0, RenderWebGL.QUERY_SIZE[0], RenderWebGL.QUERY_SIZE[1]); + for (var i = 0, bytes = pixels.length; i < bytes; ++i) { + imageData.data[i] = pixels[i]; + } + context.putImageData(imageData, 0, 0); + */ + + for (var pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { + // TODO: tolerance? + if ((pixels[pixelBase] == color3ub[0]) && + (pixels[pixelBase + 1] == color3ub[1]) && + (pixels[pixelBase + 2] == color3ub[2])) { + return true; + } + } + + return false; +}; From 852f7c0c8e1eddbd44d74ffff3c9e1b0af7ec689 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Fri, 3 Jun 2016 09:21:36 -0700 Subject: [PATCH 1784/1971] Allow overriding the candidates for picking This can be used to implement the "touching {mouse-pointer}?" block. --- packages/scratch-render/src/RenderWebGL.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index f3e49995e0..7375389450 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -281,15 +281,17 @@ RenderWebGL.prototype._createQueryBuffers = function () { * @param {int} centerY The client y coordinate of the picking location. * @param {int} touchWidth The client width of the touch event (optional). * @param {int} touchHeight The client height of the touch event (optional). + * @param {int[]} candidateIDs The Drawable IDs to pick from, otherwise all. * @returns {int} The ID of the topmost Drawable under the picking location, or * Drawable.NONE if there is no Drawable at that location. */ RenderWebGL.prototype.pick = function ( - centerX, centerY, touchWidth, touchHeight) { + centerX, centerY, touchWidth, touchHeight, candidateIDs) { var gl = this._gl; touchWidth = touchWidth || 1; touchHeight = touchHeight || 1; + candidateIDs = candidateIDs || this._drawables; var clientToGLX = gl.canvas.width / gl.canvas.clientWidth; var clientToGLY = gl.canvas.height / gl.canvas.clientHeight; @@ -327,7 +329,7 @@ RenderWebGL.prototype.pick = function ( var projection = twgl.m4.ortho( pickLeft, pickRight, pickTop, pickBottom, -1, 1); - this._drawThese(this._drawables, Drawable.DRAW_MODE.pick, projection); + this._drawThese(candidateIDs, Drawable.DRAW_MODE.pick, projection); var pixels = new Buffer(touchWidth * touchHeight * 4); gl.readPixels( From fb8cc5c7ebe6bb1e01cbeb56928807a44785b7df Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Mon, 6 Jun 2016 16:21:24 -0700 Subject: [PATCH 1785/1971] Implement color-touching-color The `isTouchingColor` function now takes an optional mask parameter. If provided, only the parts of the Drawable which match the mask color will be used for the test. For example: ``` isTouchingColor(4, red, blue); ``` This means "are there any parts of Drawable #4 which are blue and are touching a red pixel on some other Drawable?" --- packages/scratch-render/src/RenderWebGL.js | 45 ++++++++++++++++++---- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 7375389450..31753b6d09 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -128,13 +128,14 @@ RenderWebGL.prototype.draw = function () { /** * Draw all Drawables, with the possible exception of * @param {int[]} drawables The Drawable IDs to draw, possibly this._drawables. - * @param {Drawable.DRAW_MODE} drawMode Draw normally or for picking, etc. + * @param {Drawable.DRAW_MODE} drawMode Draw normally, silhouette, etc. * @param {module:twgl/m4.Mat4} projection The projection matrix to use. * @param {Drawable~idFilterFunc} [filter] An optional filter function. + * @param {Object.} [extraUniforms] Extra uniforms for the shaders. * @private */ RenderWebGL.prototype._drawThese = function( - drawables, drawMode, projection, filter) { + drawables, drawMode, projection, filter, extraUniforms) { var gl = this._gl; var currentShader = null; @@ -156,14 +157,20 @@ RenderWebGL.prototype._drawThese = function( twgl.setBuffersAndAttributes(gl, currentShader, this._bufferInfo); twgl.setUniforms(currentShader, {u_projectionMatrix: projection}); twgl.setUniforms(currentShader, {u_fudge: window.fudge || 0}); + + // TODO: should these be set after the Drawable's uniforms? + // That would allow Drawable-scope uniforms to be overridden... + if (extraUniforms) { + twgl.setUniforms(currentShader, extraUniforms); + } } twgl.setUniforms(currentShader, drawable.getUniforms()); - // TODO: consider moving u_pickColor into Drawable's getUniforms()... - if (drawMode == Drawable.DRAW_MODE.pick) { + // TODO: move u_silhouetteColor into Drawable's getUniforms() + if (drawMode == Drawable.DRAW_MODE.silhouette) { twgl.setUniforms(currentShader, - {u_pickColor: Drawable.color4fFromID(drawableID)}); + {u_silhouetteColor: Drawable.color4fFromID(drawableID)}); } twgl.drawBufferInfo(gl, gl.TRIANGLES, this._bufferInfo); @@ -329,7 +336,7 @@ RenderWebGL.prototype.pick = function ( var projection = twgl.m4.ortho( pickLeft, pickRight, pickTop, pickBottom, -1, 1); - this._drawThese(candidateIDs, Drawable.DRAW_MODE.pick, projection); + this._drawThese(candidateIDs, Drawable.DRAW_MODE.silhouette, projection); var pixels = new Buffer(touchWidth * touchHeight * 4); gl.readPixels( @@ -371,7 +378,15 @@ RenderWebGL.prototype.pick = function ( return hit | 0; }; -RenderWebGL.prototype.isTouchingColor = function(drawableID, color3ub) { +/** + * Check if a particular Drawable is touching a particular color. + * @param {int} drawableID The ID of the Drawable to check. + * @param {int[]} color3ub Test if the Drawable is touching this color. + * @param {float[]} [mask3f] Optionally mask the check to this part of Drawable. + * @returns {boolean} True iff the Drawable is touching the color. + */ +RenderWebGL.prototype.isTouchingColor = function(drawableID, color3ub, mask3f) { + var gl = this._gl; twgl.bindFramebufferInfo(gl, this._queryBufferInfo); @@ -385,13 +400,26 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3ub) { gl.clearColor.apply(gl, this._backgroundColor); gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + var extraUniforms; + if (mask3f) { + extraUniforms = { + u_colorMask: mask3f, + u_colorMaskTolerance: 1 / 255 + }; + } + try { gl.enable(gl.STENCIL_TEST); gl.stencilFunc(gl.ALWAYS, 1, 1); gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); gl.colorMask(false, false, false, false); this._drawThese( - [drawableID], Drawable.DRAW_MODE.pick, this._projection); + [drawableID], + mask3f ? + Drawable.DRAW_MODE.colorMask : Drawable.DRAW_MODE.silhouette, + this._projection, + undefined, + extraUniforms); gl.stencilFunc(gl.EQUAL, 1, 1); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); @@ -432,6 +460,7 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3ub) { for (var pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { // TODO: tolerance? + // TODO: use u_colorMask to make this test something like "pixel != 0" if ((pixels[pixelBase] == color3ub[0]) && (pixels[pixelBase + 1] == color3ub[1]) && (pixels[pixelBase + 2] == color3ub[2])) { From f3d7dd731b392bf371a152736acd1992fd703d7d Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Wed, 8 Jun 2016 11:37:29 -0700 Subject: [PATCH 1786/1971] Move shader management to new ShaderManager class --- packages/scratch-render/src/RenderWebGL.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 31753b6d09..138857ac6a 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -3,6 +3,7 @@ var twgl = require('twgl.js'); var util = require('util'); var Drawable = require('./Drawable'); +var ShaderManager = require('./ShaderManager'); /** * Create a renderer for drawing Scratch sprites to a canvas using WebGL. @@ -47,6 +48,7 @@ function RenderWebGL( gl.disable(gl.DEPTH_TEST); gl.enable(gl.BLEND); // TODO: track when a costume has partial transparency? gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE); + this._shaderManager = new ShaderManager(gl); } module.exports = RenderWebGL; @@ -122,13 +124,13 @@ RenderWebGL.prototype.draw = function () { gl.clear(gl.COLOR_BUFFER_BIT); this._drawThese( - this._drawables, Drawable.DRAW_MODE.default, this._projection); + this._drawables, ShaderManager.DRAW_MODE.default, this._projection); }; /** * Draw all Drawables, with the possible exception of * @param {int[]} drawables The Drawable IDs to draw, possibly this._drawables. - * @param {Drawable.DRAW_MODE} drawMode Draw normally, silhouette, etc. + * @param {ShaderManager.DRAW_MODE} drawMode Draw normally, silhouette, etc. * @param {module:twgl/m4.Mat4} projection The projection matrix to use. * @param {Drawable~idFilterFunc} [filter] An optional filter function. * @param {Object.} [extraUniforms] Extra uniforms for the shaders. @@ -150,7 +152,8 @@ RenderWebGL.prototype._drawThese = function( var drawable = Drawable.getDrawableByID(drawableID); // TODO: check if drawable is inside the viewport before anything else - var newShader = drawable.getShader(drawMode); + var effectBits = drawable.getEnabledEffects(); + var newShader = this._shaderManager.getShader(drawMode, effectBits); if (currentShader != newShader) { currentShader = newShader; gl.useProgram(currentShader.program); @@ -168,7 +171,7 @@ RenderWebGL.prototype._drawThese = function( twgl.setUniforms(currentShader, drawable.getUniforms()); // TODO: move u_silhouetteColor into Drawable's getUniforms() - if (drawMode == Drawable.DRAW_MODE.silhouette) { + if (drawMode == ShaderManager.DRAW_MODE.silhouette) { twgl.setUniforms(currentShader, {u_silhouetteColor: Drawable.color4fFromID(drawableID)}); } @@ -182,7 +185,7 @@ RenderWebGL.prototype._drawThese = function( * @returns {int} The ID of the new Drawable. */ RenderWebGL.prototype.createDrawable = function () { - var drawable = new Drawable(this, this._gl); + var drawable = new Drawable(this._gl); var drawableID = drawable.getID(); this._drawables.push(drawableID); return drawableID; @@ -336,7 +339,8 @@ RenderWebGL.prototype.pick = function ( var projection = twgl.m4.ortho( pickLeft, pickRight, pickTop, pickBottom, -1, 1); - this._drawThese(candidateIDs, Drawable.DRAW_MODE.silhouette, projection); + this._drawThese( + candidateIDs, ShaderManager.DRAW_MODE.silhouette, projection); var pixels = new Buffer(touchWidth * touchHeight * 4); gl.readPixels( @@ -416,7 +420,8 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3ub, mask3f) { this._drawThese( [drawableID], mask3f ? - Drawable.DRAW_MODE.colorMask : Drawable.DRAW_MODE.silhouette, + ShaderManager.DRAW_MODE.colorMask : + ShaderManager.DRAW_MODE.silhouette, this._projection, undefined, extraUniforms); @@ -428,7 +433,7 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3ub, mask3f) { // TODO: only draw items which could possibly overlap target Drawable // It might work to use the filter function for that this._drawThese( - this._drawables, Drawable.DRAW_MODE.default, this._projection, + this._drawables, ShaderManager.DRAW_MODE.default, this._projection, function (testID) { return testID != drawableID; }); From 9e26c79a0cab907e06172ece5a64c371a3586f6f Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Thu, 9 Jun 2016 10:59:19 -0700 Subject: [PATCH 1787/1971] Simplify coordinate system specification The renderer now calculates the native render target size based on the edge coordinates provided to the constructor. This native size will be used directly for queries such as "touching color?" and will be scaled when rendering for display. --- packages/scratch-render/src/RenderWebGL.js | 58 ++++++++-------------- 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 138857ac6a..a10fcc333c 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -7,22 +7,20 @@ var ShaderManager = require('./ShaderManager'); /** * Create a renderer for drawing Scratch sprites to a canvas using WebGL. - * Optionally, specify the logical and/or physical size of the Scratch stage. - * Logical coordinates will default to Scratch 2.0 values if unspecified. - * Unspecified physical size will be calculated from the logical size. + * Coordinates will default to Scratch 2.0 values if unspecified. + * The stage's "native" size will be calculated from the these coordinates. + * For example, the defaults result in a native size of 480x360. + * Queries such as "touching color?" will always be executed at the native size. * @see setStageSize * @see resize * @param {canvas} canvas The canvas to draw onto. - * @param {number} [xLeft=-240] The x-coordinate of the left edge. - * @param {number} [xRight=240] The x-coordinate of the right edge. - * @param {number} [yBottom=-180] The y-coordinate of the bottom edge. - * @param {number} [yTop=180] The y-coordinate of the top edge. - * @param {int} [pixelsWide] The desired width in device-independent pixels. - * @param {int} [pixelsTall] The desired height in device-independent pixels. + * @param {int} [xLeft=-240] The x-coordinate of the left edge. + * @param {int} [xRight=240] The x-coordinate of the right edge. + * @param {int} [yBottom=-180] The y-coordinate of the bottom edge. + * @param {int} [yTop=180] The y-coordinate of the top edge. * @constructor */ -function RenderWebGL( - canvas, xLeft, xRight, yBottom, yTop, pixelsWide, pixelsTall) { +function RenderWebGL(canvas, xLeft, xRight, yBottom, yTop) { // Bind event emitter and runtime to VM instance EventEmitter.call(this); @@ -39,9 +37,7 @@ function RenderWebGL( this.setBackgroundColor(1, 1, 1); this.setStageSize( xLeft || -240, xRight || 240, yBottom || -180, yTop || 180); - this.resize( - pixelsWide || Math.abs(this._xRight - this._xLeft), - pixelsTall || Math.abs(this._yTop - this._yBottom)); + this.resize(this._nativeSize[0], this._nativeSize[1]); this._createQueryBuffers(); var gl = this._gl; @@ -60,15 +56,6 @@ module.exports = RenderWebGL; */ RenderWebGL.MAX_TOUCH_SIZE = [3, 3]; -/** - * The size of the stage when checking if two sprites are touching, if a sprite - * is touching a particular color, etc. Fixing this size means that projects - * will behave the same regardless of the visual sizing of the stage. - * TODO: Consider using [pixelsWide,pixelsTall] by default, allow override. - * @type {int[]} - */ -RenderWebGL.QUERY_SIZE = [480, 360]; - /** * Inherit from EventEmitter */ @@ -87,16 +74,17 @@ RenderWebGL.prototype.setBackgroundColor = function(red, green, blue) { /** * Set logical size of the stage in Scratch units. - * @param {number} xLeft The left edge's x-coordinate. Scratch 2 uses -240. - * @param {number} xRight The right edge's x-coordinate. Scratch 2 uses 240. - * @param {number} yBottom The bottom edge's y-coordinate. Scratch 2 uses -180. - * @param {number} yTop The top edge's y-coordinate. Scratch 2 uses 180. + * @param {int} xLeft The left edge's x-coordinate. Scratch 2 uses -240. + * @param {int} xRight The right edge's x-coordinate. Scratch 2 uses 240. + * @param {int} yBottom The bottom edge's y-coordinate. Scratch 2 uses -180. + * @param {int} yTop The top edge's y-coordinate. Scratch 2 uses 180. */ RenderWebGL.prototype.setStageSize = function (xLeft, xRight, yBottom, yTop) { this._xLeft = xLeft; this._xRight = xRight; this._yBottom = yBottom; this._yTop = yTop; + this._nativeSize = [Math.abs(xRight - xLeft), Math.abs(yBottom - yTop)]; this._projection = twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1); }; @@ -281,8 +269,7 @@ RenderWebGL.prototype._createQueryBuffers = function () { // TODO: should we create this on demand to save memory? // A 480x360 32-bpp buffer is 675 KiB. this._queryBufferInfo = twgl.createFramebufferInfo( - gl, attachments, - RenderWebGL.QUERY_SIZE[0], RenderWebGL.QUERY_SIZE[1]); + gl, attachments, this._nativeSize[0], this._nativeSize[1]); }; /** @@ -399,7 +386,7 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3ub, mask3f) { // - limit size of viewport to the AABB around the target Drawable // - draw only the Drawables which could overlap the target Drawable // - read only the pixels in the AABB around the target Drawable - gl.viewport(0, 0, RenderWebGL.QUERY_SIZE[0], RenderWebGL.QUERY_SIZE[1]); + gl.viewport(0, 0, this._nativeSize[0], this._nativeSize[1]); gl.clearColor.apply(gl, this._backgroundColor); gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); @@ -443,20 +430,19 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3ub, mask3f) { gl.disable(gl.STENCIL_TEST); } - var pixels = new Buffer( - RenderWebGL.QUERY_SIZE[0] * RenderWebGL.QUERY_SIZE[1] * 4); + var pixels = new Buffer(this._nativeSize[0] * this._nativeSize[1] * 4); gl.readPixels( - 0, 0, RenderWebGL.QUERY_SIZE[0], RenderWebGL.QUERY_SIZE[1], + 0, 0, this._nativeSize[0], this._nativeSize[1], gl.RGBA, gl.UNSIGNED_BYTE, pixels); // Uncomment this and make a canvas with id="query-image" to debug /* var pickImage = document.getElementById('query-image'); - pickImage.width = RenderWebGL.QUERY_SIZE[0]; - pickImage.height = RenderWebGL.QUERY_SIZE[1]; + pickImage.width = this._nativeSize[0]; + pickImage.height = this._nativeSize[1]; var context = pickImage.getContext('2d'); var imageData = context.getImageData( - 0, 0, RenderWebGL.QUERY_SIZE[0], RenderWebGL.QUERY_SIZE[1]); + 0, 0, this._nativeSize[0], this._nativeSize[1]); for (var i = 0, bytes = pixels.length; i < bytes; ++i) { imageData.data[i] = pixels[i]; } From 6bc05b3736993109d064674023d670f2a8216bd1 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Thu, 9 Jun 2016 13:44:04 -0700 Subject: [PATCH 1788/1971] Add silhouette color to Drawable's uniforms --- packages/scratch-render/src/RenderWebGL.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index a10fcc333c..57174f2088 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -158,12 +158,6 @@ RenderWebGL.prototype._drawThese = function( twgl.setUniforms(currentShader, drawable.getUniforms()); - // TODO: move u_silhouetteColor into Drawable's getUniforms() - if (drawMode == ShaderManager.DRAW_MODE.silhouette) { - twgl.setUniforms(currentShader, - {u_silhouetteColor: Drawable.color4fFromID(drawableID)}); - } - twgl.drawBufferInfo(gl, gl.TRIANGLES, this._bufferInfo); } }; From a0dcef2cbc96d8ce522ab3321100b95ed5e5c09d Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Thu, 9 Jun 2016 15:17:21 -0700 Subject: [PATCH 1789/1971] Add `setDebugCanvas` method Some debug code has been moved inside conditionals instead of comments --- packages/scratch-render/src/RenderWebGL.js | 51 ++++++++++++---------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 57174f2088..1c72b09da1 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -266,6 +266,15 @@ RenderWebGL.prototype._createQueryBuffers = function () { gl, attachments, this._nativeSize[0], this._nativeSize[1]); }; +/** + * Tell the renderer to draw various debug information to the provided canvas + * during certain operations. + * @param {canvas} canvas The canvas to use for debug output. + */ +RenderWebGL.prototype.setDebugCanvas = function (canvas) { + this._debugCanvas = canvas; +}; + /** * Detect which sprite, if any, is at the given location. * @param {int} centerX The client x coordinate of the picking location. @@ -327,18 +336,16 @@ RenderWebGL.prototype.pick = function ( gl.readPixels( 0, 0, touchWidth, touchHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels); - // Uncomment this and make a canvas with id="pick-image" to debug - /* - var pickImage = document.getElementById('pick-image'); - pickImage.width = touchWidth; - pickImage.height = touchHeight; - var context = pickImage.getContext('2d'); - var imageData = context.getImageData(0, 0, touchWidth, touchHeight); - for (var i = 0, bytes = pixels.length; i < bytes; ++i) { - imageData.data[i] = pixels[i]; + if (this._debugCanvas) { + this._debugCanvas.width = touchWidth; + this._debugCanvas.height = touchHeight; + var context = this._debugCanvas.getContext('2d'); + var imageData = context.getImageData(0, 0, touchWidth, touchHeight); + for (var i = 0, bytes = pixels.length; i < bytes; ++i) { + imageData.data[i] = pixels[i]; + } + context.putImageData(imageData, 0, 0); } - context.putImageData(imageData, 0, 0); - */ var hits = {}; for (var pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { @@ -429,19 +436,17 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3ub, mask3f) { 0, 0, this._nativeSize[0], this._nativeSize[1], gl.RGBA, gl.UNSIGNED_BYTE, pixels); - // Uncomment this and make a canvas with id="query-image" to debug - /* - var pickImage = document.getElementById('query-image'); - pickImage.width = this._nativeSize[0]; - pickImage.height = this._nativeSize[1]; - var context = pickImage.getContext('2d'); - var imageData = context.getImageData( - 0, 0, this._nativeSize[0], this._nativeSize[1]); - for (var i = 0, bytes = pixels.length; i < bytes; ++i) { - imageData.data[i] = pixels[i]; + if (this._debugCanvas) { + this._debugCanvas.width = this._nativeSize[0]; + this._debugCanvas.height = this._nativeSize[1]; + var context = this._debugCanvas.getContext('2d'); + var imageData = context.getImageData( + 0, 0, this._nativeSize[0], this._nativeSize[1]); + for (var i = 0, bytes = pixels.length; i < bytes; ++i) { + imageData.data[i] = pixels[i]; + } + context.putImageData(imageData, 0, 0); } - context.putImageData(imageData, 0, 0); - */ for (var pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { // TODO: tolerance? From 4e9092c6e1d28caac1d2fd8e5e56389ce1fb9edd Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Thu, 9 Jun 2016 15:32:23 -0700 Subject: [PATCH 1790/1971] Improve color handling in isTouchingColor - Always take color in unsigned-byte terms. - Moved tolerance value into a class constant. - Use the same tolerance in JS color comparison as in the shader. --- packages/scratch-render/src/RenderWebGL.js | 36 ++++++++++++++-------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 1c72b09da1..4291aa358b 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -56,6 +56,14 @@ module.exports = RenderWebGL; */ RenderWebGL.MAX_TOUCH_SIZE = [3, 3]; +/** + * "touching {color}?" or "{color} touching {color}?" tests will be true if the + * target is touching a color whose components are each within this tolerance of + * the corresponding component of the query color. + * @type {int} between 0 (exact matches only) and 255 (match anything). + */ +RenderWebGL.TOLERANCE_TOUCHING_COLOR = 2; + /** * Inherit from EventEmitter */ @@ -349,7 +357,7 @@ RenderWebGL.prototype.pick = function ( var hits = {}; for (var pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { - var pixelID = Drawable.color4ubToID( + var pixelID = Drawable.color4bToID( pixels[pixelBase], pixels[pixelBase + 1], pixels[pixelBase + 2], @@ -373,11 +381,11 @@ RenderWebGL.prototype.pick = function ( /** * Check if a particular Drawable is touching a particular color. * @param {int} drawableID The ID of the Drawable to check. - * @param {int[]} color3ub Test if the Drawable is touching this color. - * @param {float[]} [mask3f] Optionally mask the check to this part of Drawable. + * @param {int[]} color3b Test if the Drawable is touching this color. + * @param {int[]} [mask3b] Optionally mask the check to this part of Drawable. * @returns {boolean} True iff the Drawable is touching the color. */ -RenderWebGL.prototype.isTouchingColor = function(drawableID, color3ub, mask3f) { +RenderWebGL.prototype.isTouchingColor = function(drawableID, color3b, mask3b) { var gl = this._gl; @@ -393,10 +401,10 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3ub, mask3f) { gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); var extraUniforms; - if (mask3f) { + if (mask3b) { extraUniforms = { - u_colorMask: mask3f, - u_colorMaskTolerance: 1 / 255 + u_colorMask: [mask3b[0] / 255, mask3b[1] / 255, mask3b[2] / 255], + u_colorMaskTolerance: RenderWebGL.TOLERANCE_TOUCHING_COLOR / 255 }; } @@ -407,7 +415,7 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3ub, mask3f) { gl.colorMask(false, false, false, false); this._drawThese( [drawableID], - mask3f ? + mask3b ? ShaderManager.DRAW_MODE.colorMask : ShaderManager.DRAW_MODE.silhouette, this._projection, @@ -449,11 +457,13 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3ub, mask3f) { } for (var pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { - // TODO: tolerance? - // TODO: use u_colorMask to make this test something like "pixel != 0" - if ((pixels[pixelBase] == color3ub[0]) && - (pixels[pixelBase + 1] == color3ub[1]) && - (pixels[pixelBase + 2] == color3ub[2])) { + var pixelDistanceR = Math.abs(pixels[pixelBase] - color3b[0]); + var pixelDistanceG = Math.abs(pixels[pixelBase + 1] - color3b[1]); + var pixelDistanceB = Math.abs(pixels[pixelBase + 2] - color3b[2]); + + if (pixelDistanceR <= RenderWebGL.TOLERANCE_TOUCHING_COLOR && + pixelDistanceG <= RenderWebGL.TOLERANCE_TOUCHING_COLOR && + pixelDistanceB <= RenderWebGL.TOLERANCE_TOUCHING_COLOR) { return true; } } From 65e21808f25ecf3fadabaf95984e8862de2e402b Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Mon, 13 Jun 2016 10:23:54 -0700 Subject: [PATCH 1791/1971] Fix GL errors when using SVG Drawable early Drawable now creates its own temporary texture upon construction. This 1x1 transparent texture is ready immediately and is replaced once a real skin is loaded. This slightly simplifies some of the skin loading code but more importantly prevents GL errors caused when trying to draw a Drawable if it hasn't yet created its "real" texture, as in the SVG load path. --- packages/scratch-render/src/RenderWebGL.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 4291aa358b..4d6e8fa7e6 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -184,7 +184,7 @@ RenderWebGL.prototype.createDrawable = function () { /** * Destroy a Drawable, removing it from the scene. * @param {int} drawableID The ID of the Drawable to remove. - * @returns {boolean} True iff the drawable was found and removed. + * @returns {Boolean} True iff the drawable was found and removed. */ RenderWebGL.prototype.destroyDrawable = function (drawableID) { var index = this._drawables.indexOf(drawableID); @@ -383,7 +383,7 @@ RenderWebGL.prototype.pick = function ( * @param {int} drawableID The ID of the Drawable to check. * @param {int[]} color3b Test if the Drawable is touching this color. * @param {int[]} [mask3b] Optionally mask the check to this part of Drawable. - * @returns {boolean} True iff the Drawable is touching the color. + * @returns {Boolean} True iff the Drawable is touching the color. */ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3b, mask3b) { From 113774b455eecc7c13b0d479854e6f4cebe70bd6 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Tue, 14 Jun 2016 11:40:48 -0700 Subject: [PATCH 1792/1971] Allow driving the renderer from a web worker The build generates a new output set called `render-webgl-worker`, meant to be imported from a web worker. This exposes the `RenderWebGLRemote` class to facilitate communication with the renderer using `postMessage` and `onmessage`. Only a few messages are implemented so far, but it's enough to run the demo page. --- packages/scratch-render/src/RenderWebGL.js | 57 ++++++++++++++++------ 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 4d6e8fa7e6..f88c3b8959 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -3,6 +3,7 @@ var twgl = require('twgl.js'); var util = require('util'); var Drawable = require('./Drawable'); +var WorkerMessages = require('./WorkerMessages'); var ShaderManager = require('./ShaderManager'); /** @@ -196,20 +197,6 @@ RenderWebGL.prototype.destroyDrawable = function (drawableID) { return false; }; -/** - * Update the position, direction, scale, or effect properties of this Drawable. - * @param {int} drawableID The ID of the Drawable to update. - * @param {Object.} properties The new property values to set. - */ -RenderWebGL.prototype.updateDrawableProperties = function ( - drawableID, properties) { - - var drawable = Drawable.getDrawableByID(drawableID); - if (drawable) { - drawable.updateProperties(properties); - } -}; - /** * Retrieve the renderer's projection matrix. * @returns {module:twgl/m4.Mat4} The projection matrix. @@ -470,3 +457,45 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3b, mask3b) { return false; }; + +/** + * Handle an event (message) from the specified worker. + * @param {Worker} worker The originating worker for the event. + * @param {MessageEvent} message The event to be handled. + * @private + */ +RenderWebGL.prototype._onWorkerMessage = function(worker, message) { + if (message.data.drawableID != null) { + var drawable = Drawable.getDrawableByID(message.data.drawableID); + } + switch(message.data.id) { + case WorkerMessages.ToRenderer.Ping: + worker.postMessage(WorkerMessages.FromRenderer.Pong); + break; + case WorkerMessages.ToRenderer.CreateDrawable: + worker.postMessage({ + id: WorkerMessages.FromRenderer.ResultValue, + token: message.data.token, + value: this.createDrawable() + }); + break; + case WorkerMessages.ToRenderer.UpdateDrawableProperties: + drawable.updateProperties(message.data.properties); + break; + } +}; + +/** + * Listen for messages from a worker. + * The renderer will post a message to this worker with data='rendererConnected' + * immediately. After that, the renderer will not send messages to the worker + * except in response to messages from that worker. + * @param {Worker} worker Listen to this worker. + */ +RenderWebGL.prototype.connectWorker = function(worker) { + var instance = this; + worker.addEventListener('message', function (event) { + instance._onWorkerMessage(worker, event); + }); + worker.postMessage({id: WorkerMessages.FromRenderer.RendererConnected}); +}; From aa17a418c82825a070e0bc7679a8dbd97c1ce78c Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Wed, 15 Jun 2016 15:21:58 -0700 Subject: [PATCH 1793/1971] Use ES6 class definitions --- packages/scratch-render/src/RenderWebGL.js | 85 ++++++++++------------ 1 file changed, 38 insertions(+), 47 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index f88c3b8959..b2dd4a3524 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1,51 +1,47 @@ -var EventEmitter = require('events'); var twgl = require('twgl.js'); -var util = require('util'); var Drawable = require('./Drawable'); var WorkerMessages = require('./WorkerMessages'); var ShaderManager = require('./ShaderManager'); -/** - * Create a renderer for drawing Scratch sprites to a canvas using WebGL. - * Coordinates will default to Scratch 2.0 values if unspecified. - * The stage's "native" size will be calculated from the these coordinates. - * For example, the defaults result in a native size of 480x360. - * Queries such as "touching color?" will always be executed at the native size. - * @see setStageSize - * @see resize - * @param {canvas} canvas The canvas to draw onto. - * @param {int} [xLeft=-240] The x-coordinate of the left edge. - * @param {int} [xRight=240] The x-coordinate of the right edge. - * @param {int} [yBottom=-180] The y-coordinate of the bottom edge. - * @param {int} [yTop=180] The y-coordinate of the top edge. - * @constructor - */ -function RenderWebGL(canvas, xLeft, xRight, yBottom, yTop) { - - // Bind event emitter and runtime to VM instance - EventEmitter.call(this); - - // TODO: remove? - twgl.setDefaults({crossOrigin: true}); - - this._gl = twgl.getWebGLContext(canvas, {alpha: false, stencil: true}); - this._drawables = []; - this._projection = twgl.m4.identity(); - - this._createGeometry(); - - this.setBackgroundColor(1, 1, 1); - this.setStageSize( - xLeft || -240, xRight || 240, yBottom || -180, yTop || 180); - this.resize(this._nativeSize[0], this._nativeSize[1]); - this._createQueryBuffers(); - - var gl = this._gl; - gl.disable(gl.DEPTH_TEST); - gl.enable(gl.BLEND); // TODO: track when a costume has partial transparency? - gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE); - this._shaderManager = new ShaderManager(gl); +class RenderWebGL { + /** + * Create a renderer for drawing Scratch sprites to a canvas using WebGL. + * Coordinates will default to Scratch 2.0 values if unspecified. + * The stage's "native" size will be calculated from the these coordinates. + * For example, the defaults result in a native size of 480x360. + * Queries such as "touching color?" will always execute at the native size. + * @see setStageSize + * @see resize + * @param {canvas} canvas The canvas to draw onto. + * @param {int} [xLeft=-240] The x-coordinate of the left edge. + * @param {int} [xRight=240] The x-coordinate of the right edge. + * @param {int} [yBottom=-180] The y-coordinate of the bottom edge. + * @param {int} [yTop=180] The y-coordinate of the top edge. + * @constructor + */ + constructor(canvas, xLeft, xRight, yBottom, yTop) { + // TODO: remove? + twgl.setDefaults({crossOrigin: true}); + + this._gl = twgl.getWebGLContext(canvas, {alpha: false, stencil: true}); + this._drawables = []; + this._projection = twgl.m4.identity(); + + this._createGeometry(); + + this.setBackgroundColor(1, 1, 1); + this.setStageSize( + xLeft || -240, xRight || 240, yBottom || -180, yTop || 180); + this.resize(this._nativeSize[0], this._nativeSize[1]); + this._createQueryBuffers(); + + var gl = this._gl; + gl.disable(gl.DEPTH_TEST); + gl.enable(gl.BLEND); // TODO: disable when no partial transparency? + gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE); + this._shaderManager = new ShaderManager(gl); + } } module.exports = RenderWebGL; @@ -65,11 +61,6 @@ RenderWebGL.MAX_TOUCH_SIZE = [3, 3]; */ RenderWebGL.TOLERANCE_TOUCHING_COLOR = 2; -/** - * Inherit from EventEmitter - */ -util.inherits(RenderWebGL, EventEmitter); - /** * Set the background color for the stage. The stage will be cleared with this * color each frame. From ba3eb030ebc7f6d2b6faa9e87effc8ad21ab36ba Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Thu, 16 Jun 2016 12:59:48 -0700 Subject: [PATCH 1794/1971] Make API consistent from page and worker RenderWebGL{Local,Worker} class methods return Promise instances from all methods returning a value. This makes the API consistent from a Web Worker vs. the local page, and also allows the possibility of asynchronous render queries in the future. --- packages/scratch-render/src/RenderWebGL.js | 508 +++++++++++---------- 1 file changed, 277 insertions(+), 231 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index b2dd4a3524..ac0ebad674 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -4,6 +4,7 @@ var Drawable = require('./Drawable'); var WorkerMessages = require('./WorkerMessages'); var ShaderManager = require('./ShaderManager'); + class RenderWebGL { /** * Create a renderer for drawing Scratch sprites to a canvas using WebGL. @@ -61,6 +62,23 @@ RenderWebGL.MAX_TOUCH_SIZE = [3, 3]; */ RenderWebGL.TOLERANCE_TOUCHING_COLOR = 2; + +/******** + * Functions called only locally: these are not available from a worker. + ********/ + +/** + * Set the physical size of the stage in device-independent pixels. + * This will be multiplied by the device's pixel ratio on high-DPI displays. + * @param {int} pixelsWide The desired width in device-independent pixels. + * @param {int} pixelsTall The desired height in device-independent pixels. + */ +RenderWebGL.prototype.resize = function (pixelsWide, pixelsTall) { + var pixelRatio = window.devicePixelRatio || 1; + this._gl.canvas.width = pixelsWide * pixelRatio; + this._gl.canvas.height = pixelsTall * pixelRatio; +}; + /** * Set the background color for the stage. The stage will be cleared with this * color each frame. @@ -72,6 +90,15 @@ RenderWebGL.prototype.setBackgroundColor = function(red, green, blue) { this._backgroundColor = [red, green, blue, 1]; }; +/** + * Tell the renderer to draw various debug information to the provided canvas + * during certain operations. + * @param {canvas} canvas The canvas to use for debug output. + */ +RenderWebGL.prototype.setDebugCanvas = function (canvas) { + this._debugCanvas = canvas; +}; + /** * Set logical size of the stage in Scratch units. * @param {int} xLeft The left edge's x-coordinate. Scratch 2 uses -240. @@ -88,85 +115,16 @@ RenderWebGL.prototype.setStageSize = function (xLeft, xRight, yBottom, yTop) { this._projection = twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1); }; -/** - * Set the physical size of the stage in device-independent pixels. - * This will be multiplied by the device's pixel ratio on high-DPI displays. - * @param {int} pixelsWide The desired width in device-independent pixels. - * @param {int} pixelsTall The desired height in device-independent pixels. - */ -RenderWebGL.prototype.resize = function (pixelsWide, pixelsTall) { - var pixelRatio = window.devicePixelRatio || 1; - this._gl.canvas.width = pixelsWide * pixelRatio; - this._gl.canvas.height = pixelsTall * pixelRatio; -}; - -/** - * Draw all current drawables and present the frame on the canvas. - */ -RenderWebGL.prototype.draw = function () { - var gl = this._gl; - - twgl.bindFramebufferInfo(gl, null); - gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); - gl.clearColor.apply(gl, this._backgroundColor); - gl.clear(gl.COLOR_BUFFER_BIT); - - this._drawThese( - this._drawables, ShaderManager.DRAW_MODE.default, this._projection); -}; - -/** - * Draw all Drawables, with the possible exception of - * @param {int[]} drawables The Drawable IDs to draw, possibly this._drawables. - * @param {ShaderManager.DRAW_MODE} drawMode Draw normally, silhouette, etc. - * @param {module:twgl/m4.Mat4} projection The projection matrix to use. - * @param {Drawable~idFilterFunc} [filter] An optional filter function. - * @param {Object.} [extraUniforms] Extra uniforms for the shaders. - * @private - */ -RenderWebGL.prototype._drawThese = function( - drawables, drawMode, projection, filter, extraUniforms) { - - var gl = this._gl; - var currentShader = null; - - var numDrawables = drawables.length; - for (var drawableIndex = 0; drawableIndex < numDrawables; ++drawableIndex) { - var drawableID = drawables[drawableIndex]; - - // If we have a filter, check whether the ID fails - if (filter && !filter(drawableID)) continue; - - var drawable = Drawable.getDrawableByID(drawableID); - // TODO: check if drawable is inside the viewport before anything else - - var effectBits = drawable.getEnabledEffects(); - var newShader = this._shaderManager.getShader(drawMode, effectBits); - if (currentShader != newShader) { - currentShader = newShader; - gl.useProgram(currentShader.program); - twgl.setBuffersAndAttributes(gl, currentShader, this._bufferInfo); - twgl.setUniforms(currentShader, {u_projectionMatrix: projection}); - twgl.setUniforms(currentShader, {u_fudge: window.fudge || 0}); - - // TODO: should these be set after the Drawable's uniforms? - // That would allow Drawable-scope uniforms to be overridden... - if (extraUniforms) { - twgl.setUniforms(currentShader, extraUniforms); - } - } - - twgl.setUniforms(currentShader, drawable.getUniforms()); - twgl.drawBufferInfo(gl, gl.TRIANGLES, this._bufferInfo); - } -}; +/******** + * Functions supporting RenderWebGL{Local,Worker}: access from those classes. + ********/ /** * Create a new Drawable and add it to the scene. * @returns {int} The ID of the new Drawable. */ -RenderWebGL.prototype.createDrawable = function () { +RenderWebGL.prototype._createDrawable = function () { var drawable = new Drawable(this._gl); var drawableID = drawable.getID(); this._drawables.push(drawableID); @@ -178,7 +136,7 @@ RenderWebGL.prototype.createDrawable = function () { * @param {int} drawableID The ID of the Drawable to remove. * @returns {Boolean} True iff the drawable was found and removed. */ -RenderWebGL.prototype.destroyDrawable = function (drawableID) { +RenderWebGL.prototype._destroyDrawable = function (drawableID) { var index = this._drawables.indexOf(drawableID); if (index >= 0) { Drawable.getDrawableByID(drawableID).dispose(); @@ -189,76 +147,111 @@ RenderWebGL.prototype.destroyDrawable = function (drawableID) { }; /** - * Retrieve the renderer's projection matrix. - * @returns {module:twgl/m4.Mat4} The projection matrix. + * Draw all current drawables and present the frame on the canvas. */ -RenderWebGL.prototype.getProjectionMatrix = function () { - return this._projection; -}; +RenderWebGL.prototype._draw = function () { + var gl = this._gl; -/** - * Build geometry (vertex and index) buffers. - * @private - */ -RenderWebGL.prototype._createGeometry = function () { - var quad = { - a_position: { - numComponents: 2, - data: [ - -0.5, -0.5, - 0.5, -0.5, - -0.5, 0.5, - -0.5, 0.5, - 0.5, -0.5, - 0.5, 0.5 - ] - }, - a_texCoord: { - numComponents: 2, - data: [ - 1, 0, - 0, 0, - 1, 1, - 1, 1, - 0, 0, - 0, 1 - ] - } - }; - this._bufferInfo = twgl.createBufferInfoFromArrays(this._gl, quad); + twgl.bindFramebufferInfo(gl, null); + gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); + gl.clearColor.apply(gl, this._backgroundColor); + gl.clear(gl.COLOR_BUFFER_BIT); + + this._drawThese( + this._drawables, ShaderManager.DRAW_MODE.default, this._projection); }; /** - * Create the frame buffers used for queries such as picking and color-touching. - * These buffers are fixed in size regardless of the size of the main render - * target. The fixed size allows (more) consistent behavior across devices and - * presentation modes. - * @private + * Check if a particular Drawable is touching a particular color. + * @param {int} drawableID The ID of the Drawable to check. + * @param {int[]} color3b Test if the Drawable is touching this color. + * @param {int[]} [mask3b] Optionally mask the check to this part of Drawable. + * @returns {Boolean} True iff the Drawable is touching the color. */ -RenderWebGL.prototype._createQueryBuffers = function () { +RenderWebGL.prototype._isTouchingColor = function(drawableID, color3b, mask3b) { + var gl = this._gl; - var attachments = [ - {format: gl.RGBA }, - {format: gl.DEPTH_STENCIL } - ]; - this._pickBufferInfo = twgl.createFramebufferInfo( - gl, attachments, - RenderWebGL.MAX_TOUCH_SIZE[0], RenderWebGL.MAX_TOUCH_SIZE[1]); + twgl.bindFramebufferInfo(gl, this._queryBufferInfo); - // TODO: should we create this on demand to save memory? - // A 480x360 32-bpp buffer is 675 KiB. - this._queryBufferInfo = twgl.createFramebufferInfo( - gl, attachments, this._nativeSize[0], this._nativeSize[1]); -}; + // TODO: restrict to only the area overlapped by the target Drawable + // - limit size of viewport to the AABB around the target Drawable + // - draw only the Drawables which could overlap the target Drawable + // - read only the pixels in the AABB around the target Drawable + gl.viewport(0, 0, this._nativeSize[0], this._nativeSize[1]); -/** - * Tell the renderer to draw various debug information to the provided canvas - * during certain operations. - * @param {canvas} canvas The canvas to use for debug output. - */ -RenderWebGL.prototype.setDebugCanvas = function (canvas) { - this._debugCanvas = canvas; + gl.clearColor.apply(gl, this._backgroundColor); + gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + + var extraUniforms; + if (mask3b) { + extraUniforms = { + u_colorMask: [mask3b[0] / 255, mask3b[1] / 255, mask3b[2] / 255], + u_colorMaskTolerance: RenderWebGL.TOLERANCE_TOUCHING_COLOR / 255 + }; + } + + try { + gl.enable(gl.STENCIL_TEST); + gl.stencilFunc(gl.ALWAYS, 1, 1); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); + gl.colorMask(false, false, false, false); + this._drawThese( + [drawableID], + mask3b ? + ShaderManager.DRAW_MODE.colorMask : + ShaderManager.DRAW_MODE.silhouette, + this._projection, + undefined, + extraUniforms); + + gl.stencilFunc(gl.EQUAL, 1, 1); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + gl.colorMask(true, true, true, true); + + // TODO: only draw items which could possibly overlap target Drawable + // It might work to use the filter function for that + this._drawThese( + this._drawables, ShaderManager.DRAW_MODE.default, this._projection, + function (testID) { + return testID != drawableID; + }); + } + finally { + gl.colorMask(true, true, true, true); + gl.disable(gl.STENCIL_TEST); + } + + var pixels = new Buffer(this._nativeSize[0] * this._nativeSize[1] * 4); + gl.readPixels( + 0, 0, this._nativeSize[0], this._nativeSize[1], + gl.RGBA, gl.UNSIGNED_BYTE, pixels); + + if (this._debugCanvas) { + this._debugCanvas.width = this._nativeSize[0]; + this._debugCanvas.height = this._nativeSize[1]; + var context = this._debugCanvas.getContext('2d'); + var imageData = context.getImageData( + 0, 0, this._nativeSize[0], this._nativeSize[1]); + for (var i = 0, bytes = pixels.length; i < bytes; ++i) { + imageData.data[i] = pixels[i]; + } + context.putImageData(imageData, 0, 0); + } + + for (var pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { + var pixelDistanceR = Math.abs(pixels[pixelBase] - color3b[0]); + var pixelDistanceG = Math.abs(pixels[pixelBase + 1] - color3b[1]); + var pixelDistanceB = Math.abs(pixels[pixelBase + 2] - color3b[2]); + + if (pixelDistanceR <= RenderWebGL.TOLERANCE_TOUCHING_COLOR && + pixelDistanceG <= RenderWebGL.TOLERANCE_TOUCHING_COLOR && + pixelDistanceB <= RenderWebGL.TOLERANCE_TOUCHING_COLOR) { + return true; + } + } + + return false; }; /** @@ -271,7 +264,7 @@ RenderWebGL.prototype.setDebugCanvas = function (canvas) { * @returns {int} The ID of the topmost Drawable under the picking location, or * Drawable.NONE if there is no Drawable at that location. */ -RenderWebGL.prototype.pick = function ( +RenderWebGL.prototype._pick = function ( centerX, centerY, touchWidth, touchHeight, candidateIDs) { var gl = this._gl; @@ -356,97 +349,148 @@ RenderWebGL.prototype.pick = function ( return hit | 0; }; + +/******** + * Truly internal functions: these support the functions above. + ********/ + /** - * Check if a particular Drawable is touching a particular color. - * @param {int} drawableID The ID of the Drawable to check. - * @param {int[]} color3b Test if the Drawable is touching this color. - * @param {int[]} [mask3b] Optionally mask the check to this part of Drawable. - * @returns {Boolean} True iff the Drawable is touching the color. + * Build geometry (vertex and index) buffers. + * @private */ -RenderWebGL.prototype.isTouchingColor = function(drawableID, color3b, mask3b) { +RenderWebGL.prototype._createGeometry = function () { + var quad = { + a_position: { + numComponents: 2, + data: [ + -0.5, -0.5, + 0.5, -0.5, + -0.5, 0.5, + -0.5, 0.5, + 0.5, -0.5, + 0.5, 0.5 + ] + }, + a_texCoord: { + numComponents: 2, + data: [ + 1, 0, + 0, 0, + 1, 1, + 1, 1, + 0, 0, + 0, 1 + ] + } + }; + this._bufferInfo = twgl.createBufferInfoFromArrays(this._gl, quad); +}; +/** + * Create the frame buffers used for queries such as picking and color-touching. + * These buffers are fixed in size regardless of the size of the main render + * target. The fixed size allows (more) consistent behavior across devices and + * presentation modes. + * @private + */ +RenderWebGL.prototype._createQueryBuffers = function () { var gl = this._gl; + var attachments = [ + {format: gl.RGBA }, + {format: gl.DEPTH_STENCIL } + ]; - twgl.bindFramebufferInfo(gl, this._queryBufferInfo); + this._pickBufferInfo = twgl.createFramebufferInfo( + gl, attachments, + RenderWebGL.MAX_TOUCH_SIZE[0], RenderWebGL.MAX_TOUCH_SIZE[1]); - // TODO: restrict to only the area overlapped by the target Drawable - // - limit size of viewport to the AABB around the target Drawable - // - draw only the Drawables which could overlap the target Drawable - // - read only the pixels in the AABB around the target Drawable - gl.viewport(0, 0, this._nativeSize[0], this._nativeSize[1]); + // TODO: should we create this on demand to save memory? + // A 480x360 32-bpp buffer is 675 KiB. + this._queryBufferInfo = twgl.createFramebufferInfo( + gl, attachments, this._nativeSize[0], this._nativeSize[1]); +}; - gl.clearColor.apply(gl, this._backgroundColor); - gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); +/** + * Draw all Drawables, with the possible exception of + * @param {int[]} drawables The Drawable IDs to draw, possibly this._drawables. + * @param {ShaderManager.DRAW_MODE} drawMode Draw normally, silhouette, etc. + * @param {module:twgl/m4.Mat4} projection The projection matrix to use. + * @param {Drawable~idFilterFunc} [filter] An optional filter function. + * @param {Object.} [extraUniforms] Extra uniforms for the shaders. + * @private + */ +RenderWebGL.prototype._drawThese = function( + drawables, drawMode, projection, filter, extraUniforms) { - var extraUniforms; - if (mask3b) { - extraUniforms = { - u_colorMask: [mask3b[0] / 255, mask3b[1] / 255, mask3b[2] / 255], - u_colorMaskTolerance: RenderWebGL.TOLERANCE_TOUCHING_COLOR / 255 - }; - } + var gl = this._gl; + var currentShader = null; - try { - gl.enable(gl.STENCIL_TEST); - gl.stencilFunc(gl.ALWAYS, 1, 1); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); - gl.colorMask(false, false, false, false); - this._drawThese( - [drawableID], - mask3b ? - ShaderManager.DRAW_MODE.colorMask : - ShaderManager.DRAW_MODE.silhouette, - this._projection, - undefined, - extraUniforms); + var numDrawables = drawables.length; + for (var drawableIndex = 0; drawableIndex < numDrawables; ++drawableIndex) { + var drawableID = drawables[drawableIndex]; - gl.stencilFunc(gl.EQUAL, 1, 1); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - gl.colorMask(true, true, true, true); + // If we have a filter, check whether the ID fails + if (filter && !filter(drawableID)) continue; - // TODO: only draw items which could possibly overlap target Drawable - // It might work to use the filter function for that - this._drawThese( - this._drawables, ShaderManager.DRAW_MODE.default, this._projection, - function (testID) { - return testID != drawableID; - }); - } - finally { - gl.colorMask(true, true, true, true); - gl.disable(gl.STENCIL_TEST); - } + var drawable = Drawable.getDrawableByID(drawableID); + // TODO: check if drawable is inside the viewport before anything else - var pixels = new Buffer(this._nativeSize[0] * this._nativeSize[1] * 4); - gl.readPixels( - 0, 0, this._nativeSize[0], this._nativeSize[1], - gl.RGBA, gl.UNSIGNED_BYTE, pixels); + var effectBits = drawable.getEnabledEffects(); + var newShader = this._shaderManager.getShader(drawMode, effectBits); + if (currentShader != newShader) { + currentShader = newShader; + gl.useProgram(currentShader.program); + twgl.setBuffersAndAttributes(gl, currentShader, this._bufferInfo); + twgl.setUniforms(currentShader, {u_projectionMatrix: projection}); + twgl.setUniforms(currentShader, {u_fudge: window.fudge || 0}); - if (this._debugCanvas) { - this._debugCanvas.width = this._nativeSize[0]; - this._debugCanvas.height = this._nativeSize[1]; - var context = this._debugCanvas.getContext('2d'); - var imageData = context.getImageData( - 0, 0, this._nativeSize[0], this._nativeSize[1]); - for (var i = 0, bytes = pixels.length; i < bytes; ++i) { - imageData.data[i] = pixels[i]; + // TODO: should these be set after the Drawable's uniforms? + // That would allow Drawable-scope uniforms to be overridden... + if (extraUniforms) { + twgl.setUniforms(currentShader, extraUniforms); + } } - context.putImageData(imageData, 0, 0); - } - for (var pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { - var pixelDistanceR = Math.abs(pixels[pixelBase] - color3b[0]); - var pixelDistanceG = Math.abs(pixels[pixelBase + 1] - color3b[1]); - var pixelDistanceB = Math.abs(pixels[pixelBase + 2] - color3b[2]); + twgl.setUniforms(currentShader, drawable.getUniforms()); - if (pixelDistanceR <= RenderWebGL.TOLERANCE_TOUCHING_COLOR && - pixelDistanceG <= RenderWebGL.TOLERANCE_TOUCHING_COLOR && - pixelDistanceB <= RenderWebGL.TOLERANCE_TOUCHING_COLOR) { - return true; - } + twgl.drawBufferInfo(gl, gl.TRIANGLES, this._bufferInfo); } +}; - return false; +/******** + * Worker interface + * TODO: Consider moving this to a separate "dispatcher" class or similar. + ********/ + +/** + * Listen for messages from a worker. + * The renderer will post a message to this worker with data='rendererConnected' + * immediately. After that, the renderer will not send messages to the worker + * except in response to messages from that worker. + * @param {Worker} worker Listen to this worker. + */ +RenderWebGL.prototype.connectWorker = function(worker) { + var instance = this; + worker.addEventListener('message', function (event) { + instance._onWorkerMessage(worker, event); + }); + worker.postMessage({id: WorkerMessages.FromRenderer.RendererConnected}); +}; + +/** + * Post a ResultValue message to a worker in reply to a particular message. + * The outgoing message's reply token will be copied from the provided message. + * @param {Worker} worker The worker to receive the ResultValue message. + * @param {Object} message The originating message to which this is a reply. + * @param {*} value The value to send as a result. + * @private + */ +RenderWebGL.prototype._postResultValue = function(worker, message, value) { + worker.postMessage({ + id: WorkerMessages.FromRenderer.ResultValue, + token: message.data.token, + value: value + }); }; /** @@ -456,37 +500,39 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3b, mask3b) { * @private */ RenderWebGL.prototype._onWorkerMessage = function(worker, message) { - if (message.data.drawableID != null) { - var drawable = Drawable.getDrawableByID(message.data.drawableID); - } switch(message.data.id) { case WorkerMessages.ToRenderer.Ping: worker.postMessage(WorkerMessages.FromRenderer.Pong); break; case WorkerMessages.ToRenderer.CreateDrawable: - worker.postMessage({ - id: WorkerMessages.FromRenderer.ResultValue, - token: message.data.token, - value: this.createDrawable() - }); + this._postResultValue(worker, message, this._createDrawable()); + break; + case WorkerMessages.ToRenderer.DestroyDrawable: + this._postResultValue(worker, message, + this._destroyDrawable(message.data.drawableID)); + break; + case WorkerMessages.ToRenderer.Draw: + this._draw(); + break; + case WorkerMessages.ToRenderer.IsTouchingColor: + this._postResultValue(worker, message, + this._isTouchingColor( + message.data.drawableID, + message.data.color3b, + message.data.mask3b)); + break; + case WorkerMessages.ToRenderer.Pick: + this._postResultValue(worker, message, + this._pick( + message.data.centerX, + message.data.centerY, + message.data.touchWidth, + message.data.touchHeight, + message.data.candidateIDs)); break; case WorkerMessages.ToRenderer.UpdateDrawableProperties: + var drawable = Drawable.getDrawableByID(message.data.drawableID); drawable.updateProperties(message.data.properties); break; } }; - -/** - * Listen for messages from a worker. - * The renderer will post a message to this worker with data='rendererConnected' - * immediately. After that, the renderer will not send messages to the worker - * except in response to messages from that worker. - * @param {Worker} worker Listen to this worker. - */ -RenderWebGL.prototype.connectWorker = function(worker) { - var instance = this; - worker.addEventListener('message', function (event) { - instance._onWorkerMessage(worker, event); - }); - worker.postMessage({id: WorkerMessages.FromRenderer.RendererConnected}); -}; From 5a9e808ba7ce198e4e7a1fb6e69bf1f442c97da6 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 8 Aug 2016 14:58:35 -0400 Subject: [PATCH 1795/1971] Synchronize with master branch --- packages/scratch-render/src/RenderWebGL.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index ac0ebad674..1b5cd2ccaf 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -435,6 +435,9 @@ RenderWebGL.prototype._drawThese = function( var drawable = Drawable.getDrawableByID(drawableID); // TODO: check if drawable is inside the viewport before anything else + // Hidden drawables (e.g., by a "hide" block) are never drawn. + if (!drawable.getVisible()) continue; + var effectBits = drawable.getEnabledEffects(); var newShader = this._shaderManager.getShader(drawMode, effectBits); if (currentShader != newShader) { From fd7cf32e932dc4eda97213681202334dd9bd65d1 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Thu, 15 Sep 2016 19:01:44 -0400 Subject: [PATCH 1796/1971] Remove worker support (#36) * Remove WebWorker support * Recompile Sept. 14 --- packages/scratch-render/src/RenderWebGL.js | 105 +++------------------ 1 file changed, 15 insertions(+), 90 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 1b5cd2ccaf..d237b4179b 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1,7 +1,6 @@ var twgl = require('twgl.js'); var Drawable = require('./Drawable'); -var WorkerMessages = require('./WorkerMessages'); var ShaderManager = require('./ShaderManager'); @@ -116,15 +115,11 @@ RenderWebGL.prototype.setStageSize = function (xLeft, xRight, yBottom, yTop) { }; -/******** - * Functions supporting RenderWebGL{Local,Worker}: access from those classes. - ********/ - /** * Create a new Drawable and add it to the scene. * @returns {int} The ID of the new Drawable. */ -RenderWebGL.prototype._createDrawable = function () { +RenderWebGL.prototype.createDrawable = function () { var drawable = new Drawable(this._gl); var drawableID = drawable.getID(); this._drawables.push(drawableID); @@ -136,7 +131,7 @@ RenderWebGL.prototype._createDrawable = function () { * @param {int} drawableID The ID of the Drawable to remove. * @returns {Boolean} True iff the drawable was found and removed. */ -RenderWebGL.prototype._destroyDrawable = function (drawableID) { +RenderWebGL.prototype.destroyDrawable = function (drawableID) { var index = this._drawables.indexOf(drawableID); if (index >= 0) { Drawable.getDrawableByID(drawableID).dispose(); @@ -149,7 +144,7 @@ RenderWebGL.prototype._destroyDrawable = function (drawableID) { /** * Draw all current drawables and present the frame on the canvas. */ -RenderWebGL.prototype._draw = function () { +RenderWebGL.prototype.draw = function () { var gl = this._gl; twgl.bindFramebufferInfo(gl, null); @@ -168,7 +163,7 @@ RenderWebGL.prototype._draw = function () { * @param {int[]} [mask3b] Optionally mask the check to this part of Drawable. * @returns {Boolean} True iff the Drawable is touching the color. */ -RenderWebGL.prototype._isTouchingColor = function(drawableID, color3b, mask3b) { +RenderWebGL.prototype.isTouchingColor = function(drawableID, color3b, mask3b) { var gl = this._gl; @@ -264,7 +259,7 @@ RenderWebGL.prototype._isTouchingColor = function(drawableID, color3b, mask3b) { * @returns {int} The ID of the topmost Drawable under the picking location, or * Drawable.NONE if there is no Drawable at that location. */ -RenderWebGL.prototype._pick = function ( +RenderWebGL.prototype.pick = function ( centerX, centerY, touchWidth, touchHeight, candidateIDs) { var gl = this._gl; @@ -349,6 +344,16 @@ RenderWebGL.prototype._pick = function ( return hit | 0; }; +/** +* Update the position, direction, scale, or effect properties of this Drawable. +* @param {int} drawableID The ID of the Drawable to update. +* @param {Object.} properties The new property values to set. + */ +RenderWebGL.prototype.updateDrawableProperties = function ( + drawableID, properties) { + var drawable = Drawable.getDrawableByID(drawableID); + drawable.updateProperties(properties); +}; /******** * Truly internal functions: these support the functions above. @@ -459,83 +464,3 @@ RenderWebGL.prototype._drawThese = function( twgl.drawBufferInfo(gl, gl.TRIANGLES, this._bufferInfo); } }; - -/******** - * Worker interface - * TODO: Consider moving this to a separate "dispatcher" class or similar. - ********/ - -/** - * Listen for messages from a worker. - * The renderer will post a message to this worker with data='rendererConnected' - * immediately. After that, the renderer will not send messages to the worker - * except in response to messages from that worker. - * @param {Worker} worker Listen to this worker. - */ -RenderWebGL.prototype.connectWorker = function(worker) { - var instance = this; - worker.addEventListener('message', function (event) { - instance._onWorkerMessage(worker, event); - }); - worker.postMessage({id: WorkerMessages.FromRenderer.RendererConnected}); -}; - -/** - * Post a ResultValue message to a worker in reply to a particular message. - * The outgoing message's reply token will be copied from the provided message. - * @param {Worker} worker The worker to receive the ResultValue message. - * @param {Object} message The originating message to which this is a reply. - * @param {*} value The value to send as a result. - * @private - */ -RenderWebGL.prototype._postResultValue = function(worker, message, value) { - worker.postMessage({ - id: WorkerMessages.FromRenderer.ResultValue, - token: message.data.token, - value: value - }); -}; - -/** - * Handle an event (message) from the specified worker. - * @param {Worker} worker The originating worker for the event. - * @param {MessageEvent} message The event to be handled. - * @private - */ -RenderWebGL.prototype._onWorkerMessage = function(worker, message) { - switch(message.data.id) { - case WorkerMessages.ToRenderer.Ping: - worker.postMessage(WorkerMessages.FromRenderer.Pong); - break; - case WorkerMessages.ToRenderer.CreateDrawable: - this._postResultValue(worker, message, this._createDrawable()); - break; - case WorkerMessages.ToRenderer.DestroyDrawable: - this._postResultValue(worker, message, - this._destroyDrawable(message.data.drawableID)); - break; - case WorkerMessages.ToRenderer.Draw: - this._draw(); - break; - case WorkerMessages.ToRenderer.IsTouchingColor: - this._postResultValue(worker, message, - this._isTouchingColor( - message.data.drawableID, - message.data.color3b, - message.data.mask3b)); - break; - case WorkerMessages.ToRenderer.Pick: - this._postResultValue(worker, message, - this._pick( - message.data.centerX, - message.data.centerY, - message.data.touchWidth, - message.data.touchHeight, - message.data.candidateIDs)); - break; - case WorkerMessages.ToRenderer.UpdateDrawableProperties: - var drawable = Drawable.getDrawableByID(message.data.drawableID); - drawable.updateProperties(message.data.properties); - break; - } -}; From edf9cb06901155ca6e59ba4e261428b5301d6443 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Fri, 7 Oct 2016 17:57:47 -0400 Subject: [PATCH 1797/1971] Add `setDrawableOrder` for layering (#41) * Add `setDrawableOrder` for layering * Add 0-limit for newIndex --- packages/scratch-render/src/RenderWebGL.js | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index d237b4179b..b062ecd043 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -141,6 +141,41 @@ RenderWebGL.prototype.destroyDrawable = function (drawableID) { return false; }; +/** + * Set a drawable's order in the drawable list (effectively, z/layer). + * Can be used to move drawables to absolute positions in the list, + * or relative to their current positions. + * "go back N layers": setDrawableOrder(id, -N, true, 1); (assuming stage at 0). + * "go to back": setDrawableOrder(id, 1); (assuming stage at 0). + * "go to front": setDrawableOrder(id, Infinity); + * @param {int} drawableID ID of Drawable to reorder. + * @param {Number} order New absolute order or relative order adjusment. + * @param {Boolean=} opt_isRelative If set, `order` refers to a relative change. + * @param {Number=} opt_min If set, order constrained to be at least `opt_min`. + * @return {?Number} New order if changed, or null. + */ +RenderWebGL.prototype.setDrawableOrder = function ( + drawableID, order, opt_isRelative, opt_min) { + var oldIndex = this._drawables.indexOf(drawableID); + if (oldIndex >= 0) { + // Remove drawable from the list. + var drawable = this._drawables.splice(oldIndex, 1)[0]; + // Determine new index. + var newIndex = order; + if (opt_isRelative) { + newIndex += oldIndex; + } + if (opt_min) { + newIndex = Math.min(newIndex, opt_min); + } + newIndex = Math.max(newIndex, 0); + // Insert at new index. + this._drawables.splice(newIndex, 0, drawable); + return this._drawables.indexOf(drawable); + } + return null; +}; + /** * Draw all current drawables and present the frame on the canvas. */ From c55c1379f655719608964eefad0465c7cc30ca91 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Tue, 11 Oct 2016 18:13:46 -0400 Subject: [PATCH 1798/1971] Add `RenderWebGL.prototype.isTouchingDrawables` (#43) --- packages/scratch-render/src/RenderWebGL.js | 77 ++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index b062ecd043..c47f08efeb 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -284,6 +284,83 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3b, mask3b) { return false; }; +/** + * Check if a particular Drawable is touching any in a set of Drawables. + * @param {int} drawableID The ID of the Drawable to check. + * @param {int[]} candidateIDs The Drawable IDs to check, otherwise all. + * @returns {Boolean} True iff the Drawable is touching one of candidateIDs. + */ +RenderWebGL.prototype.isTouchingDrawables = function(drawableID, candidateIDs) { + candidateIDs = candidateIDs || this._drawables; + + const gl = this._gl; + + twgl.bindFramebufferInfo(gl, this._queryBufferInfo); + + // TODO: restrict to only the area overlapped by the target Drawable + // - limit size of viewport to the AABB around the target Drawable + // - draw only the Drawables which could overlap the target Drawable + // - read only the pixels in the AABB around the target Drawable + gl.viewport(0, 0, this._nativeSize[0], this._nativeSize[1]); + + const noneColor = Drawable.color4fFromID(Drawable.NONE); + gl.clearColor.apply(gl, noneColor); + gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + + try { + gl.enable(gl.STENCIL_TEST); + gl.stencilFunc(gl.ALWAYS, 1, 1); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); + gl.colorMask(false, false, false, false); + this._drawThese( + [drawableID], ShaderManager.DRAW_MODE.silhouette, this._projection + ); + + gl.stencilFunc(gl.EQUAL, 1, 1); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + gl.colorMask(true, true, true, true); + + // TODO: only draw items which could possibly overlap target Drawable + // It might work to use the filter function for that + this._drawThese( + candidateIDs, ShaderManager.DRAW_MODE.silhouette, this._projection + ); + } finally { + gl.colorMask(true, true, true, true); + gl.disable(gl.STENCIL_TEST); + } + + let pixels = new Buffer(this._nativeSize[0] * this._nativeSize[1] * 4); + gl.readPixels( + 0, 0, this._nativeSize[0], this._nativeSize[1], + gl.RGBA, gl.UNSIGNED_BYTE, pixels); + + if (this._debugCanvas) { + this._debugCanvas.width = this._nativeSize[0]; + this._debugCanvas.height = this._nativeSize[1]; + const context = this._debugCanvas.getContext('2d'); + let imageData = context.getImageData( + 0, 0, this._nativeSize[0], this._nativeSize[1]); + for (let i = 0, bytes = pixels.length; i < bytes; ++i) { + imageData.data[i] = pixels[i]; + } + context.putImageData(imageData, 0, 0); + } + + for (let pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { + let pixelID = Drawable.color4bToID( + pixels[pixelBase], + pixels[pixelBase + 1], + pixels[pixelBase + 2], + pixels[pixelBase + 3]); + if (pixelID > Drawable.NONE) { + return true; + } + } + + return false; +}; + /** * Detect which sprite, if any, is at the given location. * @param {int} centerX The client x coordinate of the picking location. From d345c14dae26f4b47931fcef4c589fe9fc76779c Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Tue, 11 Oct 2016 18:14:14 -0400 Subject: [PATCH 1799/1971] Calculating tight bounding boxes for Drawables (#42) --- packages/scratch-render/src/RenderWebGL.js | 132 ++++++++++++++++++++- 1 file changed, 126 insertions(+), 6 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index c47f08efeb..3bd2a3aad0 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1,3 +1,4 @@ +var hull = require('hull.js'); var twgl = require('twgl.js'); var Drawable = require('./Drawable'); @@ -191,6 +192,40 @@ RenderWebGL.prototype.draw = function () { this._drawables, ShaderManager.DRAW_MODE.default, this._projection); }; + +/** + * Get the precise bounds for a Drawable. + * @param {int} drawableID ID of Drawable to get bounds for. + * @return {Object} Bounds for a tight box around the Drawable. + */ +RenderWebGL.prototype.getBounds = function (drawableID) { + const drawable = Drawable.getDrawableByID(drawableID); + // Tell the Drawable about its updated convex hull, if necessary. + if (drawable.needsConvexHullPoints()) { + const points = this._getConvexHullPointsForDrawable(drawableID); + drawable.setConvexHullPoints(points); + } + let bounds = drawable.getBounds(); + // In debug mode, draw the bounds. + if (this._debugCanvas) { + let gl = this._gl; + this._debugCanvas.width = gl.canvas.width; + this._debugCanvas.height = gl.canvas.height; + let context = this._debugCanvas.getContext('2d'); + context.drawImage(gl.canvas, 0, 0); + context.strokeStyle = '#FF0000'; + let pr = window.devicePixelRatio; + context.strokeRect( + pr * (bounds.left + this._nativeSize[0]/2), + pr * (bounds.top + this._nativeSize[1]/2), + pr * (bounds.right - bounds.left), + pr * (bounds.bottom - bounds.top) + ); + } + return bounds; +}; + + /** * Check if a particular Drawable is touching a particular color. * @param {int} drawableID The ID of the Drawable to check. @@ -563,16 +598,101 @@ RenderWebGL.prototype._drawThese = function( twgl.setBuffersAndAttributes(gl, currentShader, this._bufferInfo); twgl.setUniforms(currentShader, {u_projectionMatrix: projection}); twgl.setUniforms(currentShader, {u_fudge: window.fudge || 0}); - - // TODO: should these be set after the Drawable's uniforms? - // That would allow Drawable-scope uniforms to be overridden... - if (extraUniforms) { - twgl.setUniforms(currentShader, extraUniforms); - } } twgl.setUniforms(currentShader, drawable.getUniforms()); + // Apply extra uniforms after the Drawable's, to allow overwriting. + if (extraUniforms) { + twgl.setUniforms(currentShader, extraUniforms); + } + twgl.drawBufferInfo(gl, gl.TRIANGLES, this._bufferInfo); } }; + +/** + * Get the convex hull points for a particular Drawable. + * To do this, draw the Drawable unrotated, unscaled, and untranslated. + * Read back the pixels and find all boundary points. + * Finally, apply a convex hull algorithm to simplify the set. + * @param {int} drawablesID The Drawable IDs calculate convex hull for. + * @return {Array.>} points Convex hull points, as [[x, y], ...] + */ +RenderWebGL.prototype._getConvexHullPointsForDrawable = function (drawableID) { + const drawable = Drawable.getDrawableByID(drawableID); + const [width, height] = drawable._uniforms.u_skinSize; + // No points in the hull if invisible or size is 0. + if (!drawable.getVisible() || width == 0 || height == 0) { + return []; + } + + // Only draw to the size of the untransformed drawable. + const gl = this._gl; + twgl.bindFramebufferInfo(gl, this._queryBufferInfo); + gl.viewport(0, 0, width, height); + + // Clear the canvas with Drawable.NONE. + const noneColor = Drawable.color4fFromID(Drawable.NONE); + gl.clearColor.apply(gl, noneColor); + gl.clear(gl.COLOR_BUFFER_BIT); + + // Overwrite the model matrix to be unrotated, unscaled, untranslated. + let modelMatrix = twgl.m4.identity(); + twgl.m4.rotateZ(modelMatrix, Math.PI, modelMatrix); + twgl.m4.scale(modelMatrix, [width, height], modelMatrix); + + const projection = twgl.m4.ortho( + -0.5 * width, 0.5 * width, + -0.5 * height, 0.5 * height, + -1, 1 + ); + + this._drawThese([drawableID], + ShaderManager.DRAW_MODE.silhouette, + projection, + undefined, + {u_modelMatrix: modelMatrix} + ); + + const pixels = new Buffer(width * height * 4); + gl.readPixels( + 0, 0, width, height, + gl.RGBA, gl.UNSIGNED_BYTE, pixels); + + // Known boundary points on left/right edges of pixels. + let boundaryPoints = []; + + /** + * Helper method to look up a pixel. + * @param {int} x X coordinate of the pixel in `pixels`. + * @param {int} y Y coordinate of the pixel in `pixels`. + * @return Known ID at that pixel, or Drawable.NONE. + */ + const _getPixel = function (x, y) { + var pixelBase = ((width * y) + x) * 4; + return Drawable.color4bToID( + pixels[pixelBase], + pixels[pixelBase + 1], + pixels[pixelBase + 2], + pixels[pixelBase + 3]); + }; + for (let y = 0; y <= height; y++) { + // Scan from left. + for (let x = 0; x < width; x++) { + if (_getPixel(x, y) > Drawable.NONE) { + boundaryPoints.push([x, y]); + break; + } + } + // Scan from right. + for (let x = width - 1; x >= 0; x--) { + if (_getPixel(x, y) > Drawable.NONE) { + boundaryPoints.push([x, y]); + break; + } + } + } + // Simplify boundary points using convex hull. + return hull(boundaryPoints, Infinity); +}; From fa03f0d0548476f98680f50f64d2a0eaacd5e03c Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Tue, 11 Oct 2016 18:43:57 -0400 Subject: [PATCH 1800/1971] Expose skin/costume size (#45) * Expose skin/costume size * Return skin size as a copy --- packages/scratch-render/src/RenderWebGL.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 3bd2a3aad0..d54540a958 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -225,6 +225,15 @@ RenderWebGL.prototype.getBounds = function (drawableID) { return bounds; }; +/** + * Get the current skin (costume) size of a Drawable. + * @param {int} drawableID The ID of the Drawable to measure. + * @return {Array.} Skin size, width and height. + */ +RenderWebGL.prototype.getSkinSize = function (drawableID) { + const drawable = Drawable.getDrawableByID(drawableID); + return drawable.getSkinSize(); +}; /** * Check if a particular Drawable is touching a particular color. From 07e87d5d344be1f60e6474d555c6d5da670b7db3 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Wed, 12 Oct 2016 13:46:32 -0400 Subject: [PATCH 1801/1971] Math.max mix-up Bounding to a minimum, we should use `Math.max`... --- packages/scratch-render/src/RenderWebGL.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index d54540a958..ea8e1b74eb 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -167,7 +167,7 @@ RenderWebGL.prototype.setDrawableOrder = function ( newIndex += oldIndex; } if (opt_min) { - newIndex = Math.min(newIndex, opt_min); + newIndex = Math.max(newIndex, opt_min); } newIndex = Math.max(newIndex, 0); // Insert at new index. From adcce05b395a07dcde28ed488a83680daf5226e1 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Wed, 12 Oct 2016 14:32:14 -0400 Subject: [PATCH 1802/1971] Fix y coordinates of bounding box (#46) --- packages/scratch-render/src/RenderWebGL.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index ea8e1b74eb..865ea89e52 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -217,9 +217,9 @@ RenderWebGL.prototype.getBounds = function (drawableID) { let pr = window.devicePixelRatio; context.strokeRect( pr * (bounds.left + this._nativeSize[0]/2), - pr * (bounds.top + this._nativeSize[1]/2), + pr * (-bounds.top + this._nativeSize[1]/2), pr * (bounds.right - bounds.left), - pr * (bounds.bottom - bounds.top) + pr * (-bounds.bottom + bounds.top) ); } return bounds; From 376132053b44a02efd130b5ab110f2abb722c57a Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Mon, 24 Oct 2016 13:19:20 -0400 Subject: [PATCH 1803/1971] Bounding-box optimizations for touching color, touching drawables (#55) * Add Rectangle utility and use it in Drawable.getBounds * Add `getAABB`, `getFastBounds`. * Add width and height getters to Rectangle * Add rectangle clamp * Optimized isTouchingColor * Optimized isTouchingDrawables * Rectangle.ceil -> Rectangle.snapToInt * Refactor to common _touchingQueryCandidates * Split helper into two --- packages/scratch-render/src/RenderWebGL.js | 128 ++++++++++++++++----- 1 file changed, 98 insertions(+), 30 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 865ea89e52..ba70e76b05 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -243,16 +243,25 @@ RenderWebGL.prototype.getSkinSize = function (drawableID) { * @returns {Boolean} True iff the Drawable is touching the color. */ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3b, mask3b) { + const gl = this._gl; + twgl.bindFramebufferInfo(gl, this._queryBufferInfo); - var gl = this._gl; + let bounds = this._touchingBounds(drawableID); + if (!bounds) { + return; + } + let candidateIDs = this._filterCandidatesTouching( + drawableID, this._drawables, bounds); + if (!candidateIDs) { + return; + } - twgl.bindFramebufferInfo(gl, this._queryBufferInfo); - // TODO: restrict to only the area overlapped by the target Drawable - // - limit size of viewport to the AABB around the target Drawable - // - draw only the Drawables which could overlap the target Drawable - // - read only the pixels in the AABB around the target Drawable - gl.viewport(0, 0, this._nativeSize[0], this._nativeSize[1]); + // Limit size of viewport to the bounds around the target Drawable, + // and create the projection matrix for the draw. + gl.viewport(0, 0, bounds.width, bounds.height); + const projection = twgl.m4.ortho( + bounds.left, bounds.right, bounds.bottom, bounds.top, -1, 1); gl.clearColor.apply(gl, this._backgroundColor); gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); @@ -275,7 +284,7 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3b, mask3b) { mask3b ? ShaderManager.DRAW_MODE.colorMask : ShaderManager.DRAW_MODE.silhouette, - this._projection, + projection, undefined, extraUniforms); @@ -283,10 +292,8 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3b, mask3b) { gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); gl.colorMask(true, true, true, true); - // TODO: only draw items which could possibly overlap target Drawable - // It might work to use the filter function for that this._drawThese( - this._drawables, ShaderManager.DRAW_MODE.default, this._projection, + candidateIDs, ShaderManager.DRAW_MODE.default, projection, function (testID) { return testID != drawableID; }); @@ -296,17 +303,17 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3b, mask3b) { gl.disable(gl.STENCIL_TEST); } - var pixels = new Buffer(this._nativeSize[0] * this._nativeSize[1] * 4); + var pixels = new Buffer(bounds.width * bounds.height * 4); gl.readPixels( - 0, 0, this._nativeSize[0], this._nativeSize[1], + 0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); if (this._debugCanvas) { - this._debugCanvas.width = this._nativeSize[0]; - this._debugCanvas.height = this._nativeSize[1]; + this._debugCanvas.width = bounds.width; + this._debugCanvas.height = bounds.height; var context = this._debugCanvas.getContext('2d'); var imageData = context.getImageData( - 0, 0, this._nativeSize[0], this._nativeSize[1]); + 0, 0, bounds.width, bounds.height); for (var i = 0, bytes = pixels.length; i < bytes; ++i) { imageData.data[i] = pixels[i]; } @@ -341,11 +348,21 @@ RenderWebGL.prototype.isTouchingDrawables = function(drawableID, candidateIDs) { twgl.bindFramebufferInfo(gl, this._queryBufferInfo); - // TODO: restrict to only the area overlapped by the target Drawable - // - limit size of viewport to the AABB around the target Drawable - // - draw only the Drawables which could overlap the target Drawable - // - read only the pixels in the AABB around the target Drawable - gl.viewport(0, 0, this._nativeSize[0], this._nativeSize[1]); + let bounds = this._touchingBounds(drawableID); + if (!bounds) { + return; + } + candidateIDs = this._filterCandidatesTouching( + drawableID, candidateIDs, bounds); + if (!candidateIDs) { + return; + } + + // Limit size of viewport to the bounds around the target Drawable, + // and create the projection matrix for the draw. + gl.viewport(0, 0, bounds.width, bounds.height); + const projection = twgl.m4.ortho( + bounds.left, bounds.right, bounds.bottom, bounds.top, -1, 1); const noneColor = Drawable.color4fFromID(Drawable.NONE); gl.clearColor.apply(gl, noneColor); @@ -357,34 +374,35 @@ RenderWebGL.prototype.isTouchingDrawables = function(drawableID, candidateIDs) { gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); gl.colorMask(false, false, false, false); this._drawThese( - [drawableID], ShaderManager.DRAW_MODE.silhouette, this._projection + [drawableID], ShaderManager.DRAW_MODE.silhouette, projection ); gl.stencilFunc(gl.EQUAL, 1, 1); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); gl.colorMask(true, true, true, true); - // TODO: only draw items which could possibly overlap target Drawable - // It might work to use the filter function for that this._drawThese( - candidateIDs, ShaderManager.DRAW_MODE.silhouette, this._projection + candidateIDs, ShaderManager.DRAW_MODE.silhouette, projection, + function (testID) { + return testID != drawableID; + } ); } finally { gl.colorMask(true, true, true, true); gl.disable(gl.STENCIL_TEST); } - let pixels = new Buffer(this._nativeSize[0] * this._nativeSize[1] * 4); + let pixels = new Buffer(bounds.width * bounds.height * 4); gl.readPixels( - 0, 0, this._nativeSize[0], this._nativeSize[1], + 0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); if (this._debugCanvas) { - this._debugCanvas.width = this._nativeSize[0]; - this._debugCanvas.height = this._nativeSize[1]; + this._debugCanvas.width = bounds.width; + this._debugCanvas.height = bounds.height; const context = this._debugCanvas.getContext('2d'); let imageData = context.getImageData( - 0, 0, this._nativeSize[0], this._nativeSize[1]); + 0, 0, bounds.width, bounds.height); for (let i = 0, bytes = pixels.length; i < bytes; ++i) { imageData.data[i] = pixels[i]; } @@ -500,6 +518,56 @@ RenderWebGL.prototype.pick = function ( return hit | 0; }; +/** + * Get the candidate bounding box for a touching query. + * @param {int} drawableID ID for drawable of query. + * @return {?Rectangle} Rectangle bounds for touching query, or null. + */ +RenderWebGL.prototype._touchingBounds = function (drawableID) { + const drawable = Drawable.getDrawableByID(drawableID); + const bounds = drawable.getFastBounds(); + + // Limit queries to the stage size. + bounds.clamp(this._xLeft, this._xRight, this._yBottom, this._yTop); + + // Use integer coordinates for queries - weird things happen + // when you provide float width/heights to gl.viewport and projection. + bounds.snapToInt(); + + if (bounds.width == 0 || bounds.height == 0) { + // No space to query. + return null; + } + return bounds; +}; + +/** + * Filter a list of candidates for a touching query into only those that + * could possibly intersect the given bounds. + * @param {int} drawableID ID for drawable of query. + * @param {Array.} candidateIDs Candidates for touching query. + * @param {Rectangle} Bounds to limit candidates to. + * @return {?Array.} Filtered candidateIDs, or null if none. + */ +RenderWebGL.prototype._filterCandidatesTouching = function ( + drawableID, candidateIDs, bounds) { + // Filter candidates by rough bounding box intersection. + // Do this before _drawThese, so we can prevent any GL operations + // and readback by returning early. + candidateIDs = candidateIDs.filter(function (testID) { + if (testID == drawableID) return false; + // Only draw items which could possibly overlap target Drawable. + let candidate = Drawable.getDrawableByID(testID); + let candidateBounds = candidate.getFastBounds(); + return bounds.intersects(candidateBounds); + }); + if (candidateIDs.length == 0) { + // No possible intersections based on bounding boxes. + return null; + } + return candidateIDs; +}; + /** * Update the position, direction, scale, or effect properties of this Drawable. * @param {int} drawableID The ID of the Drawable to update. From 924706f70fa4cfd53cbd651c96eca8d10de7c68c Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Tue, 13 Dec 2016 10:04:06 -0800 Subject: [PATCH 1804/1971] Merge pull request #64 from cwillisf/structural-updates Update configuration to match other Scratch 3.0 projects --- packages/scratch-render/src/RenderWebGL.js | 240 ++++++++++----------- 1 file changed, 118 insertions(+), 122 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index ba70e76b05..e54a4a44e6 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1,8 +1,8 @@ -var hull = require('hull.js'); -var twgl = require('twgl.js'); +const hull = require('hull.js'); +const twgl = require('twgl.js'); -var Drawable = require('./Drawable'); -var ShaderManager = require('./ShaderManager'); +const Drawable = require('./Drawable'); +const ShaderManager = require('./ShaderManager'); class RenderWebGL { @@ -21,7 +21,7 @@ class RenderWebGL { * @param {int} [yTop=180] The y-coordinate of the top edge. * @constructor */ - constructor(canvas, xLeft, xRight, yBottom, yTop) { + constructor (canvas, xLeft, xRight, yBottom, yTop) { // TODO: remove? twgl.setDefaults({crossOrigin: true}); @@ -37,7 +37,7 @@ class RenderWebGL { this.resize(this._nativeSize[0], this._nativeSize[1]); this._createQueryBuffers(); - var gl = this._gl; + const gl = this._gl; gl.disable(gl.DEPTH_TEST); gl.enable(gl.BLEND); // TODO: disable when no partial transparency? gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE); @@ -63,9 +63,9 @@ RenderWebGL.MAX_TOUCH_SIZE = [3, 3]; RenderWebGL.TOLERANCE_TOUCHING_COLOR = 2; -/******** - * Functions called only locally: these are not available from a worker. - ********/ +/* ******* + * Functions called only locally: these are not available from a worker. + *********/ /** * Set the physical size of the stage in device-independent pixels. @@ -74,7 +74,7 @@ RenderWebGL.TOLERANCE_TOUCHING_COLOR = 2; * @param {int} pixelsTall The desired height in device-independent pixels. */ RenderWebGL.prototype.resize = function (pixelsWide, pixelsTall) { - var pixelRatio = window.devicePixelRatio || 1; + const pixelRatio = window.devicePixelRatio || 1; this._gl.canvas.width = pixelsWide * pixelRatio; this._gl.canvas.height = pixelsTall * pixelRatio; }; @@ -86,7 +86,7 @@ RenderWebGL.prototype.resize = function (pixelsWide, pixelsTall) { * @param {number} green The green component for the background. * @param {number} blue The blue component for the background. */ -RenderWebGL.prototype.setBackgroundColor = function(red, green, blue) { +RenderWebGL.prototype.setBackgroundColor = function (red, green, blue) { this._backgroundColor = [red, green, blue, 1]; }; @@ -121,8 +121,8 @@ RenderWebGL.prototype.setStageSize = function (xLeft, xRight, yBottom, yTop) { * @returns {int} The ID of the new Drawable. */ RenderWebGL.prototype.createDrawable = function () { - var drawable = new Drawable(this._gl); - var drawableID = drawable.getID(); + const drawable = new Drawable(this._gl); + const drawableID = drawable.getID(); this._drawables.push(drawableID); return drawableID; }; @@ -130,10 +130,10 @@ RenderWebGL.prototype.createDrawable = function () { /** * Destroy a Drawable, removing it from the scene. * @param {int} drawableID The ID of the Drawable to remove. - * @returns {Boolean} True iff the drawable was found and removed. + * @returns {boolean} True iff the drawable was found and removed. */ RenderWebGL.prototype.destroyDrawable = function (drawableID) { - var index = this._drawables.indexOf(drawableID); + const index = this._drawables.indexOf(drawableID); if (index >= 0) { Drawable.getDrawableByID(drawableID).dispose(); this._drawables.splice(index, 1); @@ -150,24 +150,24 @@ RenderWebGL.prototype.destroyDrawable = function (drawableID) { * "go to back": setDrawableOrder(id, 1); (assuming stage at 0). * "go to front": setDrawableOrder(id, Infinity); * @param {int} drawableID ID of Drawable to reorder. - * @param {Number} order New absolute order or relative order adjusment. - * @param {Boolean=} opt_isRelative If set, `order` refers to a relative change. - * @param {Number=} opt_min If set, order constrained to be at least `opt_min`. - * @return {?Number} New order if changed, or null. + * @param {number} order New absolute order or relative order adjusment. + * @param {boolean=} optIsRelative If set, `order` refers to a relative change. + * @param {number=} optMin If set, order constrained to be at least `optMin`. + * @return {?number} New order if changed, or null. */ RenderWebGL.prototype.setDrawableOrder = function ( - drawableID, order, opt_isRelative, opt_min) { - var oldIndex = this._drawables.indexOf(drawableID); + drawableID, order, optIsRelative, optMin) { + const oldIndex = this._drawables.indexOf(drawableID); if (oldIndex >= 0) { // Remove drawable from the list. - var drawable = this._drawables.splice(oldIndex, 1)[0]; + const drawable = this._drawables.splice(oldIndex, 1)[0]; // Determine new index. - var newIndex = order; - if (opt_isRelative) { + let newIndex = order; + if (optIsRelative) { newIndex += oldIndex; } - if (opt_min) { - newIndex = Math.max(newIndex, opt_min); + if (optMin) { + newIndex = Math.max(newIndex, optMin); } newIndex = Math.max(newIndex, 0); // Insert at new index. @@ -181,7 +181,7 @@ RenderWebGL.prototype.setDrawableOrder = function ( * Draw all current drawables and present the frame on the canvas. */ RenderWebGL.prototype.draw = function () { - var gl = this._gl; + const gl = this._gl; twgl.bindFramebufferInfo(gl, null); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); @@ -196,7 +196,7 @@ RenderWebGL.prototype.draw = function () { /** * Get the precise bounds for a Drawable. * @param {int} drawableID ID of Drawable to get bounds for. - * @return {Object} Bounds for a tight box around the Drawable. + * @return {object} Bounds for a tight box around the Drawable. */ RenderWebGL.prototype.getBounds = function (drawableID) { const drawable = Drawable.getDrawableByID(drawableID); @@ -205,19 +205,19 @@ RenderWebGL.prototype.getBounds = function (drawableID) { const points = this._getConvexHullPointsForDrawable(drawableID); drawable.setConvexHullPoints(points); } - let bounds = drawable.getBounds(); + const bounds = drawable.getBounds(); // In debug mode, draw the bounds. if (this._debugCanvas) { - let gl = this._gl; + const gl = this._gl; this._debugCanvas.width = gl.canvas.width; this._debugCanvas.height = gl.canvas.height; - let context = this._debugCanvas.getContext('2d'); + const context = this._debugCanvas.getContext('2d'); context.drawImage(gl.canvas, 0, 0); context.strokeStyle = '#FF0000'; - let pr = window.devicePixelRatio; + const pr = window.devicePixelRatio; context.strokeRect( - pr * (bounds.left + this._nativeSize[0]/2), - pr * (-bounds.top + this._nativeSize[1]/2), + pr * (bounds.left + (this._nativeSize[0] / 2)), + pr * (-bounds.top + (this._nativeSize[1] / 2)), pr * (bounds.right - bounds.left), pr * (-bounds.bottom + bounds.top) ); @@ -240,17 +240,17 @@ RenderWebGL.prototype.getSkinSize = function (drawableID) { * @param {int} drawableID The ID of the Drawable to check. * @param {int[]} color3b Test if the Drawable is touching this color. * @param {int[]} [mask3b] Optionally mask the check to this part of Drawable. - * @returns {Boolean} True iff the Drawable is touching the color. + * @returns {boolean} True iff the Drawable is touching the color. */ -RenderWebGL.prototype.isTouchingColor = function(drawableID, color3b, mask3b) { +RenderWebGL.prototype.isTouchingColor = function (drawableID, color3b, mask3b) { const gl = this._gl; twgl.bindFramebufferInfo(gl, this._queryBufferInfo); - let bounds = this._touchingBounds(drawableID); + const bounds = this._touchingBounds(drawableID); if (!bounds) { return; } - let candidateIDs = this._filterCandidatesTouching( + const candidateIDs = this._filterCandidatesTouching( drawableID, this._drawables, bounds); if (!candidateIDs) { return; @@ -266,7 +266,7 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3b, mask3b) { gl.clearColor.apply(gl, this._backgroundColor); gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); - var extraUniforms; + let extraUniforms; if (mask3b) { extraUniforms = { u_colorMask: [mask3b[0] / 255, mask3b[1] / 255, mask3b[2] / 255], @@ -285,7 +285,7 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3b, mask3b) { ShaderManager.DRAW_MODE.colorMask : ShaderManager.DRAW_MODE.silhouette, projection, - undefined, + null, extraUniforms); gl.stencilFunc(gl.EQUAL, 1, 1); @@ -294,16 +294,14 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3b, mask3b) { this._drawThese( candidateIDs, ShaderManager.DRAW_MODE.default, projection, - function (testID) { - return testID != drawableID; - }); - } - finally { + testID => testID !== drawableID + ); + } finally { gl.colorMask(true, true, true, true); gl.disable(gl.STENCIL_TEST); } - var pixels = new Buffer(bounds.width * bounds.height * 4); + const pixels = new Buffer(bounds.width * bounds.height * 4); gl.readPixels( 0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); @@ -311,19 +309,19 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3b, mask3b) { if (this._debugCanvas) { this._debugCanvas.width = bounds.width; this._debugCanvas.height = bounds.height; - var context = this._debugCanvas.getContext('2d'); - var imageData = context.getImageData( + const context = this._debugCanvas.getContext('2d'); + const imageData = context.getImageData( 0, 0, bounds.width, bounds.height); - for (var i = 0, bytes = pixels.length; i < bytes; ++i) { + for (let i = 0, bytes = pixels.length; i < bytes; ++i) { imageData.data[i] = pixels[i]; } context.putImageData(imageData, 0, 0); } - for (var pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { - var pixelDistanceR = Math.abs(pixels[pixelBase] - color3b[0]); - var pixelDistanceG = Math.abs(pixels[pixelBase + 1] - color3b[1]); - var pixelDistanceB = Math.abs(pixels[pixelBase + 2] - color3b[2]); + for (let pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { + const pixelDistanceR = Math.abs(pixels[pixelBase] - color3b[0]); + const pixelDistanceG = Math.abs(pixels[pixelBase + 1] - color3b[1]); + const pixelDistanceB = Math.abs(pixels[pixelBase + 2] - color3b[2]); if (pixelDistanceR <= RenderWebGL.TOLERANCE_TOUCHING_COLOR && pixelDistanceG <= RenderWebGL.TOLERANCE_TOUCHING_COLOR && @@ -339,16 +337,16 @@ RenderWebGL.prototype.isTouchingColor = function(drawableID, color3b, mask3b) { * Check if a particular Drawable is touching any in a set of Drawables. * @param {int} drawableID The ID of the Drawable to check. * @param {int[]} candidateIDs The Drawable IDs to check, otherwise all. - * @returns {Boolean} True iff the Drawable is touching one of candidateIDs. + * @returns {boolean} True iff the Drawable is touching one of candidateIDs. */ -RenderWebGL.prototype.isTouchingDrawables = function(drawableID, candidateIDs) { +RenderWebGL.prototype.isTouchingDrawables = function (drawableID, candidateIDs) { candidateIDs = candidateIDs || this._drawables; const gl = this._gl; twgl.bindFramebufferInfo(gl, this._queryBufferInfo); - let bounds = this._touchingBounds(drawableID); + const bounds = this._touchingBounds(drawableID); if (!bounds) { return; } @@ -383,16 +381,14 @@ RenderWebGL.prototype.isTouchingDrawables = function(drawableID, candidateIDs) { this._drawThese( candidateIDs, ShaderManager.DRAW_MODE.silhouette, projection, - function (testID) { - return testID != drawableID; - } + testID => testID !== drawableID ); } finally { gl.colorMask(true, true, true, true); gl.disable(gl.STENCIL_TEST); } - let pixels = new Buffer(bounds.width * bounds.height * 4); + const pixels = new Buffer(bounds.width * bounds.height * 4); gl.readPixels( 0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); @@ -401,7 +397,7 @@ RenderWebGL.prototype.isTouchingDrawables = function(drawableID, candidateIDs) { this._debugCanvas.width = bounds.width; this._debugCanvas.height = bounds.height; const context = this._debugCanvas.getContext('2d'); - let imageData = context.getImageData( + const imageData = context.getImageData( 0, 0, bounds.width, bounds.height); for (let i = 0, bytes = pixels.length; i < bytes; ++i) { imageData.data[i] = pixels[i]; @@ -410,7 +406,7 @@ RenderWebGL.prototype.isTouchingDrawables = function(drawableID, candidateIDs) { } for (let pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { - let pixelID = Drawable.color4bToID( + const pixelID = Drawable.color4bToID( pixels[pixelBase], pixels[pixelBase + 1], pixels[pixelBase + 2], @@ -435,14 +431,14 @@ RenderWebGL.prototype.isTouchingDrawables = function(drawableID, candidateIDs) { */ RenderWebGL.prototype.pick = function ( centerX, centerY, touchWidth, touchHeight, candidateIDs) { - var gl = this._gl; + const gl = this._gl; touchWidth = touchWidth || 1; touchHeight = touchHeight || 1; candidateIDs = candidateIDs || this._drawables; - var clientToGLX = gl.canvas.width / gl.canvas.clientWidth; - var clientToGLY = gl.canvas.height / gl.canvas.clientHeight; + const clientToGLX = gl.canvas.width / gl.canvas.clientWidth; + const clientToGLY = gl.canvas.height / gl.canvas.clientHeight; centerX *= clientToGLX; centerY *= clientToGLY; @@ -454,50 +450,50 @@ RenderWebGL.prototype.pick = function ( touchHeight = Math.max(1, Math.min(touchHeight, RenderWebGL.MAX_TOUCH_SIZE[1])); - var pixelLeft = Math.floor(centerX - Math.floor(touchWidth / 2) + 0.5); - var pixelRight = Math.floor(centerX + Math.ceil(touchWidth / 2) + 0.5); - var pixelTop = Math.floor(centerY - Math.floor(touchHeight / 2) + 0.5); - var pixelBottom = Math.floor(centerY + Math.ceil(touchHeight / 2) + 0.5); + const pixelLeft = Math.floor(centerX - Math.floor(touchWidth / 2) + 0.5); + const pixelRight = Math.floor(centerX + Math.ceil(touchWidth / 2) + 0.5); + const pixelTop = Math.floor(centerY - Math.floor(touchHeight / 2) + 0.5); + const pixelBottom = Math.floor(centerY + Math.ceil(touchHeight / 2) + 0.5); twgl.bindFramebufferInfo(gl, this._pickBufferInfo); gl.viewport(0, 0, touchWidth, touchHeight); - var noneColor = Drawable.color4fFromID(Drawable.NONE); + const noneColor = Drawable.color4fFromID(Drawable.NONE); gl.clearColor.apply(gl, noneColor); gl.clear(gl.COLOR_BUFFER_BIT); - var widthPerPixel = (this._xRight - this._xLeft) / this._gl.canvas.width; - var heightPerPixel = (this._yBottom - this._yTop) / this._gl.canvas.height; + const widthPerPixel = (this._xRight - this._xLeft) / this._gl.canvas.width; + const heightPerPixel = (this._yBottom - this._yTop) / this._gl.canvas.height; - var pickLeft = this._xLeft + pixelLeft * widthPerPixel; - var pickRight = this._xLeft + pixelRight * widthPerPixel; - var pickTop = this._yTop + pixelTop * heightPerPixel; - var pickBottom = this._yTop + pixelBottom * heightPerPixel; + const pickLeft = this._xLeft + (pixelLeft * widthPerPixel); + const pickRight = this._xLeft + (pixelRight * widthPerPixel); + const pickTop = this._yTop + (pixelTop * heightPerPixel); + const pickBottom = this._yTop + (pixelBottom * heightPerPixel); - var projection = twgl.m4.ortho( + const projection = twgl.m4.ortho( pickLeft, pickRight, pickTop, pickBottom, -1, 1); this._drawThese( candidateIDs, ShaderManager.DRAW_MODE.silhouette, projection); - var pixels = new Buffer(touchWidth * touchHeight * 4); + const pixels = new Buffer(touchWidth * touchHeight * 4); gl.readPixels( 0, 0, touchWidth, touchHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels); if (this._debugCanvas) { this._debugCanvas.width = touchWidth; this._debugCanvas.height = touchHeight; - var context = this._debugCanvas.getContext('2d'); - var imageData = context.getImageData(0, 0, touchWidth, touchHeight); - for (var i = 0, bytes = pixels.length; i < bytes; ++i) { + const context = this._debugCanvas.getContext('2d'); + const imageData = context.getImageData(0, 0, touchWidth, touchHeight); + for (let i = 0, bytes = pixels.length; i < bytes; ++i) { imageData.data[i] = pixels[i]; } context.putImageData(imageData, 0, 0); } - var hits = {}; - for (var pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { - var pixelID = Drawable.color4bToID( + const hits = {}; + for (let pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { + const pixelID = Drawable.color4bToID( pixels[pixelBase], pixels[pixelBase + 1], pixels[pixelBase + 2], @@ -508,8 +504,8 @@ RenderWebGL.prototype.pick = function ( // Bias toward selecting anything over nothing hits[Drawable.NONE] = 0; - var hit = Drawable.NONE; - for (var hitID in hits) { + let hit = Drawable.NONE; + for (const hitID in hits) { if (hits.hasOwnProperty(hitID) && (hits[hitID] > hits[hit])) { hit = hitID; } @@ -534,7 +530,7 @@ RenderWebGL.prototype._touchingBounds = function (drawableID) { // when you provide float width/heights to gl.viewport and projection. bounds.snapToInt(); - if (bounds.width == 0 || bounds.height == 0) { + if (bounds.width === 0 || bounds.height === 0) { // No space to query. return null; } @@ -544,9 +540,9 @@ RenderWebGL.prototype._touchingBounds = function (drawableID) { /** * Filter a list of candidates for a touching query into only those that * could possibly intersect the given bounds. - * @param {int} drawableID ID for drawable of query. - * @param {Array.} candidateIDs Candidates for touching query. - * @param {Rectangle} Bounds to limit candidates to. + * @param {int} drawableID - ID for drawable of query. + * @param {Array.} candidateIDs - Candidates for touching query. + * @param {Rectangle} bounds - Bounds to limit candidates to. * @return {?Array.} Filtered candidateIDs, or null if none. */ RenderWebGL.prototype._filterCandidatesTouching = function ( @@ -554,14 +550,14 @@ RenderWebGL.prototype._filterCandidatesTouching = function ( // Filter candidates by rough bounding box intersection. // Do this before _drawThese, so we can prevent any GL operations // and readback by returning early. - candidateIDs = candidateIDs.filter(function (testID) { - if (testID == drawableID) return false; + candidateIDs = candidateIDs.filter(testID => { + if (testID === drawableID) return false; // Only draw items which could possibly overlap target Drawable. - let candidate = Drawable.getDrawableByID(testID); - let candidateBounds = candidate.getFastBounds(); + const candidate = Drawable.getDrawableByID(testID); + const candidateBounds = candidate.getFastBounds(); return bounds.intersects(candidateBounds); }); - if (candidateIDs.length == 0) { + if (candidateIDs.length === 0) { // No possible intersections based on bounding boxes. return null; } @@ -571,24 +567,24 @@ RenderWebGL.prototype._filterCandidatesTouching = function ( /** * Update the position, direction, scale, or effect properties of this Drawable. * @param {int} drawableID The ID of the Drawable to update. -* @param {Object.} properties The new property values to set. +* @param {object.} properties The new property values to set. */ RenderWebGL.prototype.updateDrawableProperties = function ( drawableID, properties) { - var drawable = Drawable.getDrawableByID(drawableID); + const drawable = Drawable.getDrawableByID(drawableID); drawable.updateProperties(properties); }; -/******** - * Truly internal functions: these support the functions above. - ********/ +/* ******** + * Truly internal functions: these support the functions above. + ********/ /** * Build geometry (vertex and index) buffers. * @private */ RenderWebGL.prototype._createGeometry = function () { - var quad = { + const quad = { a_position: { numComponents: 2, data: [ @@ -623,10 +619,10 @@ RenderWebGL.prototype._createGeometry = function () { * @private */ RenderWebGL.prototype._createQueryBuffers = function () { - var gl = this._gl; - var attachments = [ - {format: gl.RGBA }, - {format: gl.DEPTH_STENCIL } + const gl = this._gl; + const attachments = [ + {format: gl.RGBA}, + {format: gl.DEPTH_STENCIL} ]; this._pickBufferInfo = twgl.createFramebufferInfo( @@ -648,28 +644,28 @@ RenderWebGL.prototype._createQueryBuffers = function () { * @param {Object.} [extraUniforms] Extra uniforms for the shaders. * @private */ -RenderWebGL.prototype._drawThese = function( +RenderWebGL.prototype._drawThese = function ( drawables, drawMode, projection, filter, extraUniforms) { - var gl = this._gl; - var currentShader = null; + const gl = this._gl; + let currentShader = null; - var numDrawables = drawables.length; - for (var drawableIndex = 0; drawableIndex < numDrawables; ++drawableIndex) { - var drawableID = drawables[drawableIndex]; + const numDrawables = drawables.length; + for (let drawableIndex = 0; drawableIndex < numDrawables; ++drawableIndex) { + const drawableID = drawables[drawableIndex]; // If we have a filter, check whether the ID fails if (filter && !filter(drawableID)) continue; - var drawable = Drawable.getDrawableByID(drawableID); + const drawable = Drawable.getDrawableByID(drawableID); // TODO: check if drawable is inside the viewport before anything else // Hidden drawables (e.g., by a "hide" block) are never drawn. if (!drawable.getVisible()) continue; - var effectBits = drawable.getEnabledEffects(); - var newShader = this._shaderManager.getShader(drawMode, effectBits); - if (currentShader != newShader) { + const effectBits = drawable.getEnabledEffects(); + const newShader = this._shaderManager.getShader(drawMode, effectBits); + if (currentShader !== newShader) { currentShader = newShader; gl.useProgram(currentShader.program); twgl.setBuffersAndAttributes(gl, currentShader, this._bufferInfo); @@ -693,14 +689,14 @@ RenderWebGL.prototype._drawThese = function( * To do this, draw the Drawable unrotated, unscaled, and untranslated. * Read back the pixels and find all boundary points. * Finally, apply a convex hull algorithm to simplify the set. - * @param {int} drawablesID The Drawable IDs calculate convex hull for. + * @param {int} drawableID The Drawable IDs calculate convex hull for. * @return {Array.>} points Convex hull points, as [[x, y], ...] */ RenderWebGL.prototype._getConvexHullPointsForDrawable = function (drawableID) { const drawable = Drawable.getDrawableByID(drawableID); const [width, height] = drawable._uniforms.u_skinSize; // No points in the hull if invisible or size is 0. - if (!drawable.getVisible() || width == 0 || height == 0) { + if (!drawable.getVisible() || width === 0 || height === 0) { return []; } @@ -715,7 +711,7 @@ RenderWebGL.prototype._getConvexHullPointsForDrawable = function (drawableID) { gl.clear(gl.COLOR_BUFFER_BIT); // Overwrite the model matrix to be unrotated, unscaled, untranslated. - let modelMatrix = twgl.m4.identity(); + const modelMatrix = twgl.m4.identity(); twgl.m4.rotateZ(modelMatrix, Math.PI, modelMatrix); twgl.m4.scale(modelMatrix, [width, height], modelMatrix); @@ -728,7 +724,7 @@ RenderWebGL.prototype._getConvexHullPointsForDrawable = function (drawableID) { this._drawThese([drawableID], ShaderManager.DRAW_MODE.silhouette, projection, - undefined, + null, {u_modelMatrix: modelMatrix} ); @@ -738,16 +734,16 @@ RenderWebGL.prototype._getConvexHullPointsForDrawable = function (drawableID) { gl.RGBA, gl.UNSIGNED_BYTE, pixels); // Known boundary points on left/right edges of pixels. - let boundaryPoints = []; + const boundaryPoints = []; /** * Helper method to look up a pixel. * @param {int} x X coordinate of the pixel in `pixels`. * @param {int} y Y coordinate of the pixel in `pixels`. - * @return Known ID at that pixel, or Drawable.NONE. + * @return {int} Known ID at that pixel, or Drawable.NONE. */ const _getPixel = function (x, y) { - var pixelBase = ((width * y) + x) * 4; + const pixelBase = ((width * y) + x) * 4; return Drawable.color4bToID( pixels[pixelBase], pixels[pixelBase + 1], From 72c25417b0c0e729dff87bbb8925a11c6610718a Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Mon, 19 Dec 2016 14:53:38 -0800 Subject: [PATCH 1805/1971] Merge pull request #65 from cwillisf/more-es6 Convert class methods to ES6 style --- packages/scratch-render/src/RenderWebGL.js | 1238 ++++++++++---------- 1 file changed, 596 insertions(+), 642 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index e54a4a44e6..b0ff4fbf3e 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -4,6 +4,21 @@ const twgl = require('twgl.js'); const Drawable = require('./Drawable'); const ShaderManager = require('./ShaderManager'); +/** + * Maximum touch size for a picking check. + * TODO: Figure out a reasonable max size. Maybe this should be configurable? + * @type {int[]} + */ +const MAX_TOUCH_SIZE = [3, 3]; + +/** + * "touching {color}?" or "{color} touching {color}?" tests will be true if the + * target is touching a color whose components are each within this tolerance of + * the corresponding component of the query color. + * @type {int} between 0 (exact matches only) and 255 (match anything). + */ +const TOLERANCE_TOUCHING_COLOR = 2; + class RenderWebGL { /** @@ -32,8 +47,7 @@ class RenderWebGL { this._createGeometry(); this.setBackgroundColor(1, 1, 1); - this.setStageSize( - xLeft || -240, xRight || 240, yBottom || -180, yTop || 180); + this.setStageSize(xLeft || -240, xRight || 240, yBottom || -180, yTop || 180); this.resize(this._nativeSize[0], this._nativeSize[1]); this._createQueryBuffers(); @@ -43,729 +57,669 @@ class RenderWebGL { gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE); this._shaderManager = new ShaderManager(gl); } -} -module.exports = RenderWebGL; - -/** - * Maximum touch size for a picking check. - * TODO: Figure out a reasonable max size. Maybe this should be configurable? - * @type {int[]} - */ -RenderWebGL.MAX_TOUCH_SIZE = [3, 3]; - -/** - * "touching {color}?" or "{color} touching {color}?" tests will be true if the - * target is touching a color whose components are each within this tolerance of - * the corresponding component of the query color. - * @type {int} between 0 (exact matches only) and 255 (match anything). - */ -RenderWebGL.TOLERANCE_TOUCHING_COLOR = 2; + /** + * Set the physical size of the stage in device-independent pixels. + * This will be multiplied by the device's pixel ratio on high-DPI displays. + * @param {int} pixelsWide The desired width in device-independent pixels. + * @param {int} pixelsTall The desired height in device-independent pixels. + */ + resize (pixelsWide, pixelsTall) { + const pixelRatio = window.devicePixelRatio || 1; + this._gl.canvas.width = pixelsWide * pixelRatio; + this._gl.canvas.height = pixelsTall * pixelRatio; + } + /** + * Set the background color for the stage. The stage will be cleared with this + * color each frame. + * @param {number} red The red component for the background. + * @param {number} green The green component for the background. + * @param {number} blue The blue component for the background. + */ + setBackgroundColor (red, green, blue) { + this._backgroundColor = [red, green, blue, 1]; + } -/* ******* - * Functions called only locally: these are not available from a worker. - *********/ + /** + * Tell the renderer to draw various debug information to the provided canvas + * during certain operations. + * @param {canvas} canvas The canvas to use for debug output. + */ + setDebugCanvas (canvas) { + this._debugCanvas = canvas; + } -/** - * Set the physical size of the stage in device-independent pixels. - * This will be multiplied by the device's pixel ratio on high-DPI displays. - * @param {int} pixelsWide The desired width in device-independent pixels. - * @param {int} pixelsTall The desired height in device-independent pixels. - */ -RenderWebGL.prototype.resize = function (pixelsWide, pixelsTall) { - const pixelRatio = window.devicePixelRatio || 1; - this._gl.canvas.width = pixelsWide * pixelRatio; - this._gl.canvas.height = pixelsTall * pixelRatio; -}; + /** + * Set logical size of the stage in Scratch units. + * @param {int} xLeft The left edge's x-coordinate. Scratch 2 uses -240. + * @param {int} xRight The right edge's x-coordinate. Scratch 2 uses 240. + * @param {int} yBottom The bottom edge's y-coordinate. Scratch 2 uses -180. + * @param {int} yTop The top edge's y-coordinate. Scratch 2 uses 180. + */ + setStageSize (xLeft, xRight, yBottom, yTop) { + this._xLeft = xLeft; + this._xRight = xRight; + this._yBottom = yBottom; + this._yTop = yTop; + this._nativeSize = [Math.abs(xRight - xLeft), Math.abs(yBottom - yTop)]; + this._projection = twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1); + } -/** - * Set the background color for the stage. The stage will be cleared with this - * color each frame. - * @param {number} red The red component for the background. - * @param {number} green The green component for the background. - * @param {number} blue The blue component for the background. - */ -RenderWebGL.prototype.setBackgroundColor = function (red, green, blue) { - this._backgroundColor = [red, green, blue, 1]; -}; + /** + * Create a new Drawable and add it to the scene. + * @returns {int} The ID of the new Drawable. + */ + createDrawable () { + const drawable = new Drawable(this._gl); + const drawableID = drawable.getID(); + this._drawables.push(drawableID); + return drawableID; + } -/** - * Tell the renderer to draw various debug information to the provided canvas - * during certain operations. - * @param {canvas} canvas The canvas to use for debug output. - */ -RenderWebGL.prototype.setDebugCanvas = function (canvas) { - this._debugCanvas = canvas; -}; + /** + * Destroy a Drawable, removing it from the scene. + * @param {int} drawableID The ID of the Drawable to remove. + * @returns {boolean} True iff the drawable was found and removed. + */ + destroyDrawable (drawableID) { + const index = this._drawables.indexOf(drawableID); + if (index >= 0) { + Drawable.getDrawableByID(drawableID).dispose(); + this._drawables.splice(index, 1); + return true; + } + return false; + } -/** - * Set logical size of the stage in Scratch units. - * @param {int} xLeft The left edge's x-coordinate. Scratch 2 uses -240. - * @param {int} xRight The right edge's x-coordinate. Scratch 2 uses 240. - * @param {int} yBottom The bottom edge's y-coordinate. Scratch 2 uses -180. - * @param {int} yTop The top edge's y-coordinate. Scratch 2 uses 180. - */ -RenderWebGL.prototype.setStageSize = function (xLeft, xRight, yBottom, yTop) { - this._xLeft = xLeft; - this._xRight = xRight; - this._yBottom = yBottom; - this._yTop = yTop; - this._nativeSize = [Math.abs(xRight - xLeft), Math.abs(yBottom - yTop)]; - this._projection = twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1); -}; + /** + * Set a drawable's order in the drawable list (effectively, z/layer). + * Can be used to move drawables to absolute positions in the list, + * or relative to their current positions. + * "go back N layers": setDrawableOrder(id, -N, true, 1); (assuming stage at 0). + * "go to back": setDrawableOrder(id, 1); (assuming stage at 0). + * "go to front": setDrawableOrder(id, Infinity); + * @param {int} drawableID ID of Drawable to reorder. + * @param {number} order New absolute order or relative order adjusment. + * @param {boolean=} optIsRelative If set, `order` refers to a relative change. + * @param {number=} optMin If set, order constrained to be at least `optMin`. + * @return {?number} New order if changed, or null. + */ + setDrawableOrder (drawableID, order, optIsRelative, optMin) { + const oldIndex = this._drawables.indexOf(drawableID); + if (oldIndex >= 0) { + // Remove drawable from the list. + const drawable = this._drawables.splice(oldIndex, 1)[0]; + // Determine new index. + let newIndex = order; + if (optIsRelative) { + newIndex += oldIndex; + } + if (optMin) { + newIndex = Math.max(newIndex, optMin); + } + newIndex = Math.max(newIndex, 0); + // Insert at new index. + this._drawables.splice(newIndex, 0, drawable); + return this._drawables.indexOf(drawable); + } + return null; + } + /** + * Draw all current drawables and present the frame on the canvas. + */ + draw () { + const gl = this._gl; -/** - * Create a new Drawable and add it to the scene. - * @returns {int} The ID of the new Drawable. - */ -RenderWebGL.prototype.createDrawable = function () { - const drawable = new Drawable(this._gl); - const drawableID = drawable.getID(); - this._drawables.push(drawableID); - return drawableID; -}; + twgl.bindFramebufferInfo(gl, null); + gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); + gl.clearColor.apply(gl, this._backgroundColor); + gl.clear(gl.COLOR_BUFFER_BIT); -/** - * Destroy a Drawable, removing it from the scene. - * @param {int} drawableID The ID of the Drawable to remove. - * @returns {boolean} True iff the drawable was found and removed. - */ -RenderWebGL.prototype.destroyDrawable = function (drawableID) { - const index = this._drawables.indexOf(drawableID); - if (index >= 0) { - Drawable.getDrawableByID(drawableID).dispose(); - this._drawables.splice(index, 1); - return true; + this._drawThese(this._drawables, ShaderManager.DRAW_MODE.default, this._projection); } - return false; -}; -/** - * Set a drawable's order in the drawable list (effectively, z/layer). - * Can be used to move drawables to absolute positions in the list, - * or relative to their current positions. - * "go back N layers": setDrawableOrder(id, -N, true, 1); (assuming stage at 0). - * "go to back": setDrawableOrder(id, 1); (assuming stage at 0). - * "go to front": setDrawableOrder(id, Infinity); - * @param {int} drawableID ID of Drawable to reorder. - * @param {number} order New absolute order or relative order adjusment. - * @param {boolean=} optIsRelative If set, `order` refers to a relative change. - * @param {number=} optMin If set, order constrained to be at least `optMin`. - * @return {?number} New order if changed, or null. - */ -RenderWebGL.prototype.setDrawableOrder = function ( - drawableID, order, optIsRelative, optMin) { - const oldIndex = this._drawables.indexOf(drawableID); - if (oldIndex >= 0) { - // Remove drawable from the list. - const drawable = this._drawables.splice(oldIndex, 1)[0]; - // Determine new index. - let newIndex = order; - if (optIsRelative) { - newIndex += oldIndex; + /** + * Get the precise bounds for a Drawable. + * @param {int} drawableID ID of Drawable to get bounds for. + * @return {object} Bounds for a tight box around the Drawable. + */ + getBounds (drawableID) { + const drawable = Drawable.getDrawableByID(drawableID); + // Tell the Drawable about its updated convex hull, if necessary. + if (drawable.needsConvexHullPoints()) { + const points = this._getConvexHullPointsForDrawable(drawableID); + drawable.setConvexHullPoints(points); } - if (optMin) { - newIndex = Math.max(newIndex, optMin); + const bounds = drawable.getBounds(); + // In debug mode, draw the bounds. + if (this._debugCanvas) { + const gl = this._gl; + this._debugCanvas.width = gl.canvas.width; + this._debugCanvas.height = gl.canvas.height; + const context = this._debugCanvas.getContext('2d'); + context.drawImage(gl.canvas, 0, 0); + context.strokeStyle = '#FF0000'; + const pr = window.devicePixelRatio; + context.strokeRect( + pr * (bounds.left + (this._nativeSize[0] / 2)), + pr * (-bounds.top + (this._nativeSize[1] / 2)), + pr * (bounds.right - bounds.left), + pr * (-bounds.bottom + bounds.top) + ); } - newIndex = Math.max(newIndex, 0); - // Insert at new index. - this._drawables.splice(newIndex, 0, drawable); - return this._drawables.indexOf(drawable); + return bounds; } - return null; -}; -/** - * Draw all current drawables and present the frame on the canvas. - */ -RenderWebGL.prototype.draw = function () { - const gl = this._gl; + /** + * Get the current skin (costume) size of a Drawable. + * @param {int} drawableID The ID of the Drawable to measure. + * @return {Array.} Skin size, width and height. + */ + getSkinSize (drawableID) { + const drawable = Drawable.getDrawableByID(drawableID); + return drawable.getSkinSize(); + } - twgl.bindFramebufferInfo(gl, null); - gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); - gl.clearColor.apply(gl, this._backgroundColor); - gl.clear(gl.COLOR_BUFFER_BIT); + /** + * Check if a particular Drawable is touching a particular color. + * @param {int} drawableID The ID of the Drawable to check. + * @param {int[]} color3b Test if the Drawable is touching this color. + * @param {int[]} [mask3b] Optionally mask the check to this part of Drawable. + * @returns {boolean} True iff the Drawable is touching the color. + */ + isTouchingColor (drawableID, color3b, mask3b) { + const gl = this._gl; + twgl.bindFramebufferInfo(gl, this._queryBufferInfo); - this._drawThese( - this._drawables, ShaderManager.DRAW_MODE.default, this._projection); -}; + const bounds = this._touchingBounds(drawableID); + if (!bounds) { + return; + } + const candidateIDs = this._filterCandidatesTouching(drawableID, this._drawables, bounds); + if (!candidateIDs) { + return; + } -/** - * Get the precise bounds for a Drawable. - * @param {int} drawableID ID of Drawable to get bounds for. - * @return {object} Bounds for a tight box around the Drawable. - */ -RenderWebGL.prototype.getBounds = function (drawableID) { - const drawable = Drawable.getDrawableByID(drawableID); - // Tell the Drawable about its updated convex hull, if necessary. - if (drawable.needsConvexHullPoints()) { - const points = this._getConvexHullPointsForDrawable(drawableID); - drawable.setConvexHullPoints(points); - } - const bounds = drawable.getBounds(); - // In debug mode, draw the bounds. - if (this._debugCanvas) { - const gl = this._gl; - this._debugCanvas.width = gl.canvas.width; - this._debugCanvas.height = gl.canvas.height; - const context = this._debugCanvas.getContext('2d'); - context.drawImage(gl.canvas, 0, 0); - context.strokeStyle = '#FF0000'; - const pr = window.devicePixelRatio; - context.strokeRect( - pr * (bounds.left + (this._nativeSize[0] / 2)), - pr * (-bounds.top + (this._nativeSize[1] / 2)), - pr * (bounds.right - bounds.left), - pr * (-bounds.bottom + bounds.top) - ); - } - return bounds; -}; + // Limit size of viewport to the bounds around the target Drawable, + // and create the projection matrix for the draw. + gl.viewport(0, 0, bounds.width, bounds.height); + const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.bottom, bounds.top, -1, 1); -/** - * Get the current skin (costume) size of a Drawable. - * @param {int} drawableID The ID of the Drawable to measure. - * @return {Array.} Skin size, width and height. - */ -RenderWebGL.prototype.getSkinSize = function (drawableID) { - const drawable = Drawable.getDrawableByID(drawableID); - return drawable.getSkinSize(); -}; + gl.clearColor.apply(gl, this._backgroundColor); + gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); -/** - * Check if a particular Drawable is touching a particular color. - * @param {int} drawableID The ID of the Drawable to check. - * @param {int[]} color3b Test if the Drawable is touching this color. - * @param {int[]} [mask3b] Optionally mask the check to this part of Drawable. - * @returns {boolean} True iff the Drawable is touching the color. - */ -RenderWebGL.prototype.isTouchingColor = function (drawableID, color3b, mask3b) { - const gl = this._gl; - twgl.bindFramebufferInfo(gl, this._queryBufferInfo); + let extraUniforms; + if (mask3b) { + extraUniforms = { + u_colorMask: [mask3b[0] / 255, mask3b[1] / 255, mask3b[2] / 255], + u_colorMaskTolerance: TOLERANCE_TOUCHING_COLOR / 255 + }; + } - const bounds = this._touchingBounds(drawableID); - if (!bounds) { - return; - } - const candidateIDs = this._filterCandidatesTouching( - drawableID, this._drawables, bounds); - if (!candidateIDs) { - return; - } + try { + gl.enable(gl.STENCIL_TEST); + gl.stencilFunc(gl.ALWAYS, 1, 1); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); + gl.colorMask(false, false, false, false); + this._drawThese( + [drawableID], + mask3b ? + ShaderManager.DRAW_MODE.colorMask : + ShaderManager.DRAW_MODE.silhouette, + projection, + null, + extraUniforms); + + gl.stencilFunc(gl.EQUAL, 1, 1); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + gl.colorMask(true, true, true, true); + + this._drawThese(candidateIDs, ShaderManager.DRAW_MODE.default, projection, + testID => testID !== drawableID + ); + } finally { + gl.colorMask(true, true, true, true); + gl.disable(gl.STENCIL_TEST); + } + const pixels = new Buffer(bounds.width * bounds.height * 4); + gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); - // Limit size of viewport to the bounds around the target Drawable, - // and create the projection matrix for the draw. - gl.viewport(0, 0, bounds.width, bounds.height); - const projection = twgl.m4.ortho( - bounds.left, bounds.right, bounds.bottom, bounds.top, -1, 1); + if (this._debugCanvas) { + this._debugCanvas.width = bounds.width; + this._debugCanvas.height = bounds.height; + const context = this._debugCanvas.getContext('2d'); + const imageData = context.getImageData(0, 0, bounds.width, bounds.height); + for (let i = 0, bytes = pixels.length; i < bytes; ++i) { + imageData.data[i] = pixels[i]; + } + context.putImageData(imageData, 0, 0); + } - gl.clearColor.apply(gl, this._backgroundColor); - gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + for (let pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { + const pixelDistanceR = Math.abs(pixels[pixelBase] - color3b[0]); + const pixelDistanceG = Math.abs(pixels[pixelBase + 1] - color3b[1]); + const pixelDistanceB = Math.abs(pixels[pixelBase + 2] - color3b[2]); - let extraUniforms; - if (mask3b) { - extraUniforms = { - u_colorMask: [mask3b[0] / 255, mask3b[1] / 255, mask3b[2] / 255], - u_colorMaskTolerance: RenderWebGL.TOLERANCE_TOUCHING_COLOR / 255 - }; + if (pixelDistanceR <= TOLERANCE_TOUCHING_COLOR && + pixelDistanceG <= TOLERANCE_TOUCHING_COLOR && + pixelDistanceB <= TOLERANCE_TOUCHING_COLOR) { + return true; + } + } + + return false; } - try { - gl.enable(gl.STENCIL_TEST); - gl.stencilFunc(gl.ALWAYS, 1, 1); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); - gl.colorMask(false, false, false, false); - this._drawThese( - [drawableID], - mask3b ? - ShaderManager.DRAW_MODE.colorMask : - ShaderManager.DRAW_MODE.silhouette, - projection, - null, - extraUniforms); + /** + * Check if a particular Drawable is touching any in a set of Drawables. + * @param {int} drawableID The ID of the Drawable to check. + * @param {int[]} candidateIDs The Drawable IDs to check, otherwise all. + * @returns {boolean} True iff the Drawable is touching one of candidateIDs. + */ + isTouchingDrawables (drawableID, candidateIDs) { + candidateIDs = candidateIDs || this._drawables; - gl.stencilFunc(gl.EQUAL, 1, 1); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - gl.colorMask(true, true, true, true); + const gl = this._gl; - this._drawThese( - candidateIDs, ShaderManager.DRAW_MODE.default, projection, - testID => testID !== drawableID - ); - } finally { - gl.colorMask(true, true, true, true); - gl.disable(gl.STENCIL_TEST); - } + twgl.bindFramebufferInfo(gl, this._queryBufferInfo); - const pixels = new Buffer(bounds.width * bounds.height * 4); - gl.readPixels( - 0, 0, bounds.width, bounds.height, - gl.RGBA, gl.UNSIGNED_BYTE, pixels); - - if (this._debugCanvas) { - this._debugCanvas.width = bounds.width; - this._debugCanvas.height = bounds.height; - const context = this._debugCanvas.getContext('2d'); - const imageData = context.getImageData( - 0, 0, bounds.width, bounds.height); - for (let i = 0, bytes = pixels.length; i < bytes; ++i) { - imageData.data[i] = pixels[i]; + const bounds = this._touchingBounds(drawableID); + if (!bounds) { + return; + } + candidateIDs = this._filterCandidatesTouching(drawableID, candidateIDs, bounds); + if (!candidateIDs) { + return; } - context.putImageData(imageData, 0, 0); - } - for (let pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { - const pixelDistanceR = Math.abs(pixels[pixelBase] - color3b[0]); - const pixelDistanceG = Math.abs(pixels[pixelBase + 1] - color3b[1]); - const pixelDistanceB = Math.abs(pixels[pixelBase + 2] - color3b[2]); + // Limit size of viewport to the bounds around the target Drawable, + // and create the projection matrix for the draw. + gl.viewport(0, 0, bounds.width, bounds.height); + const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.bottom, bounds.top, -1, 1); + + const noneColor = Drawable.color4fFromID(Drawable.NONE); + gl.clearColor.apply(gl, noneColor); + gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + + try { + gl.enable(gl.STENCIL_TEST); + gl.stencilFunc(gl.ALWAYS, 1, 1); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); + gl.colorMask(false, false, false, false); + this._drawThese([drawableID], ShaderManager.DRAW_MODE.silhouette, projection); + + gl.stencilFunc(gl.EQUAL, 1, 1); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + gl.colorMask(true, true, true, true); + + this._drawThese(candidateIDs, ShaderManager.DRAW_MODE.silhouette, projection, + testID => testID !== drawableID + ); + } finally { + gl.colorMask(true, true, true, true); + gl.disable(gl.STENCIL_TEST); + } - if (pixelDistanceR <= RenderWebGL.TOLERANCE_TOUCHING_COLOR && - pixelDistanceG <= RenderWebGL.TOLERANCE_TOUCHING_COLOR && - pixelDistanceB <= RenderWebGL.TOLERANCE_TOUCHING_COLOR) { - return true; + const pixels = new Buffer(bounds.width * bounds.height * 4); + gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); + + if (this._debugCanvas) { + this._debugCanvas.width = bounds.width; + this._debugCanvas.height = bounds.height; + const context = this._debugCanvas.getContext('2d'); + const imageData = context.getImageData(0, 0, bounds.width, bounds.height); + for (let i = 0, bytes = pixels.length; i < bytes; ++i) { + imageData.data[i] = pixels[i]; + } + context.putImageData(imageData, 0, 0); } + + for (let pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { + const pixelID = Drawable.color3bToID( + pixels[pixelBase], + pixels[pixelBase + 1], + pixels[pixelBase + 2]); + if (pixelID > Drawable.NONE) { + return true; + } + } + + return false; } - return false; -}; + /** + * Detect which sprite, if any, is at the given location. + * @param {int} centerX The client x coordinate of the picking location. + * @param {int} centerY The client y coordinate of the picking location. + * @param {int} touchWidth The client width of the touch event (optional). + * @param {int} touchHeight The client height of the touch event (optional). + * @param {int[]} candidateIDs The Drawable IDs to pick from, otherwise all. + * @returns {int} The ID of the topmost Drawable under the picking location, or + * Drawable.NONE if there is no Drawable at that location. + */ + pick (centerX, centerY, touchWidth, touchHeight, candidateIDs) { + const gl = this._gl; -/** - * Check if a particular Drawable is touching any in a set of Drawables. - * @param {int} drawableID The ID of the Drawable to check. - * @param {int[]} candidateIDs The Drawable IDs to check, otherwise all. - * @returns {boolean} True iff the Drawable is touching one of candidateIDs. - */ -RenderWebGL.prototype.isTouchingDrawables = function (drawableID, candidateIDs) { - candidateIDs = candidateIDs || this._drawables; + touchWidth = touchWidth || 1; + touchHeight = touchHeight || 1; + candidateIDs = candidateIDs || this._drawables; - const gl = this._gl; + const clientToGLX = gl.canvas.width / gl.canvas.clientWidth; + const clientToGLY = gl.canvas.height / gl.canvas.clientHeight; - twgl.bindFramebufferInfo(gl, this._queryBufferInfo); + centerX *= clientToGLX; + centerY *= clientToGLY; + touchWidth *= clientToGLX; + touchHeight *= clientToGLY; - const bounds = this._touchingBounds(drawableID); - if (!bounds) { - return; - } - candidateIDs = this._filterCandidatesTouching( - drawableID, candidateIDs, bounds); - if (!candidateIDs) { - return; - } + touchWidth = Math.max(1, Math.min(touchWidth, MAX_TOUCH_SIZE[0])); + touchHeight = Math.max(1, Math.min(touchHeight, MAX_TOUCH_SIZE[1])); - // Limit size of viewport to the bounds around the target Drawable, - // and create the projection matrix for the draw. - gl.viewport(0, 0, bounds.width, bounds.height); - const projection = twgl.m4.ortho( - bounds.left, bounds.right, bounds.bottom, bounds.top, -1, 1); - - const noneColor = Drawable.color4fFromID(Drawable.NONE); - gl.clearColor.apply(gl, noneColor); - gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); - - try { - gl.enable(gl.STENCIL_TEST); - gl.stencilFunc(gl.ALWAYS, 1, 1); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); - gl.colorMask(false, false, false, false); - this._drawThese( - [drawableID], ShaderManager.DRAW_MODE.silhouette, projection - ); + const pixelLeft = Math.floor(centerX - Math.floor(touchWidth / 2) + 0.5); + const pixelRight = Math.floor(centerX + Math.ceil(touchWidth / 2) + 0.5); + const pixelTop = Math.floor(centerY - Math.floor(touchHeight / 2) + 0.5); + const pixelBottom = Math.floor(centerY + Math.ceil(touchHeight / 2) + 0.5); - gl.stencilFunc(gl.EQUAL, 1, 1); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - gl.colorMask(true, true, true, true); + twgl.bindFramebufferInfo(gl, this._pickBufferInfo); + gl.viewport(0, 0, touchWidth, touchHeight); - this._drawThese( - candidateIDs, ShaderManager.DRAW_MODE.silhouette, projection, - testID => testID !== drawableID - ); - } finally { - gl.colorMask(true, true, true, true); - gl.disable(gl.STENCIL_TEST); - } + const noneColor = Drawable.color4fFromID(Drawable.NONE); + gl.clearColor.apply(gl, noneColor); + gl.clear(gl.COLOR_BUFFER_BIT); + + const widthPerPixel = (this._xRight - this._xLeft) / this._gl.canvas.width; + const heightPerPixel = (this._yBottom - this._yTop) / this._gl.canvas.height; + + const pickLeft = this._xLeft + (pixelLeft * widthPerPixel); + const pickRight = this._xLeft + (pixelRight * widthPerPixel); + const pickTop = this._yTop + (pixelTop * heightPerPixel); + const pickBottom = this._yTop + (pixelBottom * heightPerPixel); + + const projection = twgl.m4.ortho(pickLeft, pickRight, pickTop, pickBottom, -1, 1); - const pixels = new Buffer(bounds.width * bounds.height * 4); - gl.readPixels( - 0, 0, bounds.width, bounds.height, - gl.RGBA, gl.UNSIGNED_BYTE, pixels); - - if (this._debugCanvas) { - this._debugCanvas.width = bounds.width; - this._debugCanvas.height = bounds.height; - const context = this._debugCanvas.getContext('2d'); - const imageData = context.getImageData( - 0, 0, bounds.width, bounds.height); - for (let i = 0, bytes = pixels.length; i < bytes; ++i) { - imageData.data[i] = pixels[i]; + this._drawThese(candidateIDs, ShaderManager.DRAW_MODE.silhouette, projection); + + const pixels = new Buffer(touchWidth * touchHeight * 4); + gl.readPixels(0, 0, touchWidth, touchHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels); + + if (this._debugCanvas) { + this._debugCanvas.width = touchWidth; + this._debugCanvas.height = touchHeight; + const context = this._debugCanvas.getContext('2d'); + const imageData = context.getImageData(0, 0, touchWidth, touchHeight); + for (let i = 0, bytes = pixels.length; i < bytes; ++i) { + imageData.data[i] = pixels[i]; + } + context.putImageData(imageData, 0, 0); } - context.putImageData(imageData, 0, 0); - } - for (let pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { - const pixelID = Drawable.color4bToID( - pixels[pixelBase], - pixels[pixelBase + 1], - pixels[pixelBase + 2], - pixels[pixelBase + 3]); - if (pixelID > Drawable.NONE) { - return true; + const hits = {}; + for (let pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { + const pixelID = Drawable.color3bToID( + pixels[pixelBase], + pixels[pixelBase + 1], + pixels[pixelBase + 2]); + hits[pixelID] = (hits[pixelID] || 0) + 1; } - } - return false; -}; + // Bias toward selecting anything over nothing + hits[Drawable.NONE] = 0; -/** - * Detect which sprite, if any, is at the given location. - * @param {int} centerX The client x coordinate of the picking location. - * @param {int} centerY The client y coordinate of the picking location. - * @param {int} touchWidth The client width of the touch event (optional). - * @param {int} touchHeight The client height of the touch event (optional). - * @param {int[]} candidateIDs The Drawable IDs to pick from, otherwise all. - * @returns {int} The ID of the topmost Drawable under the picking location, or - * Drawable.NONE if there is no Drawable at that location. - */ -RenderWebGL.prototype.pick = function ( - centerX, centerY, touchWidth, touchHeight, candidateIDs) { - const gl = this._gl; - - touchWidth = touchWidth || 1; - touchHeight = touchHeight || 1; - candidateIDs = candidateIDs || this._drawables; - - const clientToGLX = gl.canvas.width / gl.canvas.clientWidth; - const clientToGLY = gl.canvas.height / gl.canvas.clientHeight; - - centerX *= clientToGLX; - centerY *= clientToGLY; - touchWidth *= clientToGLX; - touchHeight *= clientToGLY; - - touchWidth = - Math.max(1, Math.min(touchWidth, RenderWebGL.MAX_TOUCH_SIZE[0])); - touchHeight = - Math.max(1, Math.min(touchHeight, RenderWebGL.MAX_TOUCH_SIZE[1])); - - const pixelLeft = Math.floor(centerX - Math.floor(touchWidth / 2) + 0.5); - const pixelRight = Math.floor(centerX + Math.ceil(touchWidth / 2) + 0.5); - const pixelTop = Math.floor(centerY - Math.floor(touchHeight / 2) + 0.5); - const pixelBottom = Math.floor(centerY + Math.ceil(touchHeight / 2) + 0.5); - - twgl.bindFramebufferInfo(gl, this._pickBufferInfo); - gl.viewport(0, 0, touchWidth, touchHeight); - - const noneColor = Drawable.color4fFromID(Drawable.NONE); - gl.clearColor.apply(gl, noneColor); - gl.clear(gl.COLOR_BUFFER_BIT); - - const widthPerPixel = (this._xRight - this._xLeft) / this._gl.canvas.width; - const heightPerPixel = (this._yBottom - this._yTop) / this._gl.canvas.height; - - const pickLeft = this._xLeft + (pixelLeft * widthPerPixel); - const pickRight = this._xLeft + (pixelRight * widthPerPixel); - const pickTop = this._yTop + (pixelTop * heightPerPixel); - const pickBottom = this._yTop + (pixelBottom * heightPerPixel); - - const projection = twgl.m4.ortho( - pickLeft, pickRight, pickTop, pickBottom, -1, 1); - - this._drawThese( - candidateIDs, ShaderManager.DRAW_MODE.silhouette, projection); - - const pixels = new Buffer(touchWidth * touchHeight * 4); - gl.readPixels( - 0, 0, touchWidth, touchHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels); - - if (this._debugCanvas) { - this._debugCanvas.width = touchWidth; - this._debugCanvas.height = touchHeight; - const context = this._debugCanvas.getContext('2d'); - const imageData = context.getImageData(0, 0, touchWidth, touchHeight); - for (let i = 0, bytes = pixels.length; i < bytes; ++i) { - imageData.data[i] = pixels[i]; + let hit = Drawable.NONE; + for (const hitID in hits) { + if (hits.hasOwnProperty(hitID) && (hits[hitID] > hits[hit])) { + hit = hitID; + } } - context.putImageData(imageData, 0, 0); - } - const hits = {}; - for (let pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { - const pixelID = Drawable.color4bToID( - pixels[pixelBase], - pixels[pixelBase + 1], - pixels[pixelBase + 2], - pixels[pixelBase + 3]); - hits[pixelID] = (hits[pixelID] || 0) + 1; + return hit | 0; } - // Bias toward selecting anything over nothing - hits[Drawable.NONE] = 0; + /** + * Get the candidate bounding box for a touching query. + * @param {int} drawableID ID for drawable of query. + * @return {?Rectangle} Rectangle bounds for touching query, or null. + */ + _touchingBounds (drawableID) { + const drawable = Drawable.getDrawableByID(drawableID); + const bounds = drawable.getFastBounds(); - let hit = Drawable.NONE; - for (const hitID in hits) { - if (hits.hasOwnProperty(hitID) && (hits[hitID] > hits[hit])) { - hit = hitID; + // Limit queries to the stage size. + bounds.clamp(this._xLeft, this._xRight, this._yBottom, this._yTop); + + // Use integer coordinates for queries - weird things happen + // when you provide float width/heights to gl.viewport and projection. + bounds.snapToInt(); + + if (bounds.width === 0 || bounds.height === 0) { + // No space to query. + return null; } + return bounds; } - return hit | 0; -}; - -/** - * Get the candidate bounding box for a touching query. - * @param {int} drawableID ID for drawable of query. - * @return {?Rectangle} Rectangle bounds for touching query, or null. - */ -RenderWebGL.prototype._touchingBounds = function (drawableID) { - const drawable = Drawable.getDrawableByID(drawableID); - const bounds = drawable.getFastBounds(); + /** + * Filter a list of candidates for a touching query into only those that + * could possibly intersect the given bounds. + * @param {int} drawableID - ID for drawable of query. + * @param {Array.} candidateIDs - Candidates for touching query. + * @param {Rectangle} bounds - Bounds to limit candidates to. + * @return {?Array.} Filtered candidateIDs, or null if none. + */ + _filterCandidatesTouching (drawableID, candidateIDs, bounds) { + // Filter candidates by rough bounding box intersection. + // Do this before _drawThese, so we can prevent any GL operations + // and readback by returning early. + candidateIDs = candidateIDs.filter(testID => { + if (testID === drawableID) return false; + // Only draw items which could possibly overlap target Drawable. + const candidate = Drawable.getDrawableByID(testID); + const candidateBounds = candidate.getFastBounds(); + return bounds.intersects(candidateBounds); + }); + if (candidateIDs.length === 0) { + // No possible intersections based on bounding boxes. + return null; + } + return candidateIDs; + } - // Limit queries to the stage size. - bounds.clamp(this._xLeft, this._xRight, this._yBottom, this._yTop); + /** + * Update the position, direction, scale, or effect properties of this Drawable. + * @param {int} drawableID The ID of the Drawable to update. + * @param {object.} properties The new property values to set. + */ + updateDrawableProperties (drawableID, properties) { + const drawable = Drawable.getDrawableByID(drawableID); + drawable.updateProperties(properties); + } - // Use integer coordinates for queries - weird things happen - // when you provide float width/heights to gl.viewport and projection. - bounds.snapToInt(); + /* ****** + * Truly internal functions: these support the functions above. + ********/ - if (bounds.width === 0 || bounds.height === 0) { - // No space to query. - return null; + /** + * Build geometry (vertex and index) buffers. + * @private + */ + _createGeometry () { + const quad = { + a_position: { + numComponents: 2, + data: [ + -0.5, -0.5, + 0.5, -0.5, + -0.5, 0.5, + -0.5, 0.5, + 0.5, -0.5, + 0.5, 0.5 + ] + }, + a_texCoord: { + numComponents: 2, + data: [ + 1, 0, + 0, 0, + 1, 1, + 1, 1, + 0, 0, + 0, 1 + ] + } + }; + this._bufferInfo = twgl.createBufferInfoFromArrays(this._gl, quad); } - return bounds; -}; -/** - * Filter a list of candidates for a touching query into only those that - * could possibly intersect the given bounds. - * @param {int} drawableID - ID for drawable of query. - * @param {Array.} candidateIDs - Candidates for touching query. - * @param {Rectangle} bounds - Bounds to limit candidates to. - * @return {?Array.} Filtered candidateIDs, or null if none. - */ -RenderWebGL.prototype._filterCandidatesTouching = function ( - drawableID, candidateIDs, bounds) { - // Filter candidates by rough bounding box intersection. - // Do this before _drawThese, so we can prevent any GL operations - // and readback by returning early. - candidateIDs = candidateIDs.filter(testID => { - if (testID === drawableID) return false; - // Only draw items which could possibly overlap target Drawable. - const candidate = Drawable.getDrawableByID(testID); - const candidateBounds = candidate.getFastBounds(); - return bounds.intersects(candidateBounds); - }); - if (candidateIDs.length === 0) { - // No possible intersections based on bounding boxes. - return null; + /** + * Create the frame buffers used for queries such as picking and color-touching. + * These buffers are fixed in size regardless of the size of the main render + * target. The fixed size allows (more) consistent behavior across devices and + * presentation modes. + * @private + */ + _createQueryBuffers () { + const gl = this._gl; + const attachments = [ + {format: gl.RGBA}, + {format: gl.DEPTH_STENCIL} + ]; + + this._pickBufferInfo = twgl.createFramebufferInfo(gl, attachments, MAX_TOUCH_SIZE[0], MAX_TOUCH_SIZE[1]); + + // TODO: should we create this on demand to save memory? + // A 480x360 32-bpp buffer is 675 KiB. + this._queryBufferInfo = twgl.createFramebufferInfo(gl, attachments, this._nativeSize[0], this._nativeSize[1]); } - return candidateIDs; -}; -/** -* Update the position, direction, scale, or effect properties of this Drawable. -* @param {int} drawableID The ID of the Drawable to update. -* @param {object.} properties The new property values to set. - */ -RenderWebGL.prototype.updateDrawableProperties = function ( - drawableID, properties) { - const drawable = Drawable.getDrawableByID(drawableID); - drawable.updateProperties(properties); -}; + /** + * Draw all Drawables, with the possible exception of + * @param {int[]} drawables The Drawable IDs to draw, possibly this._drawables. + * @param {ShaderManager.DRAW_MODE} drawMode Draw normally, silhouette, etc. + * @param {module:twgl/m4.Mat4} projection The projection matrix to use. + * @param {Drawable~idFilterFunc} [filter] An optional filter function. + * @param {Object.} [extraUniforms] Extra uniforms for the shaders. + * @private + */ + _drawThese (drawables, drawMode, projection, filter, extraUniforms) { + const gl = this._gl; + let currentShader = null; -/* ******** - * Truly internal functions: these support the functions above. - ********/ + const numDrawables = drawables.length; + for (let drawableIndex = 0; drawableIndex < numDrawables; ++drawableIndex) { + const drawableID = drawables[drawableIndex]; -/** - * Build geometry (vertex and index) buffers. - * @private - */ -RenderWebGL.prototype._createGeometry = function () { - const quad = { - a_position: { - numComponents: 2, - data: [ - -0.5, -0.5, - 0.5, -0.5, - -0.5, 0.5, - -0.5, 0.5, - 0.5, -0.5, - 0.5, 0.5 - ] - }, - a_texCoord: { - numComponents: 2, - data: [ - 1, 0, - 0, 0, - 1, 1, - 1, 1, - 0, 0, - 0, 1 - ] - } - }; - this._bufferInfo = twgl.createBufferInfoFromArrays(this._gl, quad); -}; + // If we have a filter, check whether the ID fails + if (filter && !filter(drawableID)) continue; -/** - * Create the frame buffers used for queries such as picking and color-touching. - * These buffers are fixed in size regardless of the size of the main render - * target. The fixed size allows (more) consistent behavior across devices and - * presentation modes. - * @private - */ -RenderWebGL.prototype._createQueryBuffers = function () { - const gl = this._gl; - const attachments = [ - {format: gl.RGBA}, - {format: gl.DEPTH_STENCIL} - ]; - - this._pickBufferInfo = twgl.createFramebufferInfo( - gl, attachments, - RenderWebGL.MAX_TOUCH_SIZE[0], RenderWebGL.MAX_TOUCH_SIZE[1]); - - // TODO: should we create this on demand to save memory? - // A 480x360 32-bpp buffer is 675 KiB. - this._queryBufferInfo = twgl.createFramebufferInfo( - gl, attachments, this._nativeSize[0], this._nativeSize[1]); -}; + const drawable = Drawable.getDrawableByID(drawableID); + // TODO: check if drawable is inside the viewport before anything else -/** - * Draw all Drawables, with the possible exception of - * @param {int[]} drawables The Drawable IDs to draw, possibly this._drawables. - * @param {ShaderManager.DRAW_MODE} drawMode Draw normally, silhouette, etc. - * @param {module:twgl/m4.Mat4} projection The projection matrix to use. - * @param {Drawable~idFilterFunc} [filter] An optional filter function. - * @param {Object.} [extraUniforms] Extra uniforms for the shaders. - * @private - */ -RenderWebGL.prototype._drawThese = function ( - drawables, drawMode, projection, filter, extraUniforms) { + // Hidden drawables (e.g., by a "hide" block) are never drawn. + if (!drawable.getVisible()) continue; - const gl = this._gl; - let currentShader = null; + const effectBits = drawable.getEnabledEffects(); + const newShader = this._shaderManager.getShader(drawMode, effectBits); + if (currentShader !== newShader) { + currentShader = newShader; + gl.useProgram(currentShader.program); + twgl.setBuffersAndAttributes(gl, currentShader, this._bufferInfo); + twgl.setUniforms(currentShader, {u_projectionMatrix: projection}); + twgl.setUniforms(currentShader, {u_fudge: window.fudge || 0}); + } - const numDrawables = drawables.length; - for (let drawableIndex = 0; drawableIndex < numDrawables; ++drawableIndex) { - const drawableID = drawables[drawableIndex]; + twgl.setUniforms(currentShader, drawable.getUniforms()); - // If we have a filter, check whether the ID fails - if (filter && !filter(drawableID)) continue; + // Apply extra uniforms after the Drawable's, to allow overwriting. + if (extraUniforms) { + twgl.setUniforms(currentShader, extraUniforms); + } + twgl.drawBufferInfo(gl, gl.TRIANGLES, this._bufferInfo); + } + } + + /** + * Get the convex hull points for a particular Drawable. + * To do this, draw the Drawable unrotated, unscaled, and untranslated. + * Read back the pixels and find all boundary points. + * Finally, apply a convex hull algorithm to simplify the set. + * @param {int} drawableID The Drawable IDs calculate convex hull for. + * @return {Array.>} points Convex hull points, as [[x, y], ...] + */ + _getConvexHullPointsForDrawable (drawableID) { const drawable = Drawable.getDrawableByID(drawableID); - // TODO: check if drawable is inside the viewport before anything else - - // Hidden drawables (e.g., by a "hide" block) are never drawn. - if (!drawable.getVisible()) continue; - - const effectBits = drawable.getEnabledEffects(); - const newShader = this._shaderManager.getShader(drawMode, effectBits); - if (currentShader !== newShader) { - currentShader = newShader; - gl.useProgram(currentShader.program); - twgl.setBuffersAndAttributes(gl, currentShader, this._bufferInfo); - twgl.setUniforms(currentShader, {u_projectionMatrix: projection}); - twgl.setUniforms(currentShader, {u_fudge: window.fudge || 0}); + const [width, height] = drawable._uniforms.u_skinSize; + // No points in the hull if invisible or size is 0. + if (!drawable.getVisible() || width === 0 || height === 0) { + return []; } - twgl.setUniforms(currentShader, drawable.getUniforms()); + // Only draw to the size of the untransformed drawable. + const gl = this._gl; + twgl.bindFramebufferInfo(gl, this._queryBufferInfo); + gl.viewport(0, 0, width, height); - // Apply extra uniforms after the Drawable's, to allow overwriting. - if (extraUniforms) { - twgl.setUniforms(currentShader, extraUniforms); - } + // Clear the canvas with Drawable.NONE. + const noneColor = Drawable.color4fFromID(Drawable.NONE); + gl.clearColor.apply(gl, noneColor); + gl.clear(gl.COLOR_BUFFER_BIT); - twgl.drawBufferInfo(gl, gl.TRIANGLES, this._bufferInfo); - } -}; + // Overwrite the model matrix to be unrotated, unscaled, untranslated. + const modelMatrix = twgl.m4.identity(); + twgl.m4.rotateZ(modelMatrix, Math.PI, modelMatrix); + twgl.m4.scale(modelMatrix, [width, height], modelMatrix); -/** - * Get the convex hull points for a particular Drawable. - * To do this, draw the Drawable unrotated, unscaled, and untranslated. - * Read back the pixels and find all boundary points. - * Finally, apply a convex hull algorithm to simplify the set. - * @param {int} drawableID The Drawable IDs calculate convex hull for. - * @return {Array.>} points Convex hull points, as [[x, y], ...] - */ -RenderWebGL.prototype._getConvexHullPointsForDrawable = function (drawableID) { - const drawable = Drawable.getDrawableByID(drawableID); - const [width, height] = drawable._uniforms.u_skinSize; - // No points in the hull if invisible or size is 0. - if (!drawable.getVisible() || width === 0 || height === 0) { - return []; - } + const projection = twgl.m4.ortho(-0.5 * width, 0.5 * width, -0.5 * height, 0.5 * height, -1, 1); - // Only draw to the size of the untransformed drawable. - const gl = this._gl; - twgl.bindFramebufferInfo(gl, this._queryBufferInfo); - gl.viewport(0, 0, width, height); - - // Clear the canvas with Drawable.NONE. - const noneColor = Drawable.color4fFromID(Drawable.NONE); - gl.clearColor.apply(gl, noneColor); - gl.clear(gl.COLOR_BUFFER_BIT); - - // Overwrite the model matrix to be unrotated, unscaled, untranslated. - const modelMatrix = twgl.m4.identity(); - twgl.m4.rotateZ(modelMatrix, Math.PI, modelMatrix); - twgl.m4.scale(modelMatrix, [width, height], modelMatrix); - - const projection = twgl.m4.ortho( - -0.5 * width, 0.5 * width, - -0.5 * height, 0.5 * height, - -1, 1 - ); - - this._drawThese([drawableID], - ShaderManager.DRAW_MODE.silhouette, - projection, - null, - {u_modelMatrix: modelMatrix} - ); - - const pixels = new Buffer(width * height * 4); - gl.readPixels( - 0, 0, width, height, - gl.RGBA, gl.UNSIGNED_BYTE, pixels); - - // Known boundary points on left/right edges of pixels. - const boundaryPoints = []; + this._drawThese([drawableID], + ShaderManager.DRAW_MODE.silhouette, + projection, + null, + {u_modelMatrix: modelMatrix} + ); - /** - * Helper method to look up a pixel. - * @param {int} x X coordinate of the pixel in `pixels`. - * @param {int} y Y coordinate of the pixel in `pixels`. - * @return {int} Known ID at that pixel, or Drawable.NONE. - */ - const _getPixel = function (x, y) { - const pixelBase = ((width * y) + x) * 4; - return Drawable.color4bToID( - pixels[pixelBase], - pixels[pixelBase + 1], - pixels[pixelBase + 2], - pixels[pixelBase + 3]); - }; - for (let y = 0; y <= height; y++) { - // Scan from left. - for (let x = 0; x < width; x++) { - if (_getPixel(x, y) > Drawable.NONE) { - boundaryPoints.push([x, y]); - break; + const pixels = new Buffer(width * height * 4); + gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); + + // Known boundary points on left/right edges of pixels. + const boundaryPoints = []; + + /** + * Helper method to look up a pixel. + * @param {int} x X coordinate of the pixel in `pixels`. + * @param {int} y Y coordinate of the pixel in `pixels`. + * @return {int} Known ID at that pixel, or Drawable.NONE. + */ + const _getPixel = (x, y) => { + const pixelBase = ((width * y) + x) * 4; + return Drawable.color3bToID( + pixels[pixelBase], + pixels[pixelBase + 1], + pixels[pixelBase + 2]); + }; + for (let y = 0; y <= height; y++) { + // Scan from left. + for (let x = 0; x < width; x++) { + if (_getPixel(x, y) > Drawable.NONE) { + boundaryPoints.push([x, y]); + break; + } } - } - // Scan from right. - for (let x = width - 1; x >= 0; x--) { - if (_getPixel(x, y) > Drawable.NONE) { - boundaryPoints.push([x, y]); - break; + // Scan from right. + for (let x = width - 1; x >= 0; x--) { + if (_getPixel(x, y) > Drawable.NONE) { + boundaryPoints.push([x, y]); + break; + } } } + // Simplify boundary points using convex hull. + return hull(boundaryPoints, Infinity); } - // Simplify boundary points using convex hull. - return hull(boundaryPoints, Infinity); -}; +} + +module.exports = RenderWebGL; From ca86040cb4f6b383761d4d86714e62753cfa8fdf Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Thu, 12 Jan 2017 10:57:07 -0800 Subject: [PATCH 1806/1971] Merge pull request #67 from cwillisf/skin-classes Move `Skin` functionality out of `Drawable` into its own classes --- packages/scratch-render/src/RenderWebGL.js | 242 +++++++++++++++++---- packages/scratch-render/src/SVGSkin.js | 79 +++++++ 2 files changed, 280 insertions(+), 41 deletions(-) create mode 100644 packages/scratch-render/src/SVGSkin.js diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index b0ff4fbf3e..2caa6ec0f4 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1,8 +1,12 @@ const hull = require('hull.js'); const twgl = require('twgl.js'); +const xhr = require('xhr'); +const BitmapSkin = require('./BitmapSkin'); const Drawable = require('./Drawable'); +const RenderConstants = require('./RenderConstants'); const ShaderManager = require('./ShaderManager'); +const SVGSkin = require('./SVGSkin'); /** * Maximum touch size for a picking check. @@ -37,13 +41,30 @@ class RenderWebGL { * @constructor */ constructor (canvas, xLeft, xRight, yBottom, yTop) { - // TODO: remove? - twgl.setDefaults({crossOrigin: true}); + /** @type {Drawable[]} */ + this._allDrawables = []; + /** @type {Skin[]} */ + this._allSkins = []; + + /** @type {int[]} */ + this._drawList = []; + + /** @type {WebGLRenderingContext} */ this._gl = twgl.getWebGLContext(canvas, {alpha: false, stencil: true}); - this._drawables = []; + + /** @type {int} */ + this._nextDrawableId = RenderConstants.ID_NONE + 1; + + /** @type {int} */ + this._nextSkinId = RenderConstants.ID_NONE + 1; + + /** @type {module:twgl/m4.Mat4} */ this._projection = twgl.m4.identity(); + /** @type {Object.} */ + this._skinUrlMap = {}; + this._createGeometry(); this.setBackgroundColor(1, 1, 1); @@ -58,6 +79,13 @@ class RenderWebGL { this._shaderManager = new ShaderManager(gl); } + /** + * @returns {WebGLRenderingContext} the WebGL rendering context associated with this renderer. + */ + get gl () { + return this._gl; + } + /** * Set the physical size of the stage in device-independent pixels. * This will be multiplied by the device's pixel ratio on high-DPI displays. @@ -106,30 +134,134 @@ class RenderWebGL { this._projection = twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1); } + /** + * Create a skin by loading a bitmap or vector image from a URL, or reuse an existing skin created this way. + * WARNING: This method is deprecated and will be removed in the near future. + * Use `createBitmapSkin` or `createSVGSkin` instead. + * @param {!string} skinUrl The URL of the skin. + * @param {!int} [costumeResolution] Optional: resolution for the skin. Ignored unless creating a new Bitmap skin. + * @returns {!int} The ID of the Skin. + * @deprecated + */ + createSkinFromURL (skinUrl, costumeResolution) { + if (this._skinUrlMap.hasOwnProperty(skinUrl)) { + const existingId = this._skinUrlMap[skinUrl]; + + // Make sure the "existing" skin hasn't been destroyed + if (this._allSkins[existingId]) { + return existingId; + } + } + + const skinId = this._nextSkinId++; + this._skinUrlMap[skinUrl] = skinId; + + let newSkin; + let isVector; + + const ext = skinUrl.substring(skinUrl.lastIndexOf('.') + 1); + switch (ext) { + case 'svg': + case 'svg/get/': + case 'svgz': + case 'svgz/get/': + isVector = true; + break; + default: + isVector = false; + break; + } + + if (isVector) { + newSkin = new SVGSkin(skinId, this); + xhr.get({ + useXDR: true, + url: skinUrl + }, (err, response, body) => { + if (!err) { + newSkin.setSVG(body); + } + }); + } else { + newSkin = new BitmapSkin(skinId, this); + const img = new Image(); + img.crossOrigin = 'anonymous'; + img.onload = () => { + newSkin.setBitmap(img, costumeResolution); + }; + img.src = skinUrl; + } + + this._allSkins[skinId] = newSkin; + return skinId; + } + + /** + * Create a new bitmap skin from a snapshot of the provided bitmap data. + * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin. + * @param {!int} [costumeResolution=1] - The resolution to use for this bitmap. + * @returns {!int} the ID for the new skin. + */ + createBitmapSkin (bitmapData, costumeResolution) { + const skinId = this._nextSkinId++; + const newSkin = new BitmapSkin(skinId, this); + newSkin.setBitmap(bitmapData, costumeResolution); + this._allSkins[skinId] = newSkin; + return skinId; + } + + /** + * Create a new SVG skin. + * @param {!string} svgData - new SVG to use. + * @returns {!int} the ID for the new skin. + */ + createSVGSkin (svgData) { + const skinId = this._nextSkinId++; + const newSkin = new SVGSkin(skinId, this); + newSkin.setSVG(svgData); + this._allSkins[skinId] = newSkin; + return skinId; + } + + /** + * Destroy an existing skin. Do not use the skin or its ID after calling this. + * @param {!int} skinId - The ID of the skin to destroy. + */ + destroySkin (skinId) { + const oldSkin = this._allSkins[skinId]; + oldSkin.dispose(); + delete this._allSkins[skinId]; + } + /** * Create a new Drawable and add it to the scene. * @returns {int} The ID of the new Drawable. */ createDrawable () { - const drawable = new Drawable(this._gl); - const drawableID = drawable.getID(); - this._drawables.push(drawableID); + const drawableID = this._nextDrawableId++; + const drawable = new Drawable(drawableID, this); + this._allDrawables[drawableID] = drawable; + this._drawList.push(drawableID); + + const defaultSkinId = this.createSkinFromURL(RenderConstants.DEFAULT_SKIN); + drawable.skin = this._allSkins[defaultSkinId]; + return drawableID; } /** * Destroy a Drawable, removing it from the scene. * @param {int} drawableID The ID of the Drawable to remove. - * @returns {boolean} True iff the drawable was found and removed. */ destroyDrawable (drawableID) { - const index = this._drawables.indexOf(drawableID); - if (index >= 0) { - Drawable.getDrawableByID(drawableID).dispose(); - this._drawables.splice(index, 1); - return true; + const drawable = this._allDrawables[drawableID]; + drawable.dispose(); + delete this._allDrawables[drawableID]; + + let index; + while ((index = this._drawList.indexOf(drawableID)) >= 0) { + this._drawList.splice(index, 1); } - return false; } /** @@ -146,10 +278,10 @@ class RenderWebGL { * @return {?number} New order if changed, or null. */ setDrawableOrder (drawableID, order, optIsRelative, optMin) { - const oldIndex = this._drawables.indexOf(drawableID); + const oldIndex = this._drawList.indexOf(drawableID); if (oldIndex >= 0) { // Remove drawable from the list. - const drawable = this._drawables.splice(oldIndex, 1)[0]; + const drawable = this._drawList.splice(oldIndex, 1)[0]; // Determine new index. let newIndex = order; if (optIsRelative) { @@ -160,8 +292,8 @@ class RenderWebGL { } newIndex = Math.max(newIndex, 0); // Insert at new index. - this._drawables.splice(newIndex, 0, drawable); - return this._drawables.indexOf(drawable); + this._drawList.splice(newIndex, 0, drawable); + return this._drawList.indexOf(drawable); } return null; } @@ -177,7 +309,7 @@ class RenderWebGL { gl.clearColor.apply(gl, this._backgroundColor); gl.clear(gl.COLOR_BUFFER_BIT); - this._drawThese(this._drawables, ShaderManager.DRAW_MODE.default, this._projection); + this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, this._projection); } /** @@ -186,7 +318,7 @@ class RenderWebGL { * @return {object} Bounds for a tight box around the Drawable. */ getBounds (drawableID) { - const drawable = Drawable.getDrawableByID(drawableID); + const drawable = this._allDrawables[drawableID]; // Tell the Drawable about its updated convex hull, if necessary. if (drawable.needsConvexHullPoints()) { const points = this._getConvexHullPointsForDrawable(drawableID); @@ -218,8 +350,8 @@ class RenderWebGL { * @return {Array.} Skin size, width and height. */ getSkinSize (drawableID) { - const drawable = Drawable.getDrawableByID(drawableID); - return drawable.getSkinSize(); + const drawable = this._allDrawables[drawableID]; + return drawable.skin.size; } /** @@ -237,7 +369,7 @@ class RenderWebGL { if (!bounds) { return; } - const candidateIDs = this._filterCandidatesTouching(drawableID, this._drawables, bounds); + const candidateIDs = this._filterCandidatesTouching(drawableID, this._drawList, bounds); if (!candidateIDs) { return; } @@ -321,7 +453,7 @@ class RenderWebGL { * @returns {boolean} True iff the Drawable is touching one of candidateIDs. */ isTouchingDrawables (drawableID, candidateIDs) { - candidateIDs = candidateIDs || this._drawables; + candidateIDs = candidateIDs || this._drawList; const gl = this._gl; @@ -341,7 +473,7 @@ class RenderWebGL { gl.viewport(0, 0, bounds.width, bounds.height); const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.bottom, bounds.top, -1, 1); - const noneColor = Drawable.color4fFromID(Drawable.NONE); + const noneColor = Drawable.color4fFromID(RenderConstants.ID_NONE); gl.clearColor.apply(gl, noneColor); gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); @@ -383,7 +515,7 @@ class RenderWebGL { pixels[pixelBase], pixels[pixelBase + 1], pixels[pixelBase + 2]); - if (pixelID > Drawable.NONE) { + if (pixelID > RenderConstants.ID_NONE) { return true; } } @@ -399,14 +531,14 @@ class RenderWebGL { * @param {int} touchHeight The client height of the touch event (optional). * @param {int[]} candidateIDs The Drawable IDs to pick from, otherwise all. * @returns {int} The ID of the topmost Drawable under the picking location, or - * Drawable.NONE if there is no Drawable at that location. + * RenderConstants.ID_NONE if there is no Drawable at that location. */ pick (centerX, centerY, touchWidth, touchHeight, candidateIDs) { const gl = this._gl; touchWidth = touchWidth || 1; touchHeight = touchHeight || 1; - candidateIDs = candidateIDs || this._drawables; + candidateIDs = candidateIDs || this._drawList; const clientToGLX = gl.canvas.width / gl.canvas.clientWidth; const clientToGLY = gl.canvas.height / gl.canvas.clientHeight; @@ -427,7 +559,7 @@ class RenderWebGL { twgl.bindFramebufferInfo(gl, this._pickBufferInfo); gl.viewport(0, 0, touchWidth, touchHeight); - const noneColor = Drawable.color4fFromID(Drawable.NONE); + const noneColor = Drawable.color4fFromID(RenderConstants.ID_NONE); gl.clearColor.apply(gl, noneColor); gl.clear(gl.COLOR_BUFFER_BIT); @@ -467,9 +599,9 @@ class RenderWebGL { } // Bias toward selecting anything over nothing - hits[Drawable.NONE] = 0; + hits[RenderConstants.ID_NONE] = 0; - let hit = Drawable.NONE; + let hit = RenderConstants.ID_NONE; for (const hitID in hits) { if (hits.hasOwnProperty(hitID) && (hits[hitID] > hits[hit])) { hit = hitID; @@ -485,7 +617,11 @@ class RenderWebGL { * @return {?Rectangle} Rectangle bounds for touching query, or null. */ _touchingBounds (drawableID) { - const drawable = Drawable.getDrawableByID(drawableID); + const drawable = this._allDrawables[drawableID]; + + // TODO: remove this once URL-based skin setting is removed. + if (!drawable.skin || !drawable.skin.getTexture([100, 100])) return null; + const bounds = drawable.getFastBounds(); // Limit queries to the stage size. @@ -517,7 +653,7 @@ class RenderWebGL { candidateIDs = candidateIDs.filter(testID => { if (testID === drawableID) return false; // Only draw items which could possibly overlap target Drawable. - const candidate = Drawable.getDrawableByID(testID); + const candidate = this._allDrawables[testID]; const candidateBounds = candidate.getFastBounds(); return bounds.intersects(candidateBounds); }); @@ -534,7 +670,25 @@ class RenderWebGL { * @param {object.} properties The new property values to set. */ updateDrawableProperties (drawableID, properties) { - const drawable = Drawable.getDrawableByID(drawableID); + const drawable = this._allDrawables[drawableID]; + if (!drawable) { + // TODO: fix whatever's wrong in the VM which causes this, then add a warning or throw here. + // Right now this happens so much on some projects that a warning or exception here can hang the browser. + return; + } + // TODO: remove this after fully deprecating URL-based skin paths + if ('skin' in properties) { + const {skin, costumeResolution} = properties; + const skinId = this.createSkinFromURL(skin, costumeResolution); + drawable.skin = this._allSkins[skinId]; + } + if ('skinId' in properties) { + drawable.skin = this._allSkins[properties.skinId]; + } + if ('rotationCenter' in properties) { + const newRotationCenter = properties.rotationCenter; + drawable.skin.setRotationCenter(newRotationCenter[0], newRotationCenter[1]); + } drawable.updateProperties(properties); } @@ -597,7 +751,7 @@ class RenderWebGL { /** * Draw all Drawables, with the possible exception of - * @param {int[]} drawables The Drawable IDs to draw, possibly this._drawables. + * @param {int[]} drawables The Drawable IDs to draw, possibly this._drawList. * @param {ShaderManager.DRAW_MODE} drawMode Draw normally, silhouette, etc. * @param {module:twgl/m4.Mat4} projection The projection matrix to use. * @param {Drawable~idFilterFunc} [filter] An optional filter function. @@ -615,12 +769,17 @@ class RenderWebGL { // If we have a filter, check whether the ID fails if (filter && !filter(drawableID)) continue; - const drawable = Drawable.getDrawableByID(drawableID); + const drawable = this._allDrawables[drawableID]; // TODO: check if drawable is inside the viewport before anything else // Hidden drawables (e.g., by a "hide" block) are never drawn. if (!drawable.getVisible()) continue; + const drawableScale = drawable.scale; + + // If the texture isn't ready yet, skip it. + if (!drawable.skin.getTexture(drawableScale)) continue; + const effectBits = drawable.getEnabledEffects(); const newShader = this._shaderManager.getShader(drawMode, effectBits); if (currentShader !== newShader) { @@ -631,6 +790,7 @@ class RenderWebGL { twgl.setUniforms(currentShader, {u_fudge: window.fudge || 0}); } + twgl.setUniforms(currentShader, drawable.skin.getUniforms(drawableScale)); twgl.setUniforms(currentShader, drawable.getUniforms()); // Apply extra uniforms after the Drawable's, to allow overwriting. @@ -651,7 +811,7 @@ class RenderWebGL { * @return {Array.>} points Convex hull points, as [[x, y], ...] */ _getConvexHullPointsForDrawable (drawableID) { - const drawable = Drawable.getDrawableByID(drawableID); + const drawable = this._allDrawables[drawableID]; const [width, height] = drawable._uniforms.u_skinSize; // No points in the hull if invisible or size is 0. if (!drawable.getVisible() || width === 0 || height === 0) { @@ -663,8 +823,8 @@ class RenderWebGL { twgl.bindFramebufferInfo(gl, this._queryBufferInfo); gl.viewport(0, 0, width, height); - // Clear the canvas with Drawable.NONE. - const noneColor = Drawable.color4fFromID(Drawable.NONE); + // Clear the canvas with RenderConstants.ID_NONE. + const noneColor = Drawable.color4fFromID(RenderConstants.ID_NONE); gl.clearColor.apply(gl, noneColor); gl.clear(gl.COLOR_BUFFER_BIT); @@ -692,7 +852,7 @@ class RenderWebGL { * Helper method to look up a pixel. * @param {int} x X coordinate of the pixel in `pixels`. * @param {int} y Y coordinate of the pixel in `pixels`. - * @return {int} Known ID at that pixel, or Drawable.NONE. + * @return {int} Known ID at that pixel, or RenderConstants.ID_NONE. */ const _getPixel = (x, y) => { const pixelBase = ((width * y) + x) * 4; @@ -704,14 +864,14 @@ class RenderWebGL { for (let y = 0; y <= height; y++) { // Scan from left. for (let x = 0; x < width; x++) { - if (_getPixel(x, y) > Drawable.NONE) { + if (_getPixel(x, y) > RenderConstants.ID_NONE) { boundaryPoints.push([x, y]); break; } } // Scan from right. for (let x = width - 1; x >= 0; x--) { - if (_getPixel(x, y) > Drawable.NONE) { + if (_getPixel(x, y) > RenderConstants.ID_NONE) { boundaryPoints.push([x, y]); break; } diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js new file mode 100644 index 0000000000..345c61f845 --- /dev/null +++ b/packages/scratch-render/src/SVGSkin.js @@ -0,0 +1,79 @@ +const twgl = require('twgl.js'); + +const Skin = require('./Skin'); +const SvgRenderer = require('./svg-quirks-mode/svg-renderer'); + +class SVGSkin extends Skin { + /** + * Create a new SVG skin. + * @param {!int} id - The ID for this Skin. + * @param {!RenderWebGL} renderer - The renderer which will use this skin. + */ + constructor (id, renderer) { + super(id); + + /** @type {RenderWebGL} */ + this._renderer = renderer; + + /** @type {SvgRenderer} */ + this._svgRenderer = new SvgRenderer(); + + /** @type {WebGLTexture} */ + this._texture = null; + } + + /** + * Dispose of this object. Do not use it after calling this method. + */ + dispose () { + if (this._texture) { + this._renderer.gl.deleteTexture(this._texture); + this._texture = null; + } + super.dispose(); + } + + /** + * @return {[number,number]} the "native" size, in texels, of this skin. + */ + get size () { + return [this._svgRenderer.canvas.width, this._svgRenderer.canvas.height]; + } + + /** + * @param {[number,number]} scale - The scaling factors to be used. + * @return {WebGLTexture} The GL texture representation of this skin when drawing at the given scale. + */ + // eslint-disable-next-line no-unused-vars + getTexture (scale) { + // TODO: re-render a scaled version if the requested scale is significantly larger than the current render + return this._texture; + } + + /** + * Set the contents of this skin to a snapshot of the provided SVG data. + * @param {string} svgData - new SVG to use. + */ + setSVG (svgData) { + this._svgRenderer.fromString(svgData, () => { + const gl = this._renderer.gl; + if (this._texture) { + gl.bindTexture(gl.TEXTURE_2D, this._texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._svgRenderer.canvas); + } else { + const textureOptions = { + auto: true, + mag: gl.NEAREST, + min: gl.NEAREST, // TODO: mipmaps, linear (except pixelate) + wrap: gl.CLAMP_TO_EDGE, + src: this._svgRenderer.canvas + }; + + this._texture = twgl.createTexture(gl, textureOptions); + } + this.emit(Skin.Events.WasAltered); + }); + } +} + +module.exports = SVGSkin; From ccb919176a6336e1442947d281ee5a1099f9af06 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Fri, 20 Jan 2017 13:21:54 -0800 Subject: [PATCH 1807/1971] Merge pull request #72 from cwillisf/pen Pen --- packages/scratch-render/src/RenderWebGL.js | 195 +++++++++++++++++---- 1 file changed, 164 insertions(+), 31 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 2caa6ec0f4..9d7f99ea5c 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1,13 +1,22 @@ +const EventEmitter = require('events'); + const hull = require('hull.js'); const twgl = require('twgl.js'); const xhr = require('xhr'); const BitmapSkin = require('./BitmapSkin'); const Drawable = require('./Drawable'); +const PenSkin = require('./PenSkin'); const RenderConstants = require('./RenderConstants'); const ShaderManager = require('./ShaderManager'); const SVGSkin = require('./SVGSkin'); +/** + * @callback idFilterFunc + * @param {int} drawableID The ID to filter. + * @return {bool} True if the ID passes the filter, otherwise false. + */ + /** * Maximum touch size for a picking check. * TODO: Figure out a reasonable max size. Maybe this should be configurable? @@ -24,7 +33,7 @@ const MAX_TOUCH_SIZE = [3, 3]; const TOLERANCE_TOUCHING_COLOR = 2; -class RenderWebGL { +class RenderWebGL extends EventEmitter { /** * Create a renderer for drawing Scratch sprites to a canvas using WebGL. * Coordinates will default to Scratch 2.0 values if unspecified. @@ -41,6 +50,8 @@ class RenderWebGL { * @constructor */ constructor (canvas, xLeft, xRight, yBottom, yTop) { + super(); + /** @type {Drawable[]} */ this._allDrawables = []; @@ -51,7 +62,7 @@ class RenderWebGL { this._drawList = []; /** @type {WebGLRenderingContext} */ - this._gl = twgl.getWebGLContext(canvas, {alpha: false, stencil: true}); + const gl = this._gl = twgl.getWebGLContext(canvas, {alpha: false, stencil: true}); /** @type {int} */ this._nextDrawableId = RenderConstants.ID_NONE + 1; @@ -65,18 +76,22 @@ class RenderWebGL { /** @type {Object.} */ this._skinUrlMap = {}; + this._shaderManager = new ShaderManager(gl); + + /** @type {HTMLCanvasElement} */ + this._tempCanvas = document.createElement('canvas'); + this._createGeometry(); + this.on(RenderConstants.Events.NativeSizeChanged, this.onNativeSizeChanged); + this.setBackgroundColor(1, 1, 1); this.setStageSize(xLeft || -240, xRight || 240, yBottom || -180, yTop || 180); this.resize(this._nativeSize[0], this._nativeSize[1]); - this._createQueryBuffers(); - const gl = this._gl; gl.disable(gl.DEPTH_TEST); gl.enable(gl.BLEND); // TODO: disable when no partial transparency? gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE); - this._shaderManager = new ShaderManager(gl); } /** @@ -130,8 +145,29 @@ class RenderWebGL { this._xRight = xRight; this._yBottom = yBottom; this._yTop = yTop; - this._nativeSize = [Math.abs(xRight - xLeft), Math.abs(yBottom - yTop)]; + + // swap yBottom & yTop to fit Scratch convention of +y=up this._projection = twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1); + + this._setNativeSize(Math.abs(xRight - xLeft), Math.abs(yBottom - yTop)); + } + + /** + * @return {[int,int]} the "native" size of the stage, which is used for pen, query renders, etc. + */ + getNativeSize () { + return [this._nativeSize[0], this._nativeSize[1]]; + } + + /** + * Set the "native" size of the stage, which is used for pen, query renders, etc. + * @param {int} width - the new width to set. + * @param {int} height - the new height to set. + * @private + */ + _setNativeSize (width, height) { + this._nativeSize = [width, height]; + this.emit(RenderConstants.Events.NativeSizeChanged, {newSize: this._nativeSize}); } /** @@ -223,6 +259,17 @@ class RenderWebGL { return skinId; } + /** + * Create a new PenSkin - a skin which implements a Scratch pen layer. + * @returns {!int} the ID for the new skin. + */ + createPenSkin () { + const skinId = this._nextSkinId++; + const newSkin = new PenSkin(skinId, this); + this._allSkins[skinId] = newSkin; + return skinId; + } + /** * Destroy an existing skin. Do not use the skin or its ID after calling this. * @param {!int} skinId - The ID of the skin to destroy. @@ -374,11 +421,10 @@ class RenderWebGL { return; } - // Limit size of viewport to the bounds around the target Drawable, // and create the projection matrix for the draw. gl.viewport(0, 0, bounds.width, bounds.height); - const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.bottom, bounds.top, -1, 1); + const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); gl.clearColor.apply(gl, this._backgroundColor); gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); @@ -417,7 +463,7 @@ class RenderWebGL { gl.disable(gl.STENCIL_TEST); } - const pixels = new Buffer(bounds.width * bounds.height * 4); + const pixels = new Uint8Array(bounds.width * bounds.height * 4); gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); if (this._debugCanvas) { @@ -425,9 +471,7 @@ class RenderWebGL { this._debugCanvas.height = bounds.height; const context = this._debugCanvas.getContext('2d'); const imageData = context.getImageData(0, 0, bounds.width, bounds.height); - for (let i = 0, bytes = pixels.length; i < bytes; ++i) { - imageData.data[i] = pixels[i]; - } + imageData.data.set(pixels); context.putImageData(imageData, 0, 0); } @@ -471,7 +515,7 @@ class RenderWebGL { // Limit size of viewport to the bounds around the target Drawable, // and create the projection matrix for the draw. gl.viewport(0, 0, bounds.width, bounds.height); - const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.bottom, bounds.top, -1, 1); + const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); const noneColor = Drawable.color4fFromID(RenderConstants.ID_NONE); gl.clearColor.apply(gl, noneColor); @@ -496,7 +540,7 @@ class RenderWebGL { gl.disable(gl.STENCIL_TEST); } - const pixels = new Buffer(bounds.width * bounds.height * 4); + const pixels = new Uint8Array(bounds.width * bounds.height * 4); gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); if (this._debugCanvas) { @@ -504,9 +548,7 @@ class RenderWebGL { this._debugCanvas.height = bounds.height; const context = this._debugCanvas.getContext('2d'); const imageData = context.getImageData(0, 0, bounds.width, bounds.height); - for (let i = 0, bytes = pixels.length; i < bytes; ++i) { - imageData.data[i] = pixels[i]; - } + imageData.data.set(pixels); context.putImageData(imageData, 0, 0); } @@ -575,7 +617,7 @@ class RenderWebGL { this._drawThese(candidateIDs, ShaderManager.DRAW_MODE.silhouette, projection); - const pixels = new Buffer(touchWidth * touchHeight * 4); + const pixels = new Uint8Array(touchWidth * touchHeight * 4); gl.readPixels(0, 0, touchWidth, touchHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels); if (this._debugCanvas) { @@ -583,9 +625,7 @@ class RenderWebGL { this._debugCanvas.height = touchHeight; const context = this._debugCanvas.getContext('2d'); const imageData = context.getImageData(0, 0, touchWidth, touchHeight); - for (let i = 0, bytes = pixels.length; i < bytes; ++i) { - imageData.data[i] = pixels[i]; - } + imageData.data.set(pixels); context.putImageData(imageData, 0, 0); } @@ -692,6 +732,91 @@ class RenderWebGL { drawable.updateProperties(properties); } + /** + * Clear a pen layer. + * @param {int} penSkinID - the unique ID of a Pen Skin. + */ + penClear (penSkinID) { + const skin = /** @type {PenSkin} */ this._allSkins[penSkinID]; + skin.clear(); + } + + /** + * Draw a point on a pen layer. + * @param {int} penSkinID - the unique ID of a Pen Skin. + * @param {PenAttributes} penAttributes - how the point should be drawn. + * @param {number} x - the X coordinate of the point to draw. + * @param {number} y - the Y coordinate of the point to draw. + */ + penPoint (penSkinID, penAttributes, x, y) { + const skin = /** @type {PenSkin} */ this._allSkins[penSkinID]; + skin.drawPoint(penAttributes, x, y); + } + + /** + * Draw a line on a pen layer. + * @param {int} penSkinID - the unique ID of a Pen Skin. + * @param {PenAttributes} penAttributes - how the line should be drawn. + * @param {number} x0 - the X coordinate of the beginning of the line. + * @param {number} y0 - the Y coordinate of the beginning of the line. + * @param {number} x1 - the X coordinate of the end of the line. + * @param {number} y1 - the Y coordinate of the end of the line. + */ + penLine (penSkinID, penAttributes, x0, y0, x1, y1) { + const skin = /** @type {PenSkin} */ this._allSkins[penSkinID]; + skin.drawLine(penAttributes, x0, y0, x1, y1); + } + + /** + * Stamp a Drawable onto a pen layer. + * @param {int} penSkinID - the unique ID of a Pen Skin. + * @param {int} stampID - the unique ID of the Drawable to use as the stamp. + */ + penStamp (penSkinID, stampID) { + const stampDrawable = this._allDrawables[stampID]; + if (!stampDrawable) { + return; + } + + const bounds = this._touchingBounds(stampID); + if (!bounds) { + return; + } + + const skin = /** @type {PenSkin} */ this._allSkins[penSkinID]; + + const gl = this._gl; + twgl.bindFramebufferInfo(gl, this._queryBufferInfo); + + // Limit size of viewport to the bounds around the stamp Drawable and create the projection matrix for the draw. + gl.viewport(0, 0, bounds.width, bounds.height); + const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); + + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + + try { + gl.disable(gl.BLEND); + this._drawThese([stampID], ShaderManager.DRAW_MODE.default, projection); + } finally { + gl.enable(gl.BLEND); + } + + const stampPixels = new Uint8Array(bounds.width * bounds.height * 4); + gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, stampPixels); + + const stampCanvas = this._tempCanvas; + stampCanvas.width = bounds.width; + stampCanvas.height = bounds.height; + + const stampContext = stampCanvas.getContext('2d'); + const stampImageData = stampContext.createImageData(bounds.width, bounds.height); + stampImageData.data.set(stampPixels); + stampContext.putImageData(stampImageData, 0, 0); + + skin.drawStamp(stampCanvas, bounds.left, bounds.top); + } + /* ****** * Truly internal functions: these support the functions above. ********/ @@ -729,24 +854,32 @@ class RenderWebGL { } /** - * Create the frame buffers used for queries such as picking and color-touching. - * These buffers are fixed in size regardless of the size of the main render - * target. The fixed size allows (more) consistent behavior across devices and - * presentation modes. + * Respond to a change in the "native" rendering size. The native size is used by buffers which are fixed in size + * regardless of the size of the main render target. This includes the buffers used for queries such as picking and + * color-touching. The fixed size allows (more) consistent behavior across devices and presentation modes. + * @param {object} event - The change event. * @private */ - _createQueryBuffers () { + onNativeSizeChanged (event) { + const [width, height] = event.newSize; + const gl = this._gl; const attachments = [ {format: gl.RGBA}, {format: gl.DEPTH_STENCIL} ]; - this._pickBufferInfo = twgl.createFramebufferInfo(gl, attachments, MAX_TOUCH_SIZE[0], MAX_TOUCH_SIZE[1]); + if (!this._pickBufferInfo) { + this._pickBufferInfo = twgl.createFramebufferInfo(gl, attachments, MAX_TOUCH_SIZE[0], MAX_TOUCH_SIZE[1]); + } // TODO: should we create this on demand to save memory? // A 480x360 32-bpp buffer is 675 KiB. - this._queryBufferInfo = twgl.createFramebufferInfo(gl, attachments, this._nativeSize[0], this._nativeSize[1]); + if (this._queryBufferInfo) { + twgl.resizeFramebufferInfo(gl, this._queryBufferInfo, attachments, width, height); + } else { + this._queryBufferInfo = twgl.createFramebufferInfo(gl, attachments, width, height); + } } /** @@ -754,7 +887,7 @@ class RenderWebGL { * @param {int[]} drawables The Drawable IDs to draw, possibly this._drawList. * @param {ShaderManager.DRAW_MODE} drawMode Draw normally, silhouette, etc. * @param {module:twgl/m4.Mat4} projection The projection matrix to use. - * @param {Drawable~idFilterFunc} [filter] An optional filter function. + * @param {idFilterFunc} [filter] An optional filter function. * @param {Object.} [extraUniforms] Extra uniforms for the shaders. * @private */ @@ -812,7 +945,7 @@ class RenderWebGL { */ _getConvexHullPointsForDrawable (drawableID) { const drawable = this._allDrawables[drawableID]; - const [width, height] = drawable._uniforms.u_skinSize; + const [width, height] = drawable.skin.size; // No points in the hull if invisible or size is 0. if (!drawable.getVisible() || width === 0 || height === 0) { return []; @@ -842,7 +975,7 @@ class RenderWebGL { {u_modelMatrix: modelMatrix} ); - const pixels = new Buffer(width * height * 4); + const pixels = new Uint8Array(width * height * 4); gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); // Known boundary points on left/right edges of pixels. From 7bf0237cb844b20d8d9c9e3dc52648c7614ea0a5 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 25 Jan 2017 09:43:24 -0500 Subject: [PATCH 1808/1971] Merge pull request #70 from rschamp/bugfix/new-skins-without-rotationCenter Calculate rotation center if it's not supplied --- packages/scratch-render/src/RenderWebGL.js | 22 +++++++++++++--------- packages/scratch-render/src/SVGSkin.js | 6 +++++- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 9d7f99ea5c..2899ad2ee4 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -176,10 +176,12 @@ class RenderWebGL extends EventEmitter { * Use `createBitmapSkin` or `createSVGSkin` instead. * @param {!string} skinUrl The URL of the skin. * @param {!int} [costumeResolution] Optional: resolution for the skin. Ignored unless creating a new Bitmap skin. + * @param {number[]=} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the skin + * will be used. * @returns {!int} The ID of the Skin. * @deprecated */ - createSkinFromURL (skinUrl, costumeResolution) { + createSkinFromURL (skinUrl, costumeResolution, rotationCenter) { if (this._skinUrlMap.hasOwnProperty(skinUrl)) { const existingId = this._skinUrlMap[skinUrl]; @@ -215,7 +217,7 @@ class RenderWebGL extends EventEmitter { url: skinUrl }, (err, response, body) => { if (!err) { - newSkin.setSVG(body); + newSkin.setSVG(body, rotationCenter); } }); } else { @@ -223,7 +225,7 @@ class RenderWebGL extends EventEmitter { const img = new Image(); img.crossOrigin = 'anonymous'; img.onload = () => { - newSkin.setBitmap(img, costumeResolution); + newSkin.setBitmap(img, costumeResolution, rotationCenter); }; img.src = skinUrl; } @@ -236,12 +238,13 @@ class RenderWebGL extends EventEmitter { * Create a new bitmap skin from a snapshot of the provided bitmap data. * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin. * @param {!int} [costumeResolution=1] - The resolution to use for this bitmap. + * @param {number[]=} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the skin * @returns {!int} the ID for the new skin. */ - createBitmapSkin (bitmapData, costumeResolution) { + createBitmapSkin (bitmapData, costumeResolution, rotationCenter) { const skinId = this._nextSkinId++; const newSkin = new BitmapSkin(skinId, this); - newSkin.setBitmap(bitmapData, costumeResolution); + newSkin.setBitmap(bitmapData, costumeResolution, rotationCenter); this._allSkins[skinId] = newSkin; return skinId; } @@ -249,12 +252,13 @@ class RenderWebGL extends EventEmitter { /** * Create a new SVG skin. * @param {!string} svgData - new SVG to use. + * @param {number[]=} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the skin * @returns {!int} the ID for the new skin. */ - createSVGSkin (svgData) { + createSVGSkin (svgData, rotationCenter) { const skinId = this._nextSkinId++; const newSkin = new SVGSkin(skinId, this); - newSkin.setSVG(svgData); + newSkin.setSVG(svgData, rotationCenter); this._allSkins[skinId] = newSkin; return skinId; } @@ -718,8 +722,8 @@ class RenderWebGL extends EventEmitter { } // TODO: remove this after fully deprecating URL-based skin paths if ('skin' in properties) { - const {skin, costumeResolution} = properties; - const skinId = this.createSkinFromURL(skin, costumeResolution); + const {skin, costumeResolution, rotationCenter} = properties; + const skinId = this.createSkinFromURL(skin, costumeResolution, rotationCenter); drawable.skin = this._allSkins[skinId]; } if ('skinId' in properties) { diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 345c61f845..3ef4cd6f4d 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -53,8 +53,10 @@ class SVGSkin extends Skin { /** * Set the contents of this skin to a snapshot of the provided SVG data. * @param {string} svgData - new SVG to use. + * @param {number[]=} rotationCenter - Optional rotation center for the SVG. If not supplied, it will be + * calculated from the bounding box */ - setSVG (svgData) { + setSVG (svgData, rotationCenter) { this._svgRenderer.fromString(svgData, () => { const gl = this._renderer.gl; if (this._texture) { @@ -71,6 +73,8 @@ class SVGSkin extends Skin { this._texture = twgl.createTexture(gl, textureOptions); } + if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); + this.setRotationCenter.apply(this, rotationCenter); this.emit(Skin.Events.WasAltered); }); } From 20ebf5a7374e73ca3e0b5a5d8fd462515366322e Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 7 Feb 2017 13:58:47 -0500 Subject: [PATCH 1809/1971] Merge pull request #80 from rschamp/bugfix/73-retina-size Fix SVG size calculation --- packages/scratch-render/src/SVGSkin.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 3ef4cd6f4d..4d68ca0a53 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -34,10 +34,10 @@ class SVGSkin extends Skin { } /** - * @return {[number,number]} the "native" size, in texels, of this skin. + * @return {[number,number]} the natural size, in Scratch units, of this skin. */ get size () { - return [this._svgRenderer.canvas.width, this._svgRenderer.canvas.height]; + return this._svgRenderer.size; } /** From 707e6f9a6e8f495efef45e83bf8d307a480a2754 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Tue, 14 Feb 2017 16:54:02 -0800 Subject: [PATCH 1810/1971] Merge pull request #82 from griffpatch/feature/fencing Feature fencing --- packages/scratch-render/src/RenderWebGL.js | 40 ++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 2899ad2ee4..af04258c3b 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -736,6 +736,46 @@ class RenderWebGL extends EventEmitter { drawable.updateProperties(properties); } + /** + * Update the position object's x & y members to keep the drawable fenced in view. + * @param {int} drawableID - The ID of the Drawable to update. + * @param {Array.} position to be fenced - An array of type [x, y] + * @return {Array.} The fenced position as an array [x, y] + */ + getFencedPositionOfDrawable (drawableID, position) { + + let x = position[0]; + let y = position[1]; + + const drawable = this._allDrawables[drawableID]; + if (!drawable) { + // TODO: fix whatever's wrong in the VM which causes this, then add a warning or throw here. + // Right now this happens so much on some projects that a warning or exception here can hang the browser. + return [x, y]; + } + + const dx = x - drawable._position[0]; + const dy = y - drawable._position[1]; + + const aabb = drawable.getFastBounds(); + + // This is my best guess at the fencing in Scratch 2, + // but I suspect it may need further work to be precisely the same? + const sx = this._xRight - Math.min(15, Math.floor((aabb.right - aabb.left) / 2)); + if (aabb.right + dx < -sx) { + x = drawable._position[0] - (sx + aabb.right); + } else if (aabb.left + dx > sx) { + x = drawable._position[0] + (sx - aabb.left); + } + const sy = this._yTop - Math.min(15, Math.floor((aabb.top - aabb.bottom) / 2)); + if (aabb.top + dy < -sy) { + y = drawable._position[1] - (sy + aabb.top); + } else if (aabb.bottom + dy > sy) { + y = drawable._position[1] + (sy - aabb.bottom); + } + return [x, y]; + } + /** * Clear a pen layer. * @param {int} penSkinID - the unique ID of a Pen Skin. From 75e5dfe5dde75e507eceadf46c2cc431f97faff3 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 15 Feb 2017 09:39:38 -0500 Subject: [PATCH 1811/1971] Merge pull request #92 from LLK/revert-82-feature/fencing Revert "Feature fencing" --- packages/scratch-render/src/RenderWebGL.js | 40 ---------------------- 1 file changed, 40 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index af04258c3b..2899ad2ee4 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -736,46 +736,6 @@ class RenderWebGL extends EventEmitter { drawable.updateProperties(properties); } - /** - * Update the position object's x & y members to keep the drawable fenced in view. - * @param {int} drawableID - The ID of the Drawable to update. - * @param {Array.} position to be fenced - An array of type [x, y] - * @return {Array.} The fenced position as an array [x, y] - */ - getFencedPositionOfDrawable (drawableID, position) { - - let x = position[0]; - let y = position[1]; - - const drawable = this._allDrawables[drawableID]; - if (!drawable) { - // TODO: fix whatever's wrong in the VM which causes this, then add a warning or throw here. - // Right now this happens so much on some projects that a warning or exception here can hang the browser. - return [x, y]; - } - - const dx = x - drawable._position[0]; - const dy = y - drawable._position[1]; - - const aabb = drawable.getFastBounds(); - - // This is my best guess at the fencing in Scratch 2, - // but I suspect it may need further work to be precisely the same? - const sx = this._xRight - Math.min(15, Math.floor((aabb.right - aabb.left) / 2)); - if (aabb.right + dx < -sx) { - x = drawable._position[0] - (sx + aabb.right); - } else if (aabb.left + dx > sx) { - x = drawable._position[0] + (sx - aabb.left); - } - const sy = this._yTop - Math.min(15, Math.floor((aabb.top - aabb.bottom) / 2)); - if (aabb.top + dy < -sy) { - y = drawable._position[1] - (sy + aabb.top); - } else if (aabb.bottom + dy > sy) { - y = drawable._position[1] + (sy - aabb.bottom); - } - return [x, y]; - } - /** * Clear a pen layer. * @param {int} penSkinID - the unique ID of a Pen Skin. From 7e9c4198edcd40d4c01aacbb43514d1d79c6110d Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Fri, 17 Feb 2017 10:25:46 -0800 Subject: [PATCH 1812/1971] Merge pull request #90 from cwillisf/fix-cachalot SVG: compensate for viewbox offset --- packages/scratch-render/src/SVGSkin.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 4d68ca0a53..86a3c1ce0d 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -40,6 +40,16 @@ class SVGSkin extends Skin { return this._svgRenderer.size; } + /** + * Set the origin, in object space, about which this Skin should rotate. + * @param {number} x - The x coordinate of the new rotation center. + * @param {number} y - The y coordinate of the new rotation center. + */ + setRotationCenter (x, y) { + const viewOffset = this._svgRenderer.viewOffset; + super.setRotationCenter(x - viewOffset[0], y - viewOffset[1]); + } + /** * @param {[number,number]} scale - The scaling factors to be used. * @return {WebGLTexture} The GL texture representation of this skin when drawing at the given scale. From 5ff19087beb5b5e41561b84553883ee8f27ec956 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 21 Feb 2017 14:45:17 -0500 Subject: [PATCH 1813/1971] Merge pull request #97 from rschamp/dnd Add method to retrieve drawable pixel data --- packages/scratch-render/src/RenderWebGL.js | 103 ++++++++++++++++++--- 1 file changed, 90 insertions(+), 13 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 2899ad2ee4..02d664eb3e 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -452,15 +452,14 @@ class RenderWebGL extends EventEmitter { ShaderManager.DRAW_MODE.colorMask : ShaderManager.DRAW_MODE.silhouette, projection, - null, - extraUniforms); + {extraUniforms}); gl.stencilFunc(gl.EQUAL, 1, 1); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); gl.colorMask(true, true, true, true); this._drawThese(candidateIDs, ShaderManager.DRAW_MODE.default, projection, - testID => testID !== drawableID + {idFilterFunc: testID => testID !== drawableID} ); } finally { gl.colorMask(true, true, true, true); @@ -537,7 +536,7 @@ class RenderWebGL extends EventEmitter { gl.colorMask(true, true, true, true); this._drawThese(candidateIDs, ShaderManager.DRAW_MODE.silhouette, projection, - testID => testID !== drawableID + {idFilterFunc: testID => testID !== drawableID} ); } finally { gl.colorMask(true, true, true, true); @@ -655,6 +654,79 @@ class RenderWebGL extends EventEmitter { return hit | 0; } + /** + * @typedef DrawableExtraction + * @property {Uint8Array} data Raw pixel data for the drawable + * @property {int} width Drawable bounding box width + * @property {int} height Drawable bounding box height + * @property {int} x The x coordinate relative to drawable bounding box + * @property {int} y The y coordinate relative to drawable bounding box + */ + + /** + * Return drawable pixel data and picking coordinates relative to the drawable bounds + * @param {int} drawableID The ID of the drawable to get pixel data for + * @param {int} x The client x coordinate of the picking location. + * @param {int} y The client y coordinate of the picking location. + * @return {?DrawableExtraction} Data about the picked drawable + */ + extractDrawable (drawableID, x, y) { + const drawable = this._allDrawables[drawableID]; + if (!drawable) return null; + + const gl = this._gl; + twgl.bindFramebufferInfo(gl, this._queryBufferInfo); + + const bounds = drawable.getFastBounds(); + bounds.snapToInt(); + + // Translate input x and y to coordinates relative to the drawable + const pickX = x - ((this._nativeSize[0] / 2) + bounds.left); + const pickY = y - ((this._nativeSize[1] / 2) - bounds.top); + + // Limit size of viewport to the bounds around the target Drawable, + // and create the projection matrix for the draw. + gl.viewport(0, 0, bounds.width, bounds.height); + const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); + + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + try { + gl.disable(gl.BLEND); + this._drawThese([drawableID], ShaderManager.DRAW_MODE.default, projection, + {effectMask: ~ShaderManager.EFFECT_INFO.ghost.mask}); + } finally { + gl.enable(gl.BLEND); + } + + const data = new Uint8Array(bounds.width * bounds.height * 4); + gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, data); + + if (this._debugCanvas) { + this._debugCanvas.width = bounds.width; + this._debugCanvas.height = bounds.height; + const ctx = this._debugCanvas.getContext('2d'); + const imageData = ctx.createImageData(bounds.width, bounds.height); + imageData.data.set(data); + ctx.putImageData(imageData, 0, 0); + ctx.beginPath(); + ctx.arc(pickX, pickY, 3, 0, 2 * Math.PI, false); + ctx.fillStyle = 'white'; + ctx.fill(); + ctx.lineWidth = 1; + ctx.strokeStyle = 'black'; + ctx.stroke(); + } + + return { + data: data, + width: bounds.width, + height: bounds.height, + x: pickX, + y: pickY + }; + } + /** * Get the candidate bounding box for a touching query. * @param {int} drawableID ID for drawable of query. @@ -891,11 +963,13 @@ class RenderWebGL extends EventEmitter { * @param {int[]} drawables The Drawable IDs to draw, possibly this._drawList. * @param {ShaderManager.DRAW_MODE} drawMode Draw normally, silhouette, etc. * @param {module:twgl/m4.Mat4} projection The projection matrix to use. - * @param {idFilterFunc} [filter] An optional filter function. - * @param {Object.} [extraUniforms] Extra uniforms for the shaders. + * @param {object} [opts] Options for drawing + * @param {idFilterFunc} opts.filter An optional filter function. + * @param {object.} opts.extraUniforms Extra uniforms for the shaders. + * @param {int} opts.effectMask Bitmask for effects to allow * @private */ - _drawThese (drawables, drawMode, projection, filter, extraUniforms) { + _drawThese (drawables, drawMode, projection, opts = {}) { const gl = this._gl; let currentShader = null; @@ -904,7 +978,7 @@ class RenderWebGL extends EventEmitter { const drawableID = drawables[drawableIndex]; // If we have a filter, check whether the ID fails - if (filter && !filter(drawableID)) continue; + if (opts.filter && !opts.filter(drawableID)) continue; const drawable = this._allDrawables[drawableID]; // TODO: check if drawable is inside the viewport before anything else @@ -917,7 +991,8 @@ class RenderWebGL extends EventEmitter { // If the texture isn't ready yet, skip it. if (!drawable.skin.getTexture(drawableScale)) continue; - const effectBits = drawable.getEnabledEffects(); + let effectBits = drawable.getEnabledEffects(); + effectBits &= opts.hasOwnProperty('effectMask') ? opts.effectMask : effectBits; const newShader = this._shaderManager.getShader(drawMode, effectBits); if (currentShader !== newShader) { currentShader = newShader; @@ -931,8 +1006,8 @@ class RenderWebGL extends EventEmitter { twgl.setUniforms(currentShader, drawable.getUniforms()); // Apply extra uniforms after the Drawable's, to allow overwriting. - if (extraUniforms) { - twgl.setUniforms(currentShader, extraUniforms); + if (opts.extraUniforms) { + twgl.setUniforms(currentShader, opts.extraUniforms); } twgl.drawBufferInfo(gl, gl.TRIANGLES, this._bufferInfo); @@ -975,8 +1050,7 @@ class RenderWebGL extends EventEmitter { this._drawThese([drawableID], ShaderManager.DRAW_MODE.silhouette, projection, - null, - {u_modelMatrix: modelMatrix} + {extraUniforms: {u_modelMatrix: modelMatrix}} ); const pixels = new Uint8Array(width * height * 4); @@ -1019,4 +1093,7 @@ class RenderWebGL extends EventEmitter { } } +// :3 +RenderWebGL.prototype.canHazPixels = RenderWebGL.prototype.extractDrawable; + module.exports = RenderWebGL; From 197654d2a85989cb6de8b6e43b920906db16a00a Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 22 Feb 2017 21:15:52 -0800 Subject: [PATCH 1814/1971] Merge pull request #107 from LLK/greenkeeper/twgl.js-3.0.2 chore(package): update twgl.js to version 3.0.2 --- packages/scratch-render/src/RenderWebGL.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 02d664eb3e..5ece0ef2e6 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1010,7 +1010,7 @@ class RenderWebGL extends EventEmitter { twgl.setUniforms(currentShader, opts.extraUniforms); } - twgl.drawBufferInfo(gl, gl.TRIANGLES, this._bufferInfo); + twgl.drawBufferInfo(gl, this._bufferInfo, gl.TRIANGLES); } } From 858309b1343f42a65d7456b5be3a371945344953 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 23 Feb 2017 08:42:10 -0500 Subject: [PATCH 1815/1971] Merge pull request #102 from rschamp/jsdoc Generate docs from src, publish to gh-pages --- packages/scratch-render/src/RenderWebGL.js | 69 +++++++++++++--------- packages/scratch-render/src/SVGSkin.js | 13 ++-- 2 files changed, 48 insertions(+), 34 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 5ece0ef2e6..e290c27cf8 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -12,15 +12,16 @@ const ShaderManager = require('./ShaderManager'); const SVGSkin = require('./SVGSkin'); /** - * @callback idFilterFunc + * @callback RenderWebGL#idFilterFunc * @param {int} drawableID The ID to filter. * @return {bool} True if the ID passes the filter, otherwise false. */ /** * Maximum touch size for a picking check. - * TODO: Figure out a reasonable max size. Maybe this should be configurable? - * @type {int[]} + * @todo Figure out a reasonable max size. Maybe this should be configurable? + * @type {Array} + * @memberof RenderWebGL */ const MAX_TOUCH_SIZE = [3, 3]; @@ -28,7 +29,9 @@ const MAX_TOUCH_SIZE = [3, 3]; * "touching {color}?" or "{color} touching {color}?" tests will be true if the * target is touching a color whose components are each within this tolerance of * the corresponding component of the query color. - * @type {int} between 0 (exact matches only) and 255 (match anything). + * between 0 (exact matches only) and 255 (match anything). + * @type {int} + * @memberof RenderWebGL */ const TOLERANCE_TOUCHING_COLOR = 2; @@ -40,14 +43,15 @@ class RenderWebGL extends EventEmitter { * The stage's "native" size will be calculated from the these coordinates. * For example, the defaults result in a native size of 480x360. * Queries such as "touching color?" will always execute at the native size. - * @see setStageSize - * @see resize + * @see RenderWebGL#setStageSize + * @see RenderWebGL#resize * @param {canvas} canvas The canvas to draw onto. * @param {int} [xLeft=-240] The x-coordinate of the left edge. * @param {int} [xRight=240] The x-coordinate of the right edge. * @param {int} [yBottom=-180] The y-coordinate of the bottom edge. * @param {int} [yTop=180] The y-coordinate of the top edge. * @constructor + * @listens RenderWebGL#event:NativeSizeChanged */ constructor (canvas, xLeft, xRight, yBottom, yTop) { super(); @@ -58,7 +62,7 @@ class RenderWebGL extends EventEmitter { /** @type {Skin[]} */ this._allSkins = []; - /** @type {int[]} */ + /** @type {Array} */ this._drawList = []; /** @type {WebGLRenderingContext} */ @@ -76,6 +80,7 @@ class RenderWebGL extends EventEmitter { /** @type {Object.} */ this._skinUrlMap = {}; + /** @type {ShaderManager} */ this._shaderManager = new ShaderManager(gl); /** @type {HTMLCanvasElement} */ @@ -90,7 +95,8 @@ class RenderWebGL extends EventEmitter { this.resize(this._nativeSize[0], this._nativeSize[1]); gl.disable(gl.DEPTH_TEST); - gl.enable(gl.BLEND); // TODO: disable when no partial transparency? + /** @todo disable when no partial transparency? */ + gl.enable(gl.BLEND); gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE); } @@ -153,7 +159,7 @@ class RenderWebGL extends EventEmitter { } /** - * @return {[int,int]} the "native" size of the stage, which is used for pen, query renders, etc. + * @return {Array} the "native" size of the stage, which is used for pen, query renders, etc. */ getNativeSize () { return [this._nativeSize[0], this._nativeSize[1]]; @@ -164,6 +170,7 @@ class RenderWebGL extends EventEmitter { * @param {int} width - the new width to set. * @param {int} height - the new height to set. * @private + * @fires RenderWebGL#event:NativeSizeChanged */ _setNativeSize (width, height) { this._nativeSize = [width, height]; @@ -176,8 +183,8 @@ class RenderWebGL extends EventEmitter { * Use `createBitmapSkin` or `createSVGSkin` instead. * @param {!string} skinUrl The URL of the skin. * @param {!int} [costumeResolution] Optional: resolution for the skin. Ignored unless creating a new Bitmap skin. - * @param {number[]=} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the skin - * will be used. + * @param {?Array} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the + * skin will be used. * @returns {!int} The ID of the Skin. * @deprecated */ @@ -238,7 +245,8 @@ class RenderWebGL extends EventEmitter { * Create a new bitmap skin from a snapshot of the provided bitmap data. * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin. * @param {!int} [costumeResolution=1] - The resolution to use for this bitmap. - * @param {number[]=} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the skin + * @param {?Array} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the + * skin will be used * @returns {!int} the ID for the new skin. */ createBitmapSkin (bitmapData, costumeResolution, rotationCenter) { @@ -252,7 +260,8 @@ class RenderWebGL extends EventEmitter { /** * Create a new SVG skin. * @param {!string} svgData - new SVG to use. - * @param {number[]=} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the skin + * @param {?Array} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the + * skin will be used * @returns {!int} the ID for the new skin. */ createSVGSkin (svgData, rotationCenter) { @@ -398,7 +407,7 @@ class RenderWebGL extends EventEmitter { /** * Get the current skin (costume) size of a Drawable. * @param {int} drawableID The ID of the Drawable to measure. - * @return {Array.} Skin size, width and height. + * @return {Array} Skin size, width and height. */ getSkinSize (drawableID) { const drawable = this._allDrawables[drawableID]; @@ -408,8 +417,8 @@ class RenderWebGL extends EventEmitter { /** * Check if a particular Drawable is touching a particular color. * @param {int} drawableID The ID of the Drawable to check. - * @param {int[]} color3b Test if the Drawable is touching this color. - * @param {int[]} [mask3b] Optionally mask the check to this part of Drawable. + * @param {Array} color3b Test if the Drawable is touching this color. + * @param {Array} [mask3b] Optionally mask the check to this part of Drawable. * @returns {boolean} True iff the Drawable is touching the color. */ isTouchingColor (drawableID, color3b, mask3b) { @@ -496,7 +505,7 @@ class RenderWebGL extends EventEmitter { /** * Check if a particular Drawable is touching any in a set of Drawables. * @param {int} drawableID The ID of the Drawable to check. - * @param {int[]} candidateIDs The Drawable IDs to check, otherwise all. + * @param {Array} candidateIDs The Drawable IDs to check, otherwise all. * @returns {boolean} True iff the Drawable is touching one of candidateIDs. */ isTouchingDrawables (drawableID, candidateIDs) { @@ -574,7 +583,7 @@ class RenderWebGL extends EventEmitter { * @param {int} centerY The client y coordinate of the picking location. * @param {int} touchWidth The client width of the touch event (optional). * @param {int} touchHeight The client height of the touch event (optional). - * @param {int[]} candidateIDs The Drawable IDs to pick from, otherwise all. + * @param {Array} candidateIDs The Drawable IDs to pick from, otherwise all. * @returns {int} The ID of the topmost Drawable under the picking location, or * RenderConstants.ID_NONE if there is no Drawable at that location. */ @@ -735,7 +744,7 @@ class RenderWebGL extends EventEmitter { _touchingBounds (drawableID) { const drawable = this._allDrawables[drawableID]; - // TODO: remove this once URL-based skin setting is removed. + /** @todo remove this once URL-based skin setting is removed. */ if (!drawable.skin || !drawable.skin.getTexture([100, 100])) return null; const bounds = drawable.getFastBounds(); @@ -758,9 +767,9 @@ class RenderWebGL extends EventEmitter { * Filter a list of candidates for a touching query into only those that * could possibly intersect the given bounds. * @param {int} drawableID - ID for drawable of query. - * @param {Array.} candidateIDs - Candidates for touching query. + * @param {Array} candidateIDs - Candidates for touching query. * @param {Rectangle} bounds - Bounds to limit candidates to. - * @return {?Array.} Filtered candidateIDs, or null if none. + * @return {?Array} Filtered candidateIDs, or null if none. */ _filterCandidatesTouching (drawableID, candidateIDs, bounds) { // Filter candidates by rough bounding box intersection. @@ -788,11 +797,13 @@ class RenderWebGL extends EventEmitter { updateDrawableProperties (drawableID, properties) { const drawable = this._allDrawables[drawableID]; if (!drawable) { - // TODO: fix whatever's wrong in the VM which causes this, then add a warning or throw here. - // Right now this happens so much on some projects that a warning or exception here can hang the browser. + /** + * @todo fix whatever's wrong in the VM which causes this, then add a warning or throw here. + * Right now this happens so much on some projects that a warning or exception here can hang the browser. + */ return; } - // TODO: remove this after fully deprecating URL-based skin paths + /** @todo remove this after fully deprecating URL-based skin paths */ if ('skin' in properties) { const {skin, costumeResolution, rotationCenter} = properties; const skinId = this.createSkinFromURL(skin, costumeResolution, rotationCenter); @@ -949,7 +960,7 @@ class RenderWebGL extends EventEmitter { this._pickBufferInfo = twgl.createFramebufferInfo(gl, attachments, MAX_TOUCH_SIZE[0], MAX_TOUCH_SIZE[1]); } - // TODO: should we create this on demand to save memory? + /** @todo should we create this on demand to save memory? */ // A 480x360 32-bpp buffer is 675 KiB. if (this._queryBufferInfo) { twgl.resizeFramebufferInfo(gl, this._queryBufferInfo, attachments, width, height); @@ -959,8 +970,8 @@ class RenderWebGL extends EventEmitter { } /** - * Draw all Drawables, with the possible exception of - * @param {int[]} drawables The Drawable IDs to draw, possibly this._drawList. + * Draw a set of Drawables, by drawable ID + * @param {Array} drawables The Drawable IDs to draw, possibly this._drawList. * @param {ShaderManager.DRAW_MODE} drawMode Draw normally, silhouette, etc. * @param {module:twgl/m4.Mat4} projection The projection matrix to use. * @param {object} [opts] Options for drawing @@ -981,7 +992,7 @@ class RenderWebGL extends EventEmitter { if (opts.filter && !opts.filter(drawableID)) continue; const drawable = this._allDrawables[drawableID]; - // TODO: check if drawable is inside the viewport before anything else + /** @todo check if drawable is inside the viewport before anything else */ // Hidden drawables (e.g., by a "hide" block) are never drawn. if (!drawable.getVisible()) continue; @@ -1020,7 +1031,7 @@ class RenderWebGL extends EventEmitter { * Read back the pixels and find all boundary points. * Finally, apply a convex hull algorithm to simplify the set. * @param {int} drawableID The Drawable IDs calculate convex hull for. - * @return {Array.>} points Convex hull points, as [[x, y], ...] + * @return {Array>} points Convex hull points, as [[x, y], ...] */ _getConvexHullPointsForDrawable (drawableID) { const drawable = this._allDrawables[drawableID]; diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 86a3c1ce0d..e849bb1a31 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -8,6 +8,8 @@ class SVGSkin extends Skin { * Create a new SVG skin. * @param {!int} id - The ID for this Skin. * @param {!RenderWebGL} renderer - The renderer which will use this skin. + * @constructor + * @extends Skin */ constructor (id, renderer) { super(id); @@ -34,7 +36,7 @@ class SVGSkin extends Skin { } /** - * @return {[number,number]} the natural size, in Scratch units, of this skin. + * @return {Array} the natural size, in Scratch units, of this skin. */ get size () { return this._svgRenderer.size; @@ -51,20 +53,21 @@ class SVGSkin extends Skin { } /** - * @param {[number,number]} scale - The scaling factors to be used. + * @param {Array} scale - The scaling factors to be used. * @return {WebGLTexture} The GL texture representation of this skin when drawing at the given scale. */ // eslint-disable-next-line no-unused-vars getTexture (scale) { - // TODO: re-render a scaled version if the requested scale is significantly larger than the current render + /** @todo re-render a scaled version if the requested scale is significantly larger than the current render */ return this._texture; } /** * Set the contents of this skin to a snapshot of the provided SVG data. * @param {string} svgData - new SVG to use. - * @param {number[]=} rotationCenter - Optional rotation center for the SVG. If not supplied, it will be + * @param {Array} [rotationCenter] - Optional rotation center for the SVG. If not supplied, it will be * calculated from the bounding box + * @fires Skin.event:WasAltered */ setSVG (svgData, rotationCenter) { this._svgRenderer.fromString(svgData, () => { @@ -76,7 +79,7 @@ class SVGSkin extends Skin { const textureOptions = { auto: true, mag: gl.NEAREST, - min: gl.NEAREST, // TODO: mipmaps, linear (except pixelate) + min: gl.NEAREST, /** @todo mipmaps, linear (except pixelate) */ wrap: gl.CLAMP_TO_EDGE, src: this._svgRenderer.canvas }; From 347a68b12e0023ca4e9e367d840a7817b08357bc Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 23 Feb 2017 09:25:53 -0500 Subject: [PATCH 1816/1971] Merge pull request #93 from griffpatch/feature/FenceAgain Feature fenceagain --- packages/scratch-render/src/RenderWebGL.js | 45 ++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index e290c27cf8..11c0c6e31a 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -35,6 +35,13 @@ const MAX_TOUCH_SIZE = [3, 3]; */ const TOLERANCE_TOUCHING_COLOR = 2; +/** + * Sprite Fencing - The number of pixels a sprite is required to leave remaining + * onscreen around the edge of the staging area. + * @type {number} + */ +const FENCE_WIDTH = 15; + class RenderWebGL extends EventEmitter { /** @@ -819,6 +826,44 @@ class RenderWebGL extends EventEmitter { drawable.updateProperties(properties); } + /** + * Update the position object's x & y members to keep the drawable fenced in view. + * @param {int} drawableID - The ID of the Drawable to update. + * @param {Array.} position to be fenced - An array of type [x, y] + * @return {Array.} The fenced position as an array [x, y] + */ + getFencedPositionOfDrawable (drawableID, position) { + + let x = position[0]; + let y = position[1]; + + const drawable = this._allDrawables[drawableID]; + if (!drawable) { + // TODO: fix whatever's wrong in the VM which causes this, then add a warning or throw here. + // Right now this happens so much on some projects that a warning or exception here can hang the browser. + return [x, y]; + } + + const dx = x - drawable._position[0]; + const dy = y - drawable._position[1]; + + const aabb = drawable.getFastBounds(); + + const sx = this._xRight - Math.min(FENCE_WIDTH, Math.floor((aabb.right - aabb.left) / 2)); + if (aabb.right + dx < -sx) { + x = drawable._position[0] - (sx + aabb.right); + } else if (aabb.left + dx > sx) { + x = drawable._position[0] + (sx - aabb.left); + } + const sy = this._yTop - Math.min(FENCE_WIDTH, Math.floor((aabb.top - aabb.bottom) / 2)); + if (aabb.top + dy < -sy) { + y = drawable._position[1] - (sy + aabb.top); + } else if (aabb.bottom + dy > sy) { + y = drawable._position[1] + (sy - aabb.bottom); + } + return [x, y]; + } + /** * Clear a pen layer. * @param {int} penSkinID - the unique ID of a Pen Skin. From 88a1f93cc3eb64e25ac308c896a69836d2cf9662 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 2 Mar 2017 08:47:00 -0500 Subject: [PATCH 1817/1971] Merge pull request #96 from CSnap/fix_getConvexHullPointsForDrawable Round pixelBase --- packages/scratch-render/src/RenderWebGL.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 11c0c6e31a..18e8c4f101 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1122,7 +1122,7 @@ class RenderWebGL extends EventEmitter { * @return {int} Known ID at that pixel, or RenderConstants.ID_NONE. */ const _getPixel = (x, y) => { - const pixelBase = ((width * y) + x) * 4; + const pixelBase = Math.round(((width * y) + x) * 4); // Sometimes SVGs don't have int width and height return Drawable.color3bToID( pixels[pixelBase], pixels[pixelBase + 1], From 32bc9ba7a38af356ccac97ee13cfe0b17b3701df Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Thu, 2 Mar 2017 11:31:45 -0500 Subject: [PATCH 1818/1971] Merge pull request #94 from CSnap/getBounds Fix getBounds for Hidden Sprites --- packages/scratch-render/src/RenderWebGL.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 18e8c4f101..4d871aa0cf 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -391,7 +391,7 @@ class RenderWebGL extends EventEmitter { const points = this._getConvexHullPointsForDrawable(drawableID); drawable.setConvexHullPoints(points); } - const bounds = drawable.getBounds(); + const bounds = drawable.getFastBounds(); // In debug mode, draw the bounds. if (this._debugCanvas) { const gl = this._gl; From 981b65316ddc06d68df57d588ce9020579e7e400 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 3 Mar 2017 09:32:21 -0500 Subject: [PATCH 1819/1971] Merge pull request #113 from rschamp/dnd Add scratchOffset data to extractDrawable --- packages/scratch-render/src/RenderWebGL.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 4d871aa0cf..433fd0922e 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -675,6 +675,8 @@ class RenderWebGL extends EventEmitter { * @property {Uint8Array} data Raw pixel data for the drawable * @property {int} width Drawable bounding box width * @property {int} height Drawable bounding box height + * @property {Array} scratchOffset [x, y] offset in Scratch coordinates + * from the drawable position to the client x, y coordinate * @property {int} x The x coordinate relative to drawable bounding box * @property {int} y The y coordinate relative to drawable bounding box */ @@ -738,6 +740,10 @@ class RenderWebGL extends EventEmitter { data: data, width: bounds.width, height: bounds.height, + scratchOffset: [ + (this._nativeSize[0] / 2) - x + drawable._position[0], + (this._nativeSize[1] / 2) - y - drawable._position[1] + ], x: pickX, y: pickY }; From afed33e6b503d765477ffd660e3af45c9d72db3a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 7 Mar 2017 13:56:45 -0500 Subject: [PATCH 1820/1971] Merge pull request #119 from LLK/stamp-hidden Add stamping flag to make draw ignore visibility --- packages/scratch-render/src/RenderWebGL.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 433fd0922e..79155759a6 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -935,7 +935,7 @@ class RenderWebGL extends EventEmitter { try { gl.disable(gl.BLEND); - this._drawThese([stampID], ShaderManager.DRAW_MODE.default, projection); + this._drawThese([stampID], ShaderManager.DRAW_MODE.default, projection, {isStamping: true}); } finally { gl.enable(gl.BLEND); } @@ -1029,6 +1029,7 @@ class RenderWebGL extends EventEmitter { * @param {idFilterFunc} opts.filter An optional filter function. * @param {object.} opts.extraUniforms Extra uniforms for the shaders. * @param {int} opts.effectMask Bitmask for effects to allow + * @param {boolean} opts.isStamping Stamp mode ignores sprite visibility, always drawing. * @private */ _drawThese (drawables, drawMode, projection, opts = {}) { @@ -1045,8 +1046,8 @@ class RenderWebGL extends EventEmitter { const drawable = this._allDrawables[drawableID]; /** @todo check if drawable is inside the viewport before anything else */ - // Hidden drawables (e.g., by a "hide" block) are never drawn. - if (!drawable.getVisible()) continue; + // Hidden drawables (e.g., by a "hide" block) are not drawn unless stamping + if (!drawable.getVisible() && !opts.isStamping) continue; const drawableScale = drawable.scale; From 0adab9d781172d8f94efd9226696404b96ff1814 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Thu, 23 Mar 2017 22:18:02 -0700 Subject: [PATCH 1821/1971] Merge pull request #122 from cwillisf/remove-url-handling Remove `createSkinFromURL` and related code --- packages/scratch-render/src/RenderWebGL.js | 83 ++-------------------- 1 file changed, 4 insertions(+), 79 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 79155759a6..de322fcfc0 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -2,7 +2,6 @@ const EventEmitter = require('events'); const hull = require('hull.js'); const twgl = require('twgl.js'); -const xhr = require('xhr'); const BitmapSkin = require('./BitmapSkin'); const Drawable = require('./Drawable'); @@ -84,9 +83,6 @@ class RenderWebGL extends EventEmitter { /** @type {module:twgl/m4.Mat4} */ this._projection = twgl.m4.identity(); - /** @type {Object.} */ - this._skinUrlMap = {}; - /** @type {ShaderManager} */ this._shaderManager = new ShaderManager(gl); @@ -184,70 +180,6 @@ class RenderWebGL extends EventEmitter { this.emit(RenderConstants.Events.NativeSizeChanged, {newSize: this._nativeSize}); } - /** - * Create a skin by loading a bitmap or vector image from a URL, or reuse an existing skin created this way. - * WARNING: This method is deprecated and will be removed in the near future. - * Use `createBitmapSkin` or `createSVGSkin` instead. - * @param {!string} skinUrl The URL of the skin. - * @param {!int} [costumeResolution] Optional: resolution for the skin. Ignored unless creating a new Bitmap skin. - * @param {?Array} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the - * skin will be used. - * @returns {!int} The ID of the Skin. - * @deprecated - */ - createSkinFromURL (skinUrl, costumeResolution, rotationCenter) { - if (this._skinUrlMap.hasOwnProperty(skinUrl)) { - const existingId = this._skinUrlMap[skinUrl]; - - // Make sure the "existing" skin hasn't been destroyed - if (this._allSkins[existingId]) { - return existingId; - } - } - - const skinId = this._nextSkinId++; - this._skinUrlMap[skinUrl] = skinId; - - let newSkin; - let isVector; - - const ext = skinUrl.substring(skinUrl.lastIndexOf('.') + 1); - switch (ext) { - case 'svg': - case 'svg/get/': - case 'svgz': - case 'svgz/get/': - isVector = true; - break; - default: - isVector = false; - break; - } - - if (isVector) { - newSkin = new SVGSkin(skinId, this); - xhr.get({ - useXDR: true, - url: skinUrl - }, (err, response, body) => { - if (!err) { - newSkin.setSVG(body, rotationCenter); - } - }); - } else { - newSkin = new BitmapSkin(skinId, this); - const img = new Image(); - img.crossOrigin = 'anonymous'; - img.onload = () => { - newSkin.setBitmap(img, costumeResolution, rotationCenter); - }; - img.src = skinUrl; - } - - this._allSkins[skinId] = newSkin; - return skinId; - } - /** * Create a new bitmap skin from a snapshot of the provided bitmap data. * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin. @@ -306,12 +238,11 @@ class RenderWebGL extends EventEmitter { */ createDrawable () { const drawableID = this._nextDrawableId++; - const drawable = new Drawable(drawableID, this); + const drawable = new Drawable(drawableID); this._allDrawables[drawableID] = drawable; this._drawList.push(drawableID); - const defaultSkinId = this.createSkinFromURL(RenderConstants.DEFAULT_SKIN); - drawable.skin = this._allSkins[defaultSkinId]; + drawable.skin = null; return drawableID; } @@ -816,12 +747,6 @@ class RenderWebGL extends EventEmitter { */ return; } - /** @todo remove this after fully deprecating URL-based skin paths */ - if ('skin' in properties) { - const {skin, costumeResolution, rotationCenter} = properties; - const skinId = this.createSkinFromURL(skin, costumeResolution, rotationCenter); - drawable.skin = this._allSkins[skinId]; - } if ('skinId' in properties) { drawable.skin = this._allSkins[properties.skinId]; } @@ -1051,8 +976,8 @@ class RenderWebGL extends EventEmitter { const drawableScale = drawable.scale; - // If the texture isn't ready yet, skip it. - if (!drawable.skin.getTexture(drawableScale)) continue; + // If the skin or texture isn't ready yet, skip it. + if (!drawable.skin || !drawable.skin.getTexture(drawableScale)) continue; let effectBits = drawable.getEnabledEffects(); effectBits &= opts.hasOwnProperty('effectMask') ? opts.effectMask : effectBits; From fd757e7e3d2c61700a238d08a265370a4422f86f Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 19 Apr 2017 09:45:30 -0700 Subject: [PATCH 1822/1971] Merge pull request #132 from a49594a/patch-1 Fix typed array exception in Edge --- packages/scratch-render/src/RenderWebGL.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index de322fcfc0..e642d2484b 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -413,7 +413,7 @@ class RenderWebGL extends EventEmitter { gl.disable(gl.STENCIL_TEST); } - const pixels = new Uint8Array(bounds.width * bounds.height * 4); + const pixels = new Uint8Array(Math.floor(bounds.width * bounds.height * 4)); gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); if (this._debugCanvas) { @@ -490,7 +490,7 @@ class RenderWebGL extends EventEmitter { gl.disable(gl.STENCIL_TEST); } - const pixels = new Uint8Array(bounds.width * bounds.height * 4); + const pixels = new Uint8Array(Math.floor(bounds.width * bounds.height * 4)); gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); if (this._debugCanvas) { @@ -567,7 +567,7 @@ class RenderWebGL extends EventEmitter { this._drawThese(candidateIDs, ShaderManager.DRAW_MODE.silhouette, projection); - const pixels = new Uint8Array(touchWidth * touchHeight * 4); + const pixels = new Uint8Array(Math.floor(touchWidth * touchHeight * 4)); gl.readPixels(0, 0, touchWidth, touchHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels); if (this._debugCanvas) { @@ -648,7 +648,7 @@ class RenderWebGL extends EventEmitter { gl.enable(gl.BLEND); } - const data = new Uint8Array(bounds.width * bounds.height * 4); + const data = new Uint8Array(Math.floor(bounds.width * bounds.height * 4)); gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, data); if (this._debugCanvas) { @@ -865,7 +865,7 @@ class RenderWebGL extends EventEmitter { gl.enable(gl.BLEND); } - const stampPixels = new Uint8Array(bounds.width * bounds.height * 4); + const stampPixels = new Uint8Array(Math.floor(bounds.width * bounds.height * 4)); gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, stampPixels); const stampCanvas = this._tempCanvas; @@ -1041,7 +1041,7 @@ class RenderWebGL extends EventEmitter { {extraUniforms: {u_modelMatrix: modelMatrix}} ); - const pixels = new Uint8Array(width * height * 4); + const pixels = new Uint8Array(Math.floor(width * height * 4)); gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); // Known boundary points on left/right edges of pixels. From 3816302b9efb2499dd00314f662591631d635379 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 10 Aug 2017 16:02:57 -0400 Subject: [PATCH 1823/1971] Merge pull request #154 from paulkaplan/fix-pick-sizing Fix picking when input size is different from native size --- packages/scratch-render/src/RenderWebGL.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index e642d2484b..7118d37947 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -623,15 +623,19 @@ class RenderWebGL extends EventEmitter { const drawable = this._allDrawables[drawableID]; if (!drawable) return null; + // Convert client coordinates into absolute scratch units + const scratchX = this._nativeSize[0] * ((x / this._gl.canvas.clientWidth) - 0.5); + const scratchY = this._nativeSize[1] * ((y / this._gl.canvas.clientHeight) - 0.5); + const gl = this._gl; twgl.bindFramebufferInfo(gl, this._queryBufferInfo); const bounds = drawable.getFastBounds(); bounds.snapToInt(); - // Translate input x and y to coordinates relative to the drawable - const pickX = x - ((this._nativeSize[0] / 2) + bounds.left); - const pickY = y - ((this._nativeSize[1] / 2) - bounds.top); + // Translate to scratch units relative to the drawable + const pickX = scratchX - bounds.left; + const pickY = scratchY + bounds.top; // Limit size of viewport to the bounds around the target Drawable, // and create the projection matrix for the draw. @@ -672,8 +676,8 @@ class RenderWebGL extends EventEmitter { width: bounds.width, height: bounds.height, scratchOffset: [ - (this._nativeSize[0] / 2) - x + drawable._position[0], - (this._nativeSize[1] / 2) - y - drawable._position[1] + -scratchX + drawable._position[0], + -scratchY - drawable._position[1] ], x: pickX, y: pickY From e8ba7b317385101e37ee80020eb133a41457da06 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 31 Aug 2017 13:17:49 -0400 Subject: [PATCH 1824/1971] Merge pull request #160 from fsih/updateSvg Add updateSVGSkin --- packages/scratch-render/src/RenderWebGL.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 7118d37947..8f0101051d 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -222,6 +222,26 @@ class RenderWebGL extends EventEmitter { return skinId; } + /** + * Update an existing SVG skin, or create an SVG skin if the previous skin was not SVG. + * @param {!int} skinId the ID for the skin to change. + * @param {!string} svgData - new SVG to use. + * @param {?Array} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the + * skin will be used + */ + updateSVGSkin (skinId, svgData, rotationCenter) { + if (this._allSkins[skinId] instanceof SVGSkin) { + this._allSkins[skinId].setSVG(svgData, rotationCenter); + return; + } + + const newSkin = new SVGSkin(skinId, this); + newSkin.setSVG(svgData, rotationCenter); + const oldSkin = this._allSkins[skinId]; + this._allSkins[skinId] = newSkin; + oldSkin.dispose(); + } + /** * Destroy an existing skin. Do not use the skin or its ID after calling this. * @param {!int} skinId - The ID of the skin to destroy. From 4d66ac5678d81ad2ed5164ce8f0ed8c3f3296038 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 31 Aug 2017 17:17:54 -0400 Subject: [PATCH 1825/1971] Merge pull request #163 from fsih/updateSvg2 Update references to the old skin --- packages/scratch-render/src/RenderWebGL.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 8f0101051d..b09e2e3314 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -239,6 +239,13 @@ class RenderWebGL extends EventEmitter { newSkin.setSVG(svgData, rotationCenter); const oldSkin = this._allSkins[skinId]; this._allSkins[skinId] = newSkin; + + // Tell drawables to update + for (const drawable of this._allDrawables) { + if (drawable && drawable.skin === oldSkin) { + drawable.skin = newSkin; + } + } oldSkin.dispose(); } From 3639367aaeb7d6eba4bba1d328bdab47e949b103 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 6 Sep 2017 13:54:08 -0400 Subject: [PATCH 1826/1971] Merge pull request #159 from paulkaplan/color-picking Add color extraction method to renderer --- packages/scratch-render/src/RenderWebGL.js | 68 ++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index b09e2e3314..a1fa471550 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -5,6 +5,7 @@ const twgl = require('twgl.js'); const BitmapSkin = require('./BitmapSkin'); const Drawable = require('./Drawable'); +const Rectangle = require('./Rectangle'); const PenSkin = require('./PenSkin'); const RenderConstants = require('./RenderConstants'); const ShaderManager = require('./ShaderManager'); @@ -711,6 +712,73 @@ class RenderWebGL extends EventEmitter { }; } + /** + * @typedef ColorExtraction + * @property {Uint8Array} data Raw pixel data for the drawable + * @property {int} width Drawable bounding box width + * @property {int} height Drawable bounding box height + * @property {object} color Color object with RGBA properties at picked location + */ + + /** + * Return drawable pixel data and color at a given position + * @param {int} x The client x coordinate of the picking location. + * @param {int} y The client y coordinate of the picking location. + * @param {int} radius The client radius to extract pixels with. + * @return {?ColorExtraction} Data about the picked color + */ + extractColor (x, y, radius) { + const scratchX = Math.round(this._nativeSize[0] * ((x / this._gl.canvas.clientWidth) - 0.5)); + const scratchY = Math.round(-this._nativeSize[1] * ((y / this._gl.canvas.clientHeight) - 0.5)); + + const gl = this._gl; + twgl.bindFramebufferInfo(gl, this._queryBufferInfo); + + const bounds = new Rectangle(); + bounds.initFromBounds(scratchX - radius, scratchX + radius, scratchY - radius, scratchY + radius); + + const pickX = scratchX - bounds.left; + const pickY = bounds.top - scratchY; + + gl.viewport(0, 0, bounds.width, bounds.height); + const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); + + gl.clearColor.apply(gl, this._backgroundColor); + gl.clear(gl.COLOR_BUFFER_BIT); + this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, projection); + + const data = new Uint8Array(Math.floor(bounds.width * bounds.height * 4)); + gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, data); + + const color = { + r: data[Math.floor(4 * (pickY * bounds.width + pickX))], + g: data[Math.floor(4 * (pickY * bounds.width + pickX)) + 1], + b: data[Math.floor(4 * (pickY * bounds.width + pickX)) + 2], + a: data[Math.floor(4 * (pickY * bounds.width + pickX)) + 3] + }; + + if (this._debugCanvas) { + this._debugCanvas.width = bounds.width; + this._debugCanvas.height = bounds.height; + const ctx = this._debugCanvas.getContext('2d'); + const imageData = ctx.createImageData(bounds.width, bounds.height); + imageData.data.set(data); + ctx.putImageData(imageData, 0, 0); + ctx.strokeStyle = 'black'; + ctx.fillStyle = `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`; + ctx.rect(pickX - 4, pickY - 4, 8, 8); + ctx.fill(); + ctx.stroke(); + } + + return { + data: data, + width: bounds.width, + height: bounds.height, + color: color + }; + } + /** * Get the candidate bounding box for a touching query. * @param {int} drawableID ID for drawable of query. From 22be611bfc8673d0626e1e60bb929d8b81340363 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 6 Sep 2017 15:54:00 -0600 Subject: [PATCH 1827/1971] Merge pull request #157 from LLK/greenkeeper/eslint-config-scratch-4.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update eslint-config-scratch to the latest version 🚀 --- packages/scratch-render/src/RenderWebGL.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index a1fa471550..d0b70b75b6 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -750,11 +750,12 @@ class RenderWebGL extends EventEmitter { const data = new Uint8Array(Math.floor(bounds.width * bounds.height * 4)); gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, data); + const pixelBase = Math.floor(4 * ((pickY * bounds.width) + pickX)); const color = { - r: data[Math.floor(4 * (pickY * bounds.width + pickX))], - g: data[Math.floor(4 * (pickY * bounds.width + pickX)) + 1], - b: data[Math.floor(4 * (pickY * bounds.width + pickX)) + 2], - a: data[Math.floor(4 * (pickY * bounds.width + pickX)) + 3] + r: data[pixelBase], + g: data[pixelBase + 1], + b: data[pixelBase + 2], + a: data[pixelBase + 3] }; if (this._debugCanvas) { From df94d16760c117b3ed69aa028e3810a4f8a9a89a Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 6 Sep 2017 17:48:12 -0600 Subject: [PATCH 1828/1971] Merge pull request #162 from joshlory/color-touching-tolerance Match Scratch 2.0 threshold for `isTouchingColor` --- packages/scratch-render/src/RenderWebGL.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index d0b70b75b6..d075bb1622 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -30,10 +30,15 @@ const MAX_TOUCH_SIZE = [3, 3]; * target is touching a color whose components are each within this tolerance of * the corresponding component of the query color. * between 0 (exact matches only) and 255 (match anything). - * @type {int} + * @type {object.} * @memberof RenderWebGL */ -const TOLERANCE_TOUCHING_COLOR = 2; +const TOLERANCE_TOUCHING_COLOR = { + R: 7, + G: 7, + B: 15, + Mask: 2 +}; /** * Sprite Fencing - The number of pixels a sprite is required to leave remaining @@ -412,7 +417,7 @@ class RenderWebGL extends EventEmitter { if (mask3b) { extraUniforms = { u_colorMask: [mask3b[0] / 255, mask3b[1] / 255, mask3b[2] / 255], - u_colorMaskTolerance: TOLERANCE_TOUCHING_COLOR / 255 + u_colorMaskTolerance: TOLERANCE_TOUCHING_COLOR.Mask / 255 }; } @@ -458,9 +463,9 @@ class RenderWebGL extends EventEmitter { const pixelDistanceG = Math.abs(pixels[pixelBase + 1] - color3b[1]); const pixelDistanceB = Math.abs(pixels[pixelBase + 2] - color3b[2]); - if (pixelDistanceR <= TOLERANCE_TOUCHING_COLOR && - pixelDistanceG <= TOLERANCE_TOUCHING_COLOR && - pixelDistanceB <= TOLERANCE_TOUCHING_COLOR) { + if (pixelDistanceR <= TOLERANCE_TOUCHING_COLOR.R && + pixelDistanceG <= TOLERANCE_TOUCHING_COLOR.G && + pixelDistanceB <= TOLERANCE_TOUCHING_COLOR.B) { return true; } } From da1f7f25372e1095d5001228ddf339f3cd30e1d7 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 11 Oct 2017 14:19:53 -0400 Subject: [PATCH 1829/1971] Merge pull request #177 from paulkaplan/say-think Say/think bubble SVG templates --- packages/scratch-render/src/RenderWebGL.js | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index d075bb1622..3b42f68058 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -10,6 +10,7 @@ const PenSkin = require('./PenSkin'); const RenderConstants = require('./RenderConstants'); const ShaderManager = require('./ShaderManager'); const SVGSkin = require('./SVGSkin'); +const SVGTextBubble = require('./util/svg-text-bubble'); /** * @callback RenderWebGL#idFilterFunc @@ -95,6 +96,8 @@ class RenderWebGL extends EventEmitter { /** @type {HTMLCanvasElement} */ this._tempCanvas = document.createElement('canvas'); + this._svgTextBubble = new SVGTextBubble(); + this._createGeometry(); this.on(RenderConstants.Events.NativeSizeChanged, this.onNativeSizeChanged); @@ -228,6 +231,19 @@ class RenderWebGL extends EventEmitter { return skinId; } + /** + * Create a new SVG skin using the text bubble svg creator. The rotation center + * is always placed at the top left. + * @param {!string} type - either "say" or "think". + * @param {!string} text - the text for the bubble. + * @param {!boolean} pointsLeft - which side the bubble is pointing. + * @returns {!int} the ID for the new skin. + */ + createTextSkin (type, text, pointsLeft) { + const bubbleSvg = this._svgTextBubble.buildString(type, text, pointsLeft); + return this.createSVGSkin(bubbleSvg, [0, 0]); + } + /** * Update an existing SVG skin, or create an SVG skin if the previous skin was not SVG. * @param {!int} skinId the ID for the skin to change. @@ -255,6 +271,19 @@ class RenderWebGL extends EventEmitter { oldSkin.dispose(); } + /** + * Update a skin using the text bubble svg creator. + * @param {!int} skinId the ID for the skin to change. + * @param {!string} type - either "say" or "think". + * @param {!string} text - the text for the bubble. + * @param {!boolean} pointsLeft - which side the bubble is pointing. + */ + updateTextSkin (skinId, type, text, pointsLeft) { + const bubbleSvg = this._svgTextBubble.buildString(type, text, pointsLeft); + this.updateSVGSkin(skinId, bubbleSvg, [0, 0]); + } + + /** * Destroy an existing skin. Do not use the skin or its ID after calling this. * @param {!int} skinId - The ID of the skin to destroy. From 276e5bcbb519ac2ae01190d6f85aa56f657f7768 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Mon, 20 Nov 2017 13:29:39 -0800 Subject: [PATCH 1830/1971] Merge pull request #196 from mzgoddard/alpha-shape Emulate silhouette rendering for pick and _getConvexHullPointsForDrawable --- packages/scratch-render/src/RenderWebGL.js | 198 ++++++++++++--------- packages/scratch-render/src/SVGSkin.js | 15 ++ 2 files changed, 131 insertions(+), 82 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 3b42f68058..920a94ea57 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -11,6 +11,7 @@ const RenderConstants = require('./RenderConstants'); const ShaderManager = require('./ShaderManager'); const SVGSkin = require('./SVGSkin'); const SVGTextBubble = require('./util/svg-text-bubble'); +const EffectTransform = require('./EffectTransform'); /** * @callback RenderWebGL#idFilterFunc @@ -606,48 +607,36 @@ class RenderWebGL extends EventEmitter { touchHeight = Math.max(1, Math.min(touchHeight, MAX_TOUCH_SIZE[1])); const pixelLeft = Math.floor(centerX - Math.floor(touchWidth / 2) + 0.5); - const pixelRight = Math.floor(centerX + Math.ceil(touchWidth / 2) + 0.5); const pixelTop = Math.floor(centerY - Math.floor(touchHeight / 2) + 0.5); - const pixelBottom = Math.floor(centerY + Math.ceil(touchHeight / 2) + 0.5); - - twgl.bindFramebufferInfo(gl, this._pickBufferInfo); - gl.viewport(0, 0, touchWidth, touchHeight); - - const noneColor = Drawable.color4fFromID(RenderConstants.ID_NONE); - gl.clearColor.apply(gl, noneColor); - gl.clear(gl.COLOR_BUFFER_BIT); const widthPerPixel = (this._xRight - this._xLeft) / this._gl.canvas.width; const heightPerPixel = (this._yBottom - this._yTop) / this._gl.canvas.height; const pickLeft = this._xLeft + (pixelLeft * widthPerPixel); - const pickRight = this._xLeft + (pixelRight * widthPerPixel); const pickTop = this._yTop + (pixelTop * heightPerPixel); - const pickBottom = this._yTop + (pixelBottom * heightPerPixel); - - const projection = twgl.m4.ortho(pickLeft, pickRight, pickTop, pickBottom, -1, 1); - - this._drawThese(candidateIDs, ShaderManager.DRAW_MODE.silhouette, projection); - - const pixels = new Uint8Array(Math.floor(touchWidth * touchHeight * 4)); - gl.readPixels(0, 0, touchWidth, touchHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels); - - if (this._debugCanvas) { - this._debugCanvas.width = touchWidth; - this._debugCanvas.height = touchHeight; - const context = this._debugCanvas.getContext('2d'); - const imageData = context.getImageData(0, 0, touchWidth, touchHeight); - imageData.data.set(pixels); - context.putImageData(imageData, 0, 0); - } - const hits = {}; - for (let pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { - const pixelID = Drawable.color3bToID( - pixels[pixelBase], - pixels[pixelBase + 1], - pixels[pixelBase + 2]); - hits[pixelID] = (hits[pixelID] || 0) + 1; + const hits = []; + const worldPos = twgl.v3.create(0, 0, 0); + worldPos[2] = 0; + + // Iterate over the canvas pixels and check if any candidate can be + // touched at that point. + for (let x = 0; x < touchWidth; x++) { + worldPos[0] = x + pickLeft; + for (let y = 0; y < touchHeight; y++) { + worldPos[1] = y + pickTop; + // Check candidates in the reverse order they would have been + // drawn. This will determine what candiate's silhouette pixel + // would have been drawn at the point. + for (let d = candidateIDs.length - 1; d >= 0; d--) { + const id = candidateIDs[d]; + const drawable = this._allDrawables[id]; + if (drawable.isTouching(worldPos)) { + hits[id] = (hits[id] || 0) + 1; + break; + } + } + } } // Bias toward selecting anything over nothing @@ -1152,63 +1141,108 @@ class RenderWebGL extends EventEmitter { return []; } - // Only draw to the size of the untransformed drawable. - const gl = this._gl; - twgl.bindFramebufferInfo(gl, this._queryBufferInfo); - gl.viewport(0, 0, width, height); - - // Clear the canvas with RenderConstants.ID_NONE. - const noneColor = Drawable.color4fFromID(RenderConstants.ID_NONE); - gl.clearColor.apply(gl, noneColor); - gl.clear(gl.COLOR_BUFFER_BIT); - - // Overwrite the model matrix to be unrotated, unscaled, untranslated. - const modelMatrix = twgl.m4.identity(); - twgl.m4.rotateZ(modelMatrix, Math.PI, modelMatrix); - twgl.m4.scale(modelMatrix, [width, height], modelMatrix); - - const projection = twgl.m4.ortho(-0.5 * width, 0.5 * width, -0.5 * height, 0.5 * height, -1, 1); - - this._drawThese([drawableID], - ShaderManager.DRAW_MODE.silhouette, - projection, - {extraUniforms: {u_modelMatrix: modelMatrix}} - ); - - const pixels = new Uint8Array(Math.floor(width * height * 4)); - gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); - - // Known boundary points on left/right edges of pixels. - const boundaryPoints = []; - /** - * Helper method to look up a pixel. - * @param {int} x X coordinate of the pixel in `pixels`. - * @param {int} y Y coordinate of the pixel in `pixels`. - * @return {int} Known ID at that pixel, or RenderConstants.ID_NONE. + * Return the determinant of two vectors, the vector from A to B and + * the vector from A to C. + * + * The determinant is useful in this case to know if AC is counter + * clockwise from AB. A positive value means the AC is counter + * clockwise from AC. A negative value menas AC is clockwise from AB. + * + * @param {Float32Array} A A 2d vector in space. + * @param {Float32Array} B A 2d vector in space. + * @param {Float32Array} C A 2d vector in space. + * @return {number} Greater than 0 if counter clockwise, less than if + * clockwise, 0 if all points are on a line. */ - const _getPixel = (x, y) => { - const pixelBase = Math.round(((width * y) + x) * 4); // Sometimes SVGs don't have int width and height - return Drawable.color3bToID( - pixels[pixelBase], - pixels[pixelBase + 1], - pixels[pixelBase + 2]); + const CCW = function (A, B, C) { + // AB = B - A + // AC = C - A + // det (AB BC) = AB0 * AC1 - AB1 * AC0 + return (((B[0] - A[0]) * (C[1] - A[1])) - ((B[1] - A[1]) * (C[0] - A[0]))); }; - for (let y = 0; y <= height; y++) { - // Scan from left. - for (let x = 0; x < width; x++) { - if (_getPixel(x, y) > RenderConstants.ID_NONE) { - boundaryPoints.push([x, y]); + + // https://github.com/LLK/scratch-flash/blob/dcbeeb59d44c3be911545dfe54d + // 46a32404f8e69/src/scratch/ScratchCostume.as#L369-L413 Following + // RasterHull creation, compare and store left and right values that + // maintain a convex shape until that data can be passed to `hull` for + // further work. + const L = []; + const R = []; + const _pixelPos = twgl.v3.create(); + const _effectPos = twgl.v3.create(); + let ll = -1; + let rr = -1; + let Q; + for (let y = 0; y < height; y++) { + _pixelPos[1] = y / height; + // Scan from left to right, looking for a touchable spot in the + // skin. + let x = 0; + for (; x < width; x++) { + _pixelPos[0] = x / width; + EffectTransform.transformPoint(drawable, _pixelPos, _effectPos); + if (drawable.skin.isTouching(_effectPos)) { + Q = [x, y]; + break; + } + } + // If x is equal to the width there are no touchable points in the + // skin. Nothing we can add to L. And looping for R would find the + // same thing. + if (x === width) { + continue; + } + // Decrement ll until Q is clockwise (CCW returns negative) from the + // last two points in L. + while (ll > 0) { + if (CCW(L[ll - 1], L[ll], Q) < 0) { break; + } else { + --ll; } } - // Scan from right. - for (let x = width - 1; x >= 0; x--) { - if (_getPixel(x, y) > RenderConstants.ID_NONE) { - boundaryPoints.push([x, y]); + // Increment ll and then set L[ll] to Q. If ll was -1 before this + // line, this will set L[0] to Q. If ll was 0 before this line, this + // will set L[1] to Q. + L[++ll] = Q; + + // Scan from right to left, looking for a touchable spot in the + // skin. + for (x = width - 1; x >= 0; x--) { + _pixelPos[0] = x / width; + EffectTransform.transformPoint(drawable, _pixelPos, _effectPos); + if (drawable.skin.isTouching(_effectPos)) { + Q = [x, y]; break; } } + // Decrement rr until Q is counter clockwise (CCW returns positive) + // from the last two points in L. L takes clockwise points and R + // takes counter clockwise points. if y was decremented instead of + // incremented R would take clockwise points. We are going in the + // right direction for L and the wrong direction for R, so we + // compare the opposite value for R from L. + while (rr > 0) { + if (CCW(R[rr - 1], R[rr], Q) > 0) { + break; + } else { + --rr; + } + } + // Increment rr and then set R[rr] to Q. + R[++rr] = Q; + } + + // Known boundary points on left/right edges of pixels. + const boundaryPoints = L; + // Truncate boundaryPoints to the index of the last added Q to L. L may + // have more entries than the index for the last Q. + boundaryPoints.length = ll + 1; + // Add points in R to boundaryPoints in reverse so all points in + // boundaryPoints are clockwise from each other. + for (let j = rr; j >= 0; --j) { + boundaryPoints.push(R[j]); } // Simplify boundary points using convex hull. return hull(boundaryPoints, Infinity); diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index e849bb1a31..235dde30b9 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -3,6 +3,8 @@ const twgl = require('twgl.js'); const Skin = require('./Skin'); const SvgRenderer = require('./svg-quirks-mode/svg-renderer'); +const Silhouette = require('./Silhouette'); + class SVGSkin extends Skin { /** * Create a new SVG skin. @@ -22,6 +24,8 @@ class SVGSkin extends Skin { /** @type {WebGLTexture} */ this._texture = null; + + this._silhouette = new Silhouette(); } /** @@ -75,6 +79,7 @@ class SVGSkin extends Skin { if (this._texture) { gl.bindTexture(gl.TEXTURE_2D, this._texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._svgRenderer.canvas); + this._silhouette.update(this._svgRenderer.canvas); } else { const textureOptions = { auto: true, @@ -85,12 +90,22 @@ class SVGSkin extends Skin { }; this._texture = twgl.createTexture(gl, textureOptions); + this._silhouette.update(this._svgRenderer.canvas); } if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); this.setRotationCenter.apply(this, rotationCenter); this.emit(Skin.Events.WasAltered); }); } + + /** + * Does this point touch an opaque or translucent point on this skin? + * @param {twgl.v3} vec A texture coordinate. + * @return {boolean} Did it touch? + */ + isTouching (vec) { + return this._silhouette.isTouching(vec); + } } module.exports = SVGSkin; From ca4bd2797ab3ebe88eb2620b95ec16d581e12d11 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Mon, 20 Nov 2017 17:25:42 -0800 Subject: [PATCH 1831/1971] Merge pull request #193 from griffpatch/touching-color-white Touching color white always returns "true" --- packages/scratch-render/src/RenderWebGL.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 920a94ea57..a3d177c8e1 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -42,6 +42,13 @@ const TOLERANCE_TOUCHING_COLOR = { Mask: 2 }; +/** + * Constant used for masking when detecting the color white + * @type {Array} + * @memberof RenderWebGL + */ +const COLOR_BLACK = [0, 0, 0, 1]; + /** * Sprite Fencing - The number of pixels a sprite is required to leave remaining * onscreen around the edge of the staging area. @@ -440,7 +447,15 @@ class RenderWebGL extends EventEmitter { gl.viewport(0, 0, bounds.width, bounds.height); const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); - gl.clearColor.apply(gl, this._backgroundColor); + let fillBackgroundColor = this._backgroundColor; + + // When using masking such that the background fill color will showing through, ensure we don't + // fill using the same color that we are trying to detect! + if (color3b[0] > 196 && color3b[1] > 196 && color3b[2] > 196) { + fillBackgroundColor = COLOR_BLACK; + } + + gl.clearColor.apply(gl, fillBackgroundColor); gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); let extraUniforms; From ea3c8f6b4bffa379c0b39f7e9f3e5137b86f3b2b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 1 Dec 2017 11:27:07 -0500 Subject: [PATCH 1832/1971] Merge pull request #206 from paulkaplan/fix-convex-hull-bug Fix convex hull calculation bug --- packages/scratch-render/src/RenderWebGL.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index a3d177c8e1..b3ce3535b1 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1205,7 +1205,7 @@ class RenderWebGL extends EventEmitter { // If x is equal to the width there are no touchable points in the // skin. Nothing we can add to L. And looping for R would find the // same thing. - if (x === width) { + if (x >= width) { continue; } // Decrement ll until Q is clockwise (CCW returns negative) from the From 4849ae3302a799c1552842732c04098f4b81ac29 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 8 Dec 2017 09:02:11 -0500 Subject: [PATCH 1833/1971] Merge pull request #211 from paulkaplan/do-not-pick-invisibles Exclude hidden drawables from picking. --- packages/scratch-render/src/RenderWebGL.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index b3ce3535b1..b425dec0d9 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -594,7 +594,8 @@ class RenderWebGL extends EventEmitter { } /** - * Detect which sprite, if any, is at the given location. + * Detect which sprite, if any, is at the given location. This function will not + * pick drawables that are not visible or have ghost set all the way up. * @param {int} centerX The client x coordinate of the picking location. * @param {int} centerY The client y coordinate of the picking location. * @param {int} touchWidth The client width of the touch event (optional). @@ -608,7 +609,11 @@ class RenderWebGL extends EventEmitter { touchWidth = touchWidth || 1; touchHeight = touchHeight || 1; - candidateIDs = candidateIDs || this._drawList; + candidateIDs = (candidateIDs || this._drawList).filter(id => { + const drawable = this._allDrawables[id]; + const uniforms = drawable.getUniforms(); + return drawable.getVisible() && uniforms.u_ghost !== 0; + }); const clientToGLX = gl.canvas.width / gl.canvas.clientWidth; const clientToGLY = gl.canvas.height / gl.canvas.clientHeight; From 3b08e08e5a84964da599ac0c40a2e56d09c69608 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 11 Dec 2017 09:16:16 -0500 Subject: [PATCH 1834/1971] Merge pull request #213 from paulkaplan/touching-invisible Ignore sprite visibility when testing for touching color --- packages/scratch-render/src/RenderWebGL.js | 23 +++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index b425dec0d9..79442d3ac8 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -424,6 +424,7 @@ class RenderWebGL extends EventEmitter { /** * Check if a particular Drawable is touching a particular color. + * Unlike touching drawable, touching color tests invisible sprites. * @param {int} drawableID The ID of the Drawable to check. * @param {Array} color3b Test if the Drawable is touching this color. * @param {Array} [mask3b] Optionally mask the check to this part of Drawable. @@ -435,11 +436,11 @@ class RenderWebGL extends EventEmitter { const bounds = this._touchingBounds(drawableID); if (!bounds) { - return; + return false; } const candidateIDs = this._filterCandidatesTouching(drawableID, this._drawList, bounds); if (!candidateIDs) { - return; + return false; } // Limit size of viewport to the bounds around the target Drawable, @@ -477,7 +478,10 @@ class RenderWebGL extends EventEmitter { ShaderManager.DRAW_MODE.colorMask : ShaderManager.DRAW_MODE.silhouette, projection, - {extraUniforms}); + { + extraUniforms, + ignoreVisibility: true // Touching color ignores sprite visibility + }); gl.stencilFunc(gl.EQUAL, 1, 1); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); @@ -533,11 +537,11 @@ class RenderWebGL extends EventEmitter { const bounds = this._touchingBounds(drawableID); if (!bounds) { - return; + return false; } candidateIDs = this._filterCandidatesTouching(drawableID, candidateIDs, bounds); if (!candidateIDs) { - return; + return false; } // Limit size of viewport to the bounds around the target Drawable, @@ -1003,7 +1007,7 @@ class RenderWebGL extends EventEmitter { try { gl.disable(gl.BLEND); - this._drawThese([stampID], ShaderManager.DRAW_MODE.default, projection, {isStamping: true}); + this._drawThese([stampID], ShaderManager.DRAW_MODE.default, projection, {ignoreVisibility: true}); } finally { gl.enable(gl.BLEND); } @@ -1097,7 +1101,7 @@ class RenderWebGL extends EventEmitter { * @param {idFilterFunc} opts.filter An optional filter function. * @param {object.} opts.extraUniforms Extra uniforms for the shaders. * @param {int} opts.effectMask Bitmask for effects to allow - * @param {boolean} opts.isStamping Stamp mode ignores sprite visibility, always drawing. + * @param {boolean} opts.ignoreVisibility Draw all, despite visibility (e.g. stamping, touching color) * @private */ _drawThese (drawables, drawMode, projection, opts = {}) { @@ -1114,8 +1118,9 @@ class RenderWebGL extends EventEmitter { const drawable = this._allDrawables[drawableID]; /** @todo check if drawable is inside the viewport before anything else */ - // Hidden drawables (e.g., by a "hide" block) are not drawn unless stamping - if (!drawable.getVisible() && !opts.isStamping) continue; + // Hidden drawables (e.g., by a "hide" block) are not drawn unless + // the ignoreVisibility flag is used (e.g. for stamping or touchingColor). + if (!drawable.getVisible() && !opts.ignoreVisibility) continue; const drawableScale = drawable.scale; From d0c4652021a9e5181460977303a665983a65110b Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Mon, 11 Dec 2017 11:54:57 -0800 Subject: [PATCH 1835/1971] Merge pull request #204 from cwillisf/use-linear-filtering Use bilinear filtering except when we shouldn't --- packages/scratch-render/src/RenderWebGL.js | 31 +++++++++++++++++----- packages/scratch-render/src/SVGSkin.js | 3 +-- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 79442d3ac8..44f69c1e48 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -911,7 +911,6 @@ class RenderWebGL extends EventEmitter { * @return {Array.} The fenced position as an array [x, y] */ getFencedPositionOfDrawable (drawableID, position) { - let x = position[0]; let y = position[1]; @@ -1105,6 +1104,13 @@ class RenderWebGL extends EventEmitter { * @private */ _drawThese (drawables, drawMode, projection, opts = {}) { + const near = function (a, b, relativeTolerance = 0.01) { + const absA = Math.abs(a); + const absB = Math.abs(b); + const error = Math.abs(a - b) / Math.max(absA, absB); + return error < relativeTolerance; + }; + const gl = this._gl; let currentShader = null; @@ -1127,6 +1133,8 @@ class RenderWebGL extends EventEmitter { // If the skin or texture isn't ready yet, skip it. if (!drawable.skin || !drawable.skin.getTexture(drawableScale)) continue; + const uniforms = {}; + let effectBits = drawable.getEnabledEffects(); effectBits &= opts.hasOwnProperty('effectMask') ? opts.effectMask : effectBits; const newShader = this._shaderManager.getShader(drawMode, effectBits); @@ -1134,18 +1142,29 @@ class RenderWebGL extends EventEmitter { currentShader = newShader; gl.useProgram(currentShader.program); twgl.setBuffersAndAttributes(gl, currentShader, this._bufferInfo); - twgl.setUniforms(currentShader, {u_projectionMatrix: projection}); - twgl.setUniforms(currentShader, {u_fudge: window.fudge || 0}); + Object.assign(uniforms, { + u_projectionMatrix: projection, + u_fudge: window.fudge || 0 + }); } - twgl.setUniforms(currentShader, drawable.skin.getUniforms(drawableScale)); - twgl.setUniforms(currentShader, drawable.getUniforms()); + Object.assign(uniforms, + drawable.skin.getUniforms(drawableScale), + drawable.getUniforms()); // Apply extra uniforms after the Drawable's, to allow overwriting. if (opts.extraUniforms) { - twgl.setUniforms(currentShader, opts.extraUniforms); + Object.assign(uniforms, opts.extraUniforms); + } + + if (uniforms.u_skin) { + const useNearest = + (drawable._direction % 90 === 0) && (near(drawableScale, 100) || drawable.skin.isRaster); + twgl.setTextureParameters(gl, uniforms.u_skin, {minMag: useNearest ? gl.NEAREST : gl.LINEAR}); } + twgl.setUniforms(currentShader, uniforms); + twgl.drawBufferInfo(gl, this._bufferInfo, gl.TRIANGLES); } } diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 235dde30b9..186eb16093 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -81,10 +81,9 @@ class SVGSkin extends Skin { gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._svgRenderer.canvas); this._silhouette.update(this._svgRenderer.canvas); } else { + // TODO: mipmaps? const textureOptions = { auto: true, - mag: gl.NEAREST, - min: gl.NEAREST, /** @todo mipmaps, linear (except pixelate) */ wrap: gl.CLAMP_TO_EDGE, src: this._svgRenderer.canvas }; From 241fa9367f0023c480938c06c8b59b13dd68ed79 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Thu, 11 Jan 2018 11:38:57 -0800 Subject: [PATCH 1836/1971] Merge pull request #226 from cwillisf/check-for-webgl-support Check & report if browser supports this renderer --- packages/scratch-render/src/RenderWebGL.js | 34 ++++++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 44f69c1e48..33ae9d5b62 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -58,6 +58,31 @@ const FENCE_WIDTH = 15; class RenderWebGL extends EventEmitter { + /** + * Check if this environment appears to support this renderer before attempting to create an instance. + * Catching an exception from the constructor is also a valid way to test for (lack of) support. + * @param {canvas} [optCanvas] - An optional canvas to use for the test. Otherwise a temporary canvas will be used. + * @returns {boolean} - True if this environment appears to support this renderer, false otherwise. + */ + static isSupported (optCanvas) { + try { + // Create the context the same way that the constructor will: attributes may make the difference. + return !!RenderWebGL._getContext(optCanvas || document.createElement('canvas')); + } catch (e) { + return false; + } + } + + /** + * Ask TWGL to create a rendering context with the attributes used by this renderer. + * @param {canvas} canvas - attach the context to this canvas. + * @returns {WebGLRenderingContext} - a TWGL rendering context (backed by either WebGL 1.0 or 2.0). + * @private + */ + static _getContext (canvas) { + return twgl.getWebGLContext(canvas, {alpha: false, stencil: true}); + } + /** * Create a renderer for drawing Scratch sprites to a canvas using WebGL. * Coordinates will default to Scratch 2.0 values if unspecified. @@ -77,6 +102,12 @@ class RenderWebGL extends EventEmitter { constructor (canvas, xLeft, xRight, yBottom, yTop) { super(); + /** @type {WebGLRenderingContext} */ + const gl = this._gl = RenderWebGL._getContext(canvas); + if (!gl) { + throw new Error('Could not get WebGL context: this browser or environment may not support WebGL.'); + } + /** @type {Drawable[]} */ this._allDrawables = []; @@ -86,9 +117,6 @@ class RenderWebGL extends EventEmitter { /** @type {Array} */ this._drawList = []; - /** @type {WebGLRenderingContext} */ - const gl = this._gl = twgl.getWebGLContext(canvas, {alpha: false, stencil: true}); - /** @type {int} */ this._nextDrawableId = RenderConstants.ID_NONE + 1; From 0a83661391c43ce3da803c68d4d58df5bb78fba7 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 17 Jan 2018 10:07:13 -0500 Subject: [PATCH 1837/1971] Merge pull request #232 from cwillisf/rasterize-scaled-svg Re-rasterize SVGs when scaled up --- packages/scratch-render/src/SVGSkin.js | 39 ++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 186eb16093..3ca609996d 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -5,6 +5,8 @@ const SvgRenderer = require('./svg-quirks-mode/svg-renderer'); const Silhouette = require('./Silhouette'); +const MAX_TEXTURE_DIMENSION = 2048; + class SVGSkin extends Skin { /** * Create a new SVG skin. @@ -25,6 +27,12 @@ class SVGSkin extends Skin { /** @type {WebGLTexture} */ this._texture = null; + /** @type {number} */ + this._textureScale = 1; + + /** @type {Number} */ + this._maxTextureScale = 0; + this._silhouette = new Silhouette(); } @@ -57,12 +65,29 @@ class SVGSkin extends Skin { } /** - * @param {Array} scale - The scaling factors to be used. + * @param {Array} scale - The scaling factors to be used, each in the [0,100] range. * @return {WebGLTexture} The GL texture representation of this skin when drawing at the given scale. */ // eslint-disable-next-line no-unused-vars getTexture (scale) { - /** @todo re-render a scaled version if the requested scale is significantly larger than the current render */ + // The texture only ever gets uniform scale. Take the larger of the two axes. + const scaleMax = scale ? Math.max(Math.abs(scale[0]), Math.abs(scale[1])) : 100; + const requestedScale = Math.min(scaleMax / 100, this._maxTextureScale); + let newScale = this._textureScale; + while ((newScale < this._maxTextureScale) && (requestedScale >= 1.5 * newScale)) { + newScale *= 2; + } + if (this._textureScale !== newScale) { + this._textureScale = newScale; + this._svgRenderer._draw(this._textureScale, () => { + if (this._textureScale === newScale) { + const gl = this._renderer.gl; + gl.bindTexture(gl.TEXTURE_2D, this._texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._svgRenderer.canvas); + } + }); + } + return this._texture; } @@ -74,8 +99,9 @@ class SVGSkin extends Skin { * @fires Skin.event:WasAltered */ setSVG (svgData, rotationCenter) { - this._svgRenderer.fromString(svgData, () => { + this._svgRenderer.fromString(svgData, 1, () => { const gl = this._renderer.gl; + this._textureScale = this._maxTextureScale = 1; if (this._texture) { gl.bindTexture(gl.TEXTURE_2D, this._texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._svgRenderer.canvas); @@ -91,6 +117,13 @@ class SVGSkin extends Skin { this._texture = twgl.createTexture(gl, textureOptions); this._silhouette.update(this._svgRenderer.canvas); } + + const maxDimension = Math.max(this._svgRenderer.canvas.width, this._svgRenderer.canvas.height); + let testScale = 2; + for (testScale; maxDimension * testScale <= MAX_TEXTURE_DIMENSION; testScale *= 2) { + this._maxTextureScale = testScale; + } + if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); this.setRotationCenter.apply(this, rotationCenter); this.emit(Skin.Events.WasAltered); From 26182665455c1acbb9ac84ad9a102fb20ef5ba5e Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 22 Jan 2018 20:15:48 -0500 Subject: [PATCH 1838/1971] Merge pull request #234 from fsih/pull231 Extract svg-renderer out into a separate module --- packages/scratch-render/src/SVGSkin.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 3ca609996d..409ed1fceb 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -1,9 +1,8 @@ const twgl = require('twgl.js'); -const Skin = require('./Skin'); -const SvgRenderer = require('./svg-quirks-mode/svg-renderer'); - const Silhouette = require('./Silhouette'); +const Skin = require('./Skin'); +const SvgRenderer = require('scratch-svg-renderer'); const MAX_TEXTURE_DIMENSION = 2048; From a4b1628647076eb92ed7fd1645e054e73747e6e0 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 24 Jan 2018 15:21:16 -0800 Subject: [PATCH 1839/1971] Merge pull request #227 from cwillisf/use-browser-field Update packaging to use UMD and the browser field --- packages/scratch-render/src/RenderWebGL.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 33ae9d5b62..929b7534f4 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -229,8 +229,8 @@ class RenderWebGL extends EventEmitter { * Create a new bitmap skin from a snapshot of the provided bitmap data. * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin. * @param {!int} [costumeResolution=1] - The resolution to use for this bitmap. - * @param {?Array} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the - * skin will be used + * @param {?Array} [rotationCenter] Optional: rotation center of the skin. If not supplied, the center of + * the skin will be used. * @returns {!int} the ID for the new skin. */ createBitmapSkin (bitmapData, costumeResolution, rotationCenter) { @@ -630,9 +630,9 @@ class RenderWebGL extends EventEmitter { * pick drawables that are not visible or have ghost set all the way up. * @param {int} centerX The client x coordinate of the picking location. * @param {int} centerY The client y coordinate of the picking location. - * @param {int} touchWidth The client width of the touch event (optional). - * @param {int} touchHeight The client height of the touch event (optional). - * @param {Array} candidateIDs The Drawable IDs to pick from, otherwise all. + * @param {int} [touchWidth] The client width of the touch event (optional). + * @param {int} [touchHeight] The client height of the touch event (optional). + * @param {Array} [candidateIDs] The Drawable IDs to pick from, otherwise all. * @returns {int} The ID of the topmost Drawable under the picking location, or * RenderConstants.ID_NONE if there is no Drawable at that location. */ From d31e66f02a797ffcc9f61a4b3b9a3fd00cb7d17f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 23 Mar 2018 14:35:52 -0400 Subject: [PATCH 1840/1971] Merge pull request #248 from paulkaplan/fix-bubble-bounds Add "get bounds for bubble" method. --- packages/scratch-render/src/RenderWebGL.js | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 929b7534f4..5640fd9685 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -440,6 +440,39 @@ class RenderWebGL extends EventEmitter { return bounds; } + /** + * Get the precise bounds for a Drawable around the top slice. + * Used for positioning speech bubbles more closely to the sprite. + * @param {int} drawableID ID of Drawable to get bubble bounds for. + * @return {object} Bounds for a tight box around the Drawable top slice. + */ + getBoundsForBubble (drawableID) { + const drawable = this._allDrawables[drawableID]; + // Tell the Drawable about its updated convex hull, if necessary. + if (drawable.needsConvexHullPoints()) { + const points = this._getConvexHullPointsForDrawable(drawableID); + drawable.setConvexHullPoints(points); + } + const bounds = drawable.getBoundsForBubble(); + // In debug mode, draw the bounds. + if (this._debugCanvas) { + const gl = this._gl; + this._debugCanvas.width = gl.canvas.width; + this._debugCanvas.height = gl.canvas.height; + const context = this._debugCanvas.getContext('2d'); + context.drawImage(gl.canvas, 0, 0); + context.strokeStyle = '#FF0000'; + const pr = window.devicePixelRatio; + context.strokeRect( + pr * (bounds.left + (this._nativeSize[0] / 2)), + pr * (-bounds.top + (this._nativeSize[1] / 2)), + pr * (bounds.right - bounds.left), + pr * (-bounds.bottom + bounds.top) + ); + } + return bounds; + } + /** * Get the current skin (costume) size of a Drawable. * @param {int} drawableID The ID of the Drawable to measure. From 4ca7dd111e6ffc5488ef9ee00a4df2fe9a6f42c9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 11 Apr 2018 09:30:23 -0400 Subject: [PATCH 1841/1971] Merge pull request #254 from paulkaplan/skin-size Add method for getting the size of a skin by skin id. --- packages/scratch-render/src/RenderWebGL.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 5640fd9685..80f4fb2ca3 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -478,9 +478,19 @@ class RenderWebGL extends EventEmitter { * @param {int} drawableID The ID of the Drawable to measure. * @return {Array} Skin size, width and height. */ - getSkinSize (drawableID) { + getCurrentSkinSize (drawableID) { const drawable = this._allDrawables[drawableID]; - return drawable.skin.size; + return this.getSkinSize(drawable.skin.id); + } + + /** + * Get the size of a skin by ID. + * @param {int} skinID The ID of the Skin to measure. + * @return {Array} Skin size, width and height. + */ + getSkinSize (skinID) { + const skin = this._allSkins[skinID]; + return skin.size; } /** From 62bcca3927d9eac72b40f773984578f304fd06f1 Mon Sep 17 00:00:00 2001 From: kchadha Date: Mon, 23 Apr 2018 15:55:10 -0400 Subject: [PATCH 1842/1971] Merge pull request #260 from kchadha/get-rotation-center getSkinRotationCenter API and support for new scratch-svg-renderer --- packages/scratch-render/src/RenderWebGL.js | 10 ++++++++++ packages/scratch-render/src/SVGSkin.js | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 80f4fb2ca3..ffa43b520f 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -493,6 +493,16 @@ class RenderWebGL extends EventEmitter { return skin.size; } + /** + * Get the rotation center of a skin by ID. + * @param {int} skinID The ID of the Skin + * @return {Array} The rotationCenterX and rotationCenterY + */ + getSkinRotationCenter (skinID) { + const skin = this._allSkins[skinID]; + return skin.calculateRotationCenter(); + } + /** * Check if a particular Drawable is touching a particular color. * Unlike touching drawable, touching color tests invisible sprites. diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 409ed1fceb..4633a5a557 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -2,7 +2,7 @@ const twgl = require('twgl.js'); const Silhouette = require('./Silhouette'); const Skin = require('./Skin'); -const SvgRenderer = require('scratch-svg-renderer'); +const SvgRenderer = require('scratch-svg-renderer').SVGRenderer; const MAX_TEXTURE_DIMENSION = 2048; From 0824c24cbd94a3990b904a6c68062b82578a79b3 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 25 Apr 2018 14:11:57 -0400 Subject: [PATCH 1843/1971] Merge pull request #264 from fsih/updateBitmap Add updateBitmapSkin to renderer --- packages/scratch-render/src/RenderWebGL.js | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index ffa43b520f..ab3c606ae9 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -295,6 +295,32 @@ class RenderWebGL extends EventEmitter { const newSkin = new SVGSkin(skinId, this); newSkin.setSVG(svgData, rotationCenter); + this._reskin(skinId, newSkin); + } + + /** + * Update an existing bitmap skin, or create a bitmap skin if the previous skin was not bitmap. + * @param {!int} skinId the ID for the skin to change. + * @param {!string} imgData - new bitmap to use. + * @param {?Array} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the + * skin will be used + */ + updateBitmapSkin (skinId, imgData, rotationCenter) { + // Divide rotation center by 2 and set bitmap resolution = 2 because all images coming from paint editor + // are double resolution + const updatedRotationCenter = rotationCenter ? + [Math.floor(rotationCenter[0] / 2), Math.floor(rotationCenter[1] / 2)] : null; + if (this._allSkins[skinId] instanceof BitmapSkin) { + this._allSkins[skinId].setBitmap(imgData, 2, updatedRotationCenter); + return; + } + + const newSkin = new BitmapSkin(skinId, this); + newSkin.setBitmap(imgData, 2, updatedRotationCenter); + this._reskin(skinId, newSkin); + } + + _reskin (skinId, newSkin) { const oldSkin = this._allSkins[skinId]; this._allSkins[skinId] = newSkin; @@ -302,6 +328,8 @@ class RenderWebGL extends EventEmitter { for (const drawable of this._allDrawables) { if (drawable && drawable.skin === oldSkin) { drawable.skin = newSkin; + drawable.setConvexHullDirty(); + drawable.setTransformDirty(); } } oldSkin.dispose(); From 24630ac6712e193358b2ca35c3ff8295ab710787 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 27 Apr 2018 10:26:41 -0400 Subject: [PATCH 1844/1971] Merge pull request #266 from fsih/renderTakeOff2Factor Pass in factor of 2 instead of having magic 2s --- packages/scratch-render/src/RenderWebGL.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index ab3c606ae9..7e4e8455ed 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -302,21 +302,18 @@ class RenderWebGL extends EventEmitter { * Update an existing bitmap skin, or create a bitmap skin if the previous skin was not bitmap. * @param {!int} skinId the ID for the skin to change. * @param {!string} imgData - new bitmap to use. + * @param {!number} bitmapResolution - the resolution scale for a bitmap costume. * @param {?Array} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the * skin will be used */ - updateBitmapSkin (skinId, imgData, rotationCenter) { - // Divide rotation center by 2 and set bitmap resolution = 2 because all images coming from paint editor - // are double resolution - const updatedRotationCenter = rotationCenter ? - [Math.floor(rotationCenter[0] / 2), Math.floor(rotationCenter[1] / 2)] : null; + updateBitmapSkin (skinId, imgData, bitmapResolution, rotationCenter) { if (this._allSkins[skinId] instanceof BitmapSkin) { - this._allSkins[skinId].setBitmap(imgData, 2, updatedRotationCenter); + this._allSkins[skinId].setBitmap(imgData, bitmapResolution, rotationCenter); return; } const newSkin = new BitmapSkin(skinId, this); - newSkin.setBitmap(imgData, 2, updatedRotationCenter); + newSkin.setBitmap(imgData, bitmapResolution, rotationCenter); this._reskin(skinId, newSkin); } From 3d8eca660fdc5101ec914bc4c9ea139fb655f7eb Mon Sep 17 00:00:00 2001 From: Mx Corey Frang Date: Fri, 27 Apr 2018 16:19:25 -0400 Subject: [PATCH 1845/1971] Rewrite isTouchingDrawables on CPU (#263) * avoid gl.readPixels at all costs --- packages/scratch-render/src/RenderWebGL.js | 169 +++++++++------------ packages/scratch-render/src/SVGSkin.js | 11 -- 2 files changed, 75 insertions(+), 105 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 7e4e8455ed..7600d289c4 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -13,6 +13,9 @@ const SVGSkin = require('./SVGSkin'); const SVGTextBubble = require('./util/svg-text-bubble'); const EffectTransform = require('./EffectTransform'); +const __isTouchingDrawablesPoint = twgl.v3.create(); +const __candidatesBounds = new Rectangle(); + /** * @callback RenderWebGL#idFilterFunc * @param {int} drawableID The ID to filter. @@ -540,15 +543,15 @@ class RenderWebGL extends EventEmitter { const gl = this._gl; twgl.bindFramebufferInfo(gl, this._queryBufferInfo); - const bounds = this._touchingBounds(drawableID); - if (!bounds) { - return false; - } - const candidateIDs = this._filterCandidatesTouching(drawableID, this._drawList, bounds); - if (!candidateIDs) { + const candidates = this._candidatesTouching(drawableID, this._drawList); + if (candidates.length === 0) { return false; } + const bounds = this._candidatesBounds(candidates); + + const candidateIDs = candidates.map(({id}) => id); + // Limit size of viewport to the bounds around the target Drawable, // and create the projection matrix for the draw. gl.viewport(0, 0, bounds.width, bounds.height); @@ -631,72 +634,36 @@ class RenderWebGL extends EventEmitter { /** * Check if a particular Drawable is touching any in a set of Drawables. * @param {int} drawableID The ID of the Drawable to check. - * @param {Array} candidateIDs The Drawable IDs to check, otherwise all. - * @returns {boolean} True iff the Drawable is touching one of candidateIDs. + * @param {?Array} candidateIDs The Drawable IDs to check, otherwise all drawables in the renderer + * @returns {boolean} True if the Drawable is touching one of candidateIDs. */ - isTouchingDrawables (drawableID, candidateIDs) { - candidateIDs = candidateIDs || this._drawList; - - const gl = this._gl; + isTouchingDrawables (drawableID, candidateIDs = this._drawList) { - twgl.bindFramebufferInfo(gl, this._queryBufferInfo); - - const bounds = this._touchingBounds(drawableID); - if (!bounds) { + const candidates = this._candidatesTouching(drawableID, candidateIDs); + if (candidates.length === 0) { return false; } - candidateIDs = this._filterCandidatesTouching(drawableID, candidateIDs, bounds); - if (!candidateIDs) { - return false; - } - - // Limit size of viewport to the bounds around the target Drawable, - // and create the projection matrix for the draw. - gl.viewport(0, 0, bounds.width, bounds.height); - const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); - - const noneColor = Drawable.color4fFromID(RenderConstants.ID_NONE); - gl.clearColor.apply(gl, noneColor); - gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); - - try { - gl.enable(gl.STENCIL_TEST); - gl.stencilFunc(gl.ALWAYS, 1, 1); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); - gl.colorMask(false, false, false, false); - this._drawThese([drawableID], ShaderManager.DRAW_MODE.silhouette, projection); - - gl.stencilFunc(gl.EQUAL, 1, 1); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - gl.colorMask(true, true, true, true); - - this._drawThese(candidateIDs, ShaderManager.DRAW_MODE.silhouette, projection, - {idFilterFunc: testID => testID !== drawableID} - ); - } finally { - gl.colorMask(true, true, true, true); - gl.disable(gl.STENCIL_TEST); - } - const pixels = new Uint8Array(Math.floor(bounds.width * bounds.height * 4)); - gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); - - if (this._debugCanvas) { - this._debugCanvas.width = bounds.width; - this._debugCanvas.height = bounds.height; - const context = this._debugCanvas.getContext('2d'); - const imageData = context.getImageData(0, 0, bounds.width, bounds.height); - imageData.data.set(pixels); - context.putImageData(imageData, 0, 0); - } + // Get the union of all the candidates intersections. + const bounds = this._candidatesBounds(candidates); - for (let pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { - const pixelID = Drawable.color3bToID( - pixels[pixelBase], - pixels[pixelBase + 1], - pixels[pixelBase + 2]); - if (pixelID > RenderConstants.ID_NONE) { - return true; + const drawable = this._allDrawables[drawableID]; + const point = __isTouchingDrawablesPoint; + + // This is an EXTREMELY brute force collision detector, but it is + // still faster than asking the GPU to give us the pixels. + for (let x = bounds.left; x <= bounds.right; x++) { + // Scratch Space - +y is top + point[0] = x; + for (let y = bounds.bottom; y <= bounds.top; y++) { + point[1] = y; + if (drawable.isTouching(point)) { + for (let index = 0; index < candidates.length; index++) { + if (candidates[index].drawable.isTouching(point)) { + return true; + } + } + } } } @@ -965,27 +932,47 @@ class RenderWebGL extends EventEmitter { * could possibly intersect the given bounds. * @param {int} drawableID - ID for drawable of query. * @param {Array} candidateIDs - Candidates for touching query. - * @param {Rectangle} bounds - Bounds to limit candidates to. - * @return {?Array} Filtered candidateIDs, or null if none. + * @return {?Array< {id, drawable, intersection} >} Filtered candidates with useful data. */ - _filterCandidatesTouching (drawableID, candidateIDs, bounds) { - // Filter candidates by rough bounding box intersection. - // Do this before _drawThese, so we can prevent any GL operations - // and readback by returning early. - candidateIDs = candidateIDs.filter(testID => { - if (testID === drawableID) return false; - // Only draw items which could possibly overlap target Drawable. - const candidate = this._allDrawables[testID]; - const candidateBounds = candidate.getFastBounds(); - return bounds.intersects(candidateBounds); - }); - if (candidateIDs.length === 0) { - // No possible intersections based on bounding boxes. - return null; + _candidatesTouching (drawableID, candidateIDs) { + const bounds = this._touchingBounds(drawableID); + if (!bounds) { + return []; } - return candidateIDs; + return candidateIDs.reduce((result, id) => { + if (id !== drawableID) { + const drawable = this._allDrawables[id]; + const candidateBounds = drawable.getFastBounds(); + + if (bounds.intersects(candidateBounds)) { + result.push({ + id, + drawable, + intersection: Rectangle.intersect(bounds, candidateBounds) + }); + } + } + return result; + }, []); } + /** + * Helper to get the union bounds from a set of candidates returned from the above method + * @private + * @param {Array} candidates info from _candidatesTouching + * @return {Rectangle} the outer bounding box union + */ + _candidatesBounds (candidates) { + return candidates.reduce((memo, {intersection}) => { + if (!memo) { + return intersection; + } + // store the union of the two rectangles in our static rectangle instance + return Rectangle.union(memo, intersection, __candidatesBounds); + }, null); + } + + /** * Update the position, direction, scale, or effect properties of this Drawable. * @param {int} drawableID The ID of the Drawable to update. @@ -1210,12 +1197,6 @@ class RenderWebGL extends EventEmitter { * @private */ _drawThese (drawables, drawMode, projection, opts = {}) { - const near = function (a, b, relativeTolerance = 0.01) { - const absA = Math.abs(a); - const absB = Math.abs(b); - const error = Math.abs(a - b) / Math.max(absA, absB); - return error < relativeTolerance; - }; const gl = this._gl; let currentShader = null; @@ -1264,9 +1245,9 @@ class RenderWebGL extends EventEmitter { } if (uniforms.u_skin) { - const useNearest = - (drawable._direction % 90 === 0) && (near(drawableScale, 100) || drawable.skin.isRaster); - twgl.setTextureParameters(gl, uniforms.u_skin, {minMag: useNearest ? gl.NEAREST : gl.LINEAR}); + twgl.setTextureParameters( + gl, uniforms.u_skin, {minMag: drawable.useNearest ? gl.NEAREST : gl.LINEAR} + ); } twgl.setUniforms(currentShader, uniforms); @@ -1332,7 +1313,7 @@ class RenderWebGL extends EventEmitter { for (; x < width; x++) { _pixelPos[0] = x / width; EffectTransform.transformPoint(drawable, _pixelPos, _effectPos); - if (drawable.skin.isTouching(_effectPos)) { + if (drawable.skin.isTouchingLinear(_effectPos)) { Q = [x, y]; break; } @@ -1362,7 +1343,7 @@ class RenderWebGL extends EventEmitter { for (x = width - 1; x >= 0; x--) { _pixelPos[0] = x / width; EffectTransform.transformPoint(drawable, _pixelPos, _effectPos); - if (drawable.skin.isTouching(_effectPos)) { + if (drawable.skin.isTouchingLinear(_effectPos)) { Q = [x, y]; break; } diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 4633a5a557..b99998c542 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -1,6 +1,5 @@ const twgl = require('twgl.js'); -const Silhouette = require('./Silhouette'); const Skin = require('./Skin'); const SvgRenderer = require('scratch-svg-renderer').SVGRenderer; @@ -31,8 +30,6 @@ class SVGSkin extends Skin { /** @type {Number} */ this._maxTextureScale = 0; - - this._silhouette = new Silhouette(); } /** @@ -129,14 +126,6 @@ class SVGSkin extends Skin { }); } - /** - * Does this point touch an opaque or translucent point on this skin? - * @param {twgl.v3} vec A texture coordinate. - * @return {boolean} Did it touch? - */ - isTouching (vec) { - return this._silhouette.isTouching(vec); - } } module.exports = SVGSkin; From 8deff94c4f11859a86618e0f2f2445b0771ae26f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 2 May 2018 07:43:07 -0400 Subject: [PATCH 1846/1971] Merge pull request #268 from paulkaplan/fix-blurry-fullscreen Scale the drawable scale by the ratio of pixel size to native size. --- packages/scratch-render/src/RenderWebGL.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 7600d289c4..c3b2a09983 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1215,7 +1215,11 @@ class RenderWebGL extends EventEmitter { // the ignoreVisibility flag is used (e.g. for stamping or touchingColor). if (!drawable.getVisible() && !opts.ignoreVisibility) continue; - const drawableScale = drawable.scale; + // Combine drawable scale with the native vs. backing pixel ratio + const drawableScale = [ + drawable.scale[0] * this._gl.canvas.width / this._nativeSize[0], + drawable.scale[1] * this._gl.canvas.height / this._nativeSize[1] + ]; // If the skin or texture isn't ready yet, skip it. if (!drawable.skin || !drawable.skin.getTexture(drawableScale)) continue; From 8ad0cf55d204f778ff4e7c1e82f9d5382277ed94 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 4 May 2018 10:36:22 -0400 Subject: [PATCH 1847/1971] Merge pull request #269 from fsih/updateBitmapSkinJsdoc Fix jsdoc --- packages/scratch-render/src/RenderWebGL.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index c3b2a09983..c32c4d7ff1 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -304,7 +304,7 @@ class RenderWebGL extends EventEmitter { /** * Update an existing bitmap skin, or create a bitmap skin if the previous skin was not bitmap. * @param {!int} skinId the ID for the skin to change. - * @param {!string} imgData - new bitmap to use. + * @param {!ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} imgData - new contents for this skin. * @param {!number} bitmapResolution - the resolution scale for a bitmap costume. * @param {?Array} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the * skin will be used From 87dfab61750c4999b928fa473f4f245c3ef24de6 Mon Sep 17 00:00:00 2001 From: kchadha Date: Fri, 25 May 2018 09:31:37 -0400 Subject: [PATCH 1848/1971] Merge pull request #285 from kchadha/layer-ordering Layer ordering --- packages/scratch-render/src/RenderWebGL.js | 143 ++++++++++++++++++--- 1 file changed, 128 insertions(+), 15 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index c32c4d7ff1..221a198dc4 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -12,6 +12,7 @@ const ShaderManager = require('./ShaderManager'); const SVGSkin = require('./SVGSkin'); const SVGTextBubble = require('./util/svg-text-bubble'); const EffectTransform = require('./EffectTransform'); +const log = require('./util/log'); const __isTouchingDrawablesPoint = twgl.v3.create(); const __candidatesBounds = new Rectangle(); @@ -120,6 +121,22 @@ class RenderWebGL extends EventEmitter { /** @type {Array} */ this._drawList = []; + // A list of layer group names in the order they should appear + // from furthest back to furthest in front. + /** @type {Array} */ + this._groupOrdering = []; + + /** + * @typedef LayerGroup + * @property {int} groupIndex The relative position of this layer group in the group ordering + * @property {int} drawListOffset The absolute position of this layer group in the draw list + * This number gets updated as drawables get added to or deleted from the draw list. + */ + + // Map of group name to layer group + /** @type {Object.} */ + this._layerGroups = {}; + /** @type {int} */ this._nextDrawableId = RenderConstants.ID_NONE + 1; @@ -360,31 +377,100 @@ class RenderWebGL extends EventEmitter { /** * Create a new Drawable and add it to the scene. + * @param {string} group Layer group to add the drawable to * @returns {int} The ID of the new Drawable. */ - createDrawable () { + createDrawable (group) { + if (!group || !this._layerGroups.hasOwnProperty(group)) { + log.warn('Cannot create a drawable without a known layer group'); + return; + } const drawableID = this._nextDrawableId++; const drawable = new Drawable(drawableID); this._allDrawables[drawableID] = drawable; - this._drawList.push(drawableID); + this._addToDrawList(drawableID, group); drawable.skin = null; return drawableID; } + /** + * Set the layer group ordering for the renderer. + * @param {Array} groupOrdering The ordered array of layer group + * names + */ + setLayerGroupOrdering (groupOrdering) { + this._groupOrdering = groupOrdering; + for (let i = 0; i < this._groupOrdering.length; i++) { + this._layerGroups[this._groupOrdering[i]] = { + groupIndex: i, + drawListOffset: 0 + }; + } + } + + _addToDrawList (drawableID, group) { + const currentLayerGroup = this._layerGroups[group]; + const currentGroupOrderingIndex = currentLayerGroup.groupIndex; + + const drawListOffset = this._endIndexForKnownLayerGroup(currentLayerGroup); + this._drawList.splice(drawListOffset, 0, drawableID); + + this._updateOffsets('add', currentGroupOrderingIndex); + } + + _updateOffsets (updateType, currentGroupOrderingIndex) { + for (let i = currentGroupOrderingIndex + 1; i < this._groupOrdering.length; i++) { + const laterGroupName = this._groupOrdering[i]; + if (updateType === 'add') { + this._layerGroups[laterGroupName].drawListOffset++; + } else if (updateType === 'delete'){ + this._layerGroups[laterGroupName].drawListOffset--; + } + } + } + + // Given a layer group, return the index where it ends (non-inclusive), + // e.g. the returned index does not have a drawable from this layer group in it) + _endIndexForKnownLayerGroup (layerGroup) { + const groupIndex = layerGroup.groupIndex; + if (groupIndex === this._groupOrdering.length - 1) { + return this._drawList.length; + } + return this._layerGroups[this._groupOrdering[groupIndex + 1]].drawListOffset; + } + /** * Destroy a Drawable, removing it from the scene. * @param {int} drawableID The ID of the Drawable to remove. + * @param {string} group Group name that the drawable belongs to */ - destroyDrawable (drawableID) { + destroyDrawable (drawableID, group) { + if (!group || !this._layerGroups.hasOwnProperty(group)) { + log.warn('Cannot destroy drawable without known layer group.'); + return; + } const drawable = this._allDrawables[drawableID]; drawable.dispose(); delete this._allDrawables[drawableID]; - let index; - while ((index = this._drawList.indexOf(drawableID)) >= 0) { + const currentLayerGroup = this._layerGroups[group]; + const endIndex = this._endIndexForKnownLayerGroup(currentLayerGroup); + + let index = currentLayerGroup.drawListOffset; + while (index < endIndex) { + if (this._drawList[index] === drawableID) { + break; + } + index++; + } + if (index < endIndex) { this._drawList.splice(index, 1); + this._updateOffsets('delete', currentLayerGroup.groupIndex); + } else { + log.warn('Could not destroy drawable that could not be found in layer group.'); + return; } } @@ -397,28 +483,55 @@ class RenderWebGL extends EventEmitter { * "go to front": setDrawableOrder(id, Infinity); * @param {int} drawableID ID of Drawable to reorder. * @param {number} order New absolute order or relative order adjusment. + * @param {string=} group Name of layer group drawable belongs to. + * Reordering will not take place if drawable cannot be found within the bounds + * of the layer group. * @param {boolean=} optIsRelative If set, `order` refers to a relative change. * @param {number=} optMin If set, order constrained to be at least `optMin`. * @return {?number} New order if changed, or null. */ - setDrawableOrder (drawableID, order, optIsRelative, optMin) { - const oldIndex = this._drawList.indexOf(drawableID); - if (oldIndex >= 0) { + setDrawableOrder (drawableID, order, group, optIsRelative, optMin) { + if (!group || !this._layerGroups.hasOwnProperty(group)) { + log.warn('Cannot set the order of a drawable without a known layer group.'); + return; + } + + const currentLayerGroup = this._layerGroups[group]; + const startIndex = currentLayerGroup.drawListOffset; + const endIndex = this._endIndexForKnownLayerGroup(currentLayerGroup); + + let oldIndex = startIndex; + while (oldIndex < endIndex) { + if (this._drawList[oldIndex] === drawableID) { + break; + } + oldIndex++; + } + + if (oldIndex < endIndex) { // Remove drawable from the list. - const drawable = this._drawList.splice(oldIndex, 1)[0]; + if (order === 0) { + return oldIndex; + } + + const _ = this._drawList.splice(oldIndex, 1)[0]; // Determine new index. let newIndex = order; if (optIsRelative) { newIndex += oldIndex; } - if (optMin) { - newIndex = Math.max(newIndex, optMin); - } - newIndex = Math.max(newIndex, 0); + + const possibleMin = (optMin || 0) + startIndex; + const min = (possibleMin >= startIndex && possibleMin < endIndex) ? possibleMin : startIndex; + newIndex = Math.max(newIndex, min); + + newIndex = Math.min(newIndex, endIndex); + // Insert at new index. - this._drawList.splice(newIndex, 0, drawable); - return this._drawList.indexOf(drawable); + this._drawList.splice(newIndex, 0, drawableID); + return newIndex; } + return null; } From c26b18a67b158106a03372a072ffb34e6c0a3c76 Mon Sep 17 00:00:00 2001 From: kchadha Date: Tue, 24 Jul 2018 11:19:17 -0400 Subject: [PATCH 1849/1971] Merge pull request #320 from kchadha/get-drawable-order Add API for getting the drawable order of a given drawable. --- packages/scratch-render/src/RenderWebGL.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 221a198dc4..77a2b4e0ab 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -474,6 +474,16 @@ class RenderWebGL extends EventEmitter { } } + /** + * Returns the position of the given drawableID in the draw list. This is + * the absolute position irrespective of layer group. + * @param {number} drawableID The drawable ID to find. + * @return {number} The postion of the given drawable ID. + */ + getDrawableOrder (drawableID) { + return this._drawList.indexOf(drawableID); + } + /** * Set a drawable's order in the drawable list (effectively, z/layer). * Can be used to move drawables to absolute positions in the list, From 1ff7bc7f48f35ea995c3735d3444ac045ae55742 Mon Sep 17 00:00:00 2001 From: Mx Corey Frang Date: Tue, 7 Aug 2018 10:56:28 -0400 Subject: [PATCH 1850/1971] Optimizing isTouching while creating a drawableTouches for sensing mouse pointer (#325) * Allow 'isTouching' and 'pick' to still work on invisible drawables. * Always ignore visibility for isTouching on drawable * Filter invisble drawbles in isTouchingDrawable per rules of collision * polish up some docs/get logic :+1: * leftover line from deleted comment * revert to ghosted pick behavior * Add clientSpaceToScratchBounds method * fix lint * add some pick tests --- packages/scratch-render/src/RenderWebGL.js | 137 ++++++++++++++------- 1 file changed, 94 insertions(+), 43 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 77a2b4e0ab..8b1133ba65 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -431,6 +431,10 @@ class RenderWebGL extends EventEmitter { } } + get _visibleDrawList () { + return this._drawList.filter(id => this._allDrawables[id]._visible); + } + // Given a layer group, return the index where it ends (non-inclusive), // e.g. the returned index does not have a drawable from this layer group in it) _endIndexForKnownLayerGroup (layerGroup) { @@ -656,7 +660,7 @@ class RenderWebGL extends EventEmitter { /** * Check if a particular Drawable is touching a particular color. - * Unlike touching drawable, touching color tests invisible sprites. + * Unlike touching drawable, if the "tester" is invisble, we will still test. * @param {int} drawableID The ID of the Drawable to check. * @param {Array} color3b Test if the Drawable is touching this color. * @param {Array} [mask3b] Optionally mask the check to this part of Drawable. @@ -666,7 +670,7 @@ class RenderWebGL extends EventEmitter { const gl = this._gl; twgl.bindFramebufferInfo(gl, this._queryBufferInfo); - const candidates = this._candidatesTouching(drawableID, this._drawList); + const candidates = this._candidatesTouching(drawableID, this._visibleDrawList); if (candidates.length === 0) { return false; } @@ -757,13 +761,15 @@ class RenderWebGL extends EventEmitter { /** * Check if a particular Drawable is touching any in a set of Drawables. * @param {int} drawableID The ID of the Drawable to check. - * @param {?Array} candidateIDs The Drawable IDs to check, otherwise all drawables in the renderer + * @param {?Array} candidateIDs The Drawable IDs to check, otherwise all visible drawables in the renderer * @returns {boolean} True if the Drawable is touching one of candidateIDs. */ isTouchingDrawables (drawableID, candidateIDs = this._drawList) { - - const candidates = this._candidatesTouching(drawableID, candidateIDs); - if (candidates.length === 0) { + const candidates = this._candidatesTouching(drawableID, + // even if passed an invisible drawable, we will NEVER touch it! + candidateIDs.filter(id => this._allDrawables[id]._visible)); + // if we are invisble we don't touch anything. + if (candidates.length === 0 || !this._allDrawables[drawableID]._visible) { return false; } @@ -794,57 +800,102 @@ class RenderWebGL extends EventEmitter { } /** - * Detect which sprite, if any, is at the given location. This function will not - * pick drawables that are not visible or have ghost set all the way up. + * Convert a client based x/y position on the canvas to a Scratch 3 world space + * Rectangle. This creates recangles with a radius to cover selecting multiple + * scratch pixels with touch / small render areas. + * * @param {int} centerX The client x coordinate of the picking location. * @param {int} centerY The client y coordinate of the picking location. - * @param {int} [touchWidth] The client width of the touch event (optional). - * @param {int} [touchHeight] The client height of the touch event (optional). - * @param {Array} [candidateIDs] The Drawable IDs to pick from, otherwise all. - * @returns {int} The ID of the topmost Drawable under the picking location, or - * RenderConstants.ID_NONE if there is no Drawable at that location. + * @param {int} [width] The client width of the touch event (optional). + * @param {int} [height] The client width of the touch event (optional). + * @returns {Rectangle} Scratch world space rectangle, iterate bottom <= top, + * left <= right. */ - pick (centerX, centerY, touchWidth, touchHeight, candidateIDs) { + clientSpaceToScratchBounds (centerX, centerY, width = 1, height = 1) { const gl = this._gl; - touchWidth = touchWidth || 1; - touchHeight = touchHeight || 1; - candidateIDs = (candidateIDs || this._drawList).filter(id => { - const drawable = this._allDrawables[id]; - const uniforms = drawable.getUniforms(); - return drawable.getVisible() && uniforms.u_ghost !== 0; - }); + const clientToScratchX = this._nativeSize[0] / gl.canvas.clientWidth; + const clientToScratchY = this._nativeSize[1] / gl.canvas.clientHeight; - const clientToGLX = gl.canvas.width / gl.canvas.clientWidth; - const clientToGLY = gl.canvas.height / gl.canvas.clientHeight; + width *= clientToScratchX; + height *= clientToScratchY; - centerX *= clientToGLX; - centerY *= clientToGLY; - touchWidth *= clientToGLX; - touchHeight *= clientToGLY; + width = Math.max(1, Math.min(Math.round(width), MAX_TOUCH_SIZE[0])); + height = Math.max(1, Math.min(Math.round(height), MAX_TOUCH_SIZE[1])); + const x = (centerX * clientToScratchX) - ((width - 1) / 2); + // + because scratch y is inverted + const y = (centerY * clientToScratchY) + ((height - 1) / 2); - touchWidth = Math.max(1, Math.min(touchWidth, MAX_TOUCH_SIZE[0])); - touchHeight = Math.max(1, Math.min(touchHeight, MAX_TOUCH_SIZE[1])); + const xOfs = (width % 2) ? 0 : -0.5; + // y is offset +0.5 + const yOfs = (height % 2) ? 0 : -0.5; - const pixelLeft = Math.floor(centerX - Math.floor(touchWidth / 2) + 0.5); - const pixelTop = Math.floor(centerY - Math.floor(touchHeight / 2) + 0.5); + const bounds = new Rectangle(); + bounds.initFromBounds(Math.floor(this._xLeft + x + xOfs), Math.floor(this._xLeft + x + xOfs + width - 1), + Math.ceil(this._yTop - y + yOfs), Math.ceil(this._yTop - y + yOfs + height - 1)); + return bounds; + } - const widthPerPixel = (this._xRight - this._xLeft) / this._gl.canvas.width; - const heightPerPixel = (this._yBottom - this._yTop) / this._gl.canvas.height; + /** + * Determine if the drawable is touching a client based x/y. Helper method for sensing + * touching mouse-pointer. Ignores visibility. + * + * @param {int} drawableID The ID of the drawable to check. + * @param {int} centerX The client x coordinate of the picking location. + * @param {int} centerY The client y coordinate of the picking location. + * @param {int} [touchWidth] The client width of the touch event (optional). + * @param {int} [touchHeight] The client height of the touch event (optional). + * @returns {boolean} If the drawable has any pixels that would draw in the touch area + */ + drawableTouching (drawableID, centerX, centerY, touchWidth, touchHeight) { + const drawable = this._allDrawables[drawableID]; + if (!drawable) { + return false; + } + const bounds = this.clientSpaceToScratchBounds(centerX, centerY, touchWidth, touchHeight); + const worldPos = twgl.v3.create(); - const pickLeft = this._xLeft + (pixelLeft * widthPerPixel); - const pickTop = this._yTop + (pixelTop * heightPerPixel); + for (worldPos[1] = bounds.bottom; worldPos[1] <= bounds.top; worldPos[1]++) { + for (worldPos[0] = bounds.left; worldPos[0] <= bounds.right; worldPos[0]++) { + if (drawable.isTouching(worldPos)) { + return true; + } + } + } + return false; + } + /** + * Detect which sprite, if any, is at the given location. + * This function will pick all drawables that are visible, unless specific + * candidate drawable IDs are provided. Used for determining what is clicked + * or dragged. Will not select hidden / ghosted sprites. + * + * @param {int} centerX The client x coordinate of the picking location. + * @param {int} centerY The client y coordinate of the picking location. + * @param {int} [touchWidth] The client width of the touch event (optional). + * @param {int} [touchHeight] The client height of the touch event (optional). + * @param {Array} [candidateIDs] The Drawable IDs to pick from, otherwise all visible drawables. + * @returns {int} The ID of the topmost Drawable under the picking location, or + * RenderConstants.ID_NONE if there is no Drawable at that location. + */ + pick (centerX, centerY, touchWidth, touchHeight, candidateIDs) { + candidateIDs = (candidateIDs || this._drawList).filter(id => { + const drawable = this._allDrawables[id]; + // default pick list ignores visible and ghosted sprites. + return drawable.getVisible() && drawable.getUniforms().u_ghost !== 0; + }); + if (candidateIDs.length === 0) { + return false; + } + const bounds = this.clientSpaceToScratchBounds(centerX, centerY, touchWidth, touchHeight); const hits = []; const worldPos = twgl.v3.create(0, 0, 0); - worldPos[2] = 0; - - // Iterate over the canvas pixels and check if any candidate can be + // Iterate over the scratch pixels and check if any candidate can be // touched at that point. - for (let x = 0; x < touchWidth; x++) { - worldPos[0] = x + pickLeft; - for (let y = 0; y < touchHeight; y++) { - worldPos[1] = y + pickTop; + for (worldPos[1] = bounds.bottom; worldPos[1] <= bounds.top; worldPos[1]++) { + for (worldPos[0] = bounds.left; worldPos[0] <= bounds.right; worldPos[0]++) { + // Check candidates in the reverse order they would have been // drawn. This will determine what candiate's silhouette pixel // would have been drawn at the point. @@ -869,7 +920,7 @@ class RenderWebGL extends EventEmitter { } } - return hit | 0; + return Number(hit); } /** From df9bae9bc4d9a7c437582560d5af18a6aa3c1bd7 Mon Sep 17 00:00:00 2001 From: Mx Corey Frang Date: Wed, 8 Aug 2018 14:20:35 -0400 Subject: [PATCH 1851/1971] Touching color implementation (#312) * Touching color draft implementation * New constant for CPU/GPU render split determined, removing query param * Small fix for pick tests --- packages/scratch-render/src/RenderWebGL.js | 193 ++++++++++++++++----- 1 file changed, 147 insertions(+), 46 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 8b1133ba65..dbd55985b3 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -16,6 +16,12 @@ const log = require('./util/log'); const __isTouchingDrawablesPoint = twgl.v3.create(); const __candidatesBounds = new Rectangle(); +const __touchingColor = new Uint8ClampedArray(4); +const __blendColor = new Uint8ClampedArray(4); + +// More pixels than this and we give up to the GPU and take the cost of readPixels +// Width * Height * Number of drawables at location +const __cpuTouchingColorPixelCount = 4e4; /** * @callback RenderWebGL#idFilterFunc @@ -32,26 +38,40 @@ const __candidatesBounds = new Rectangle(); const MAX_TOUCH_SIZE = [3, 3]; /** - * "touching {color}?" or "{color} touching {color}?" tests will be true if the - * target is touching a color whose components are each within this tolerance of - * the corresponding component of the query color. - * between 0 (exact matches only) and 255 (match anything). - * @type {object.} - * @memberof RenderWebGL + * Passed to the uniforms for mask in touching color */ -const TOLERANCE_TOUCHING_COLOR = { - R: 7, - G: 7, - B: 15, - Mask: 2 -}; +const MASK_TOUCHING_COLOR_TOLERANCE = 2; /** - * Constant used for masking when detecting the color white - * @type {Array} - * @memberof RenderWebGL + * Determines if the mask color is "close enough" (only test the 6 top bits for + * each color). These bit masks are what scratch 2 used to use, so we do the same. + * @param {Uint8Array} a A color3b or color4b value. + * @param {Uint8Array} b A color3b or color4b value. + * @returns {boolean} If the colors match within the parameters. + */ +const maskMatches = (a, b) => ( + // has some non-alpha component to test against + a[3] > 0 && + (a[0] & 0b11111100) === (b[0] & 0b11111100) && + (a[1] & 0b11111100) === (b[1] & 0b11111100) && + (a[2] & 0b11111100) === (b[2] & 0b11111100) +); + +/** + * Determines if the given color is "close enough" (only test the 5 top bits for + * red and green, 4 bits for blue). These bit masks are what scratch 2 used to use, + * so we do the same. + * @param {Uint8Array} a A color3b or color4b value. + * @param {Uint8Array} b A color3b or color4b value / or a larger array when used with offsets + * @param {number} offset An offset into the `b` array, which lets you use a larger array to test + * multiple values at the same time. + * @returns {boolean} If the colors match within the parameters. */ -const COLOR_BLACK = [0, 0, 0, 1]; +const colorMatches = (a, b, offset) => ( + (a[0] & 0b11111000) === (b[offset + 0] & 0b11111000) && + (a[1] & 0b11111000) === (b[offset + 1] & 0b11111000) && + (a[2] & 0b11110000) === (b[offset + 2] & 0b11110000) +); /** * Sprite Fencing - The number of pixels a sprite is required to leave remaining @@ -667,9 +687,6 @@ class RenderWebGL extends EventEmitter { * @returns {boolean} True iff the Drawable is touching the color. */ isTouchingColor (drawableID, color3b, mask3b) { - const gl = this._gl; - twgl.bindFramebufferInfo(gl, this._queryBufferInfo); - const candidates = this._candidatesTouching(drawableID, this._visibleDrawList); if (candidates.length === 0) { return false; @@ -677,7 +694,43 @@ class RenderWebGL extends EventEmitter { const bounds = this._candidatesBounds(candidates); - const candidateIDs = candidates.map(({id}) => id); + // if there are just too many pixels to CPU render efficently, we + // need to let readPixels happen + if (bounds.width * bounds.height * (candidates.length + 1) >= __cpuTouchingColorPixelCount) { + this._isTouchingColorGpuStart(drawableID, candidates.map(({id}) => id).reverse(), bounds, color3b, mask3b); + } + + const drawable = this._allDrawables[drawableID]; + const point = __isTouchingDrawablesPoint; + const color = __touchingColor; + const hasMask = Boolean(mask3b); + + for (let y = bounds.bottom; y <= bounds.top; y++) { + if (bounds.width * (y - bounds.bottom) * (candidates.length + 1) >= __cpuTouchingColorPixelCount) { + return this._isTouchingColorGpuFin(bounds, color3b, y - bounds.bottom); + } + // Scratch Space - +y is top + for (let x = bounds.left; x <= bounds.right; x++) { + point[1] = y; + point[0] = x; + if ( + // if we use a mask, check our sample color + (hasMask ? + maskMatches(Drawable.sampleColor4b(point, drawable, color), mask3b) : + drawable.isTouching(point)) && + // and the target color is drawn at this pixel + colorMatches(RenderWebGL.sampleColor3b(point, candidates, color), color3b, 0) + ) { + return true; + } + } + } + return false; + } + + _isTouchingColorGpuStart (drawableID, candidateIDs, bounds, color3b, mask3b) { + const gl = this._gl; + twgl.bindFramebufferInfo(gl, this._queryBufferInfo); // Limit size of viewport to the bounds around the target Drawable, // and create the projection matrix for the draw. @@ -689,7 +742,7 @@ class RenderWebGL extends EventEmitter { // When using masking such that the background fill color will showing through, ensure we don't // fill using the same color that we are trying to detect! if (color3b[0] > 196 && color3b[1] > 196 && color3b[2] > 196) { - fillBackgroundColor = COLOR_BLACK; + fillBackgroundColor = [0, 0, 0, 255]; } gl.clearColor.apply(gl, fillBackgroundColor); @@ -699,7 +752,7 @@ class RenderWebGL extends EventEmitter { if (mask3b) { extraUniforms = { u_colorMask: [mask3b[0] / 255, mask3b[1] / 255, mask3b[2] / 255], - u_colorMaskTolerance: TOLERANCE_TOUCHING_COLOR.Mask / 255 + u_colorMaskTolerance: MASK_TOUCHING_COLOR_TOLERANCE / 255 }; } @@ -730,27 +783,24 @@ class RenderWebGL extends EventEmitter { gl.colorMask(true, true, true, true); gl.disable(gl.STENCIL_TEST); } + } - const pixels = new Uint8Array(Math.floor(bounds.width * bounds.height * 4)); - gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); + _isTouchingColorGpuFin (bounds, color3b, stop) { + const gl = this._gl; + const pixels = new Uint8Array(Math.floor(bounds.width * (bounds.height - stop) * 4)); + gl.readPixels(0, 0, bounds.width, (bounds.height - stop), gl.RGBA, gl.UNSIGNED_BYTE, pixels); if (this._debugCanvas) { this._debugCanvas.width = bounds.width; this._debugCanvas.height = bounds.height; const context = this._debugCanvas.getContext('2d'); - const imageData = context.getImageData(0, 0, bounds.width, bounds.height); + const imageData = context.getImageData(0, 0, bounds.width, bounds.height - stop); imageData.data.set(pixels); context.putImageData(imageData, 0, 0); } for (let pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { - const pixelDistanceR = Math.abs(pixels[pixelBase] - color3b[0]); - const pixelDistanceG = Math.abs(pixels[pixelBase + 1] - color3b[1]); - const pixelDistanceB = Math.abs(pixels[pixelBase + 2] - color3b[2]); - - if (pixelDistanceR <= TOLERANCE_TOUCHING_COLOR.R && - pixelDistanceG <= TOLERANCE_TOUCHING_COLOR.G && - pixelDistanceB <= TOLERANCE_TOUCHING_COLOR.B) { + if (colorMatches(color3b, pixels, pixelBase)) { return true; } } @@ -883,7 +933,12 @@ class RenderWebGL extends EventEmitter { candidateIDs = (candidateIDs || this._drawList).filter(id => { const drawable = this._allDrawables[id]; // default pick list ignores visible and ghosted sprites. - return drawable.getVisible() && drawable.getUniforms().u_ghost !== 0; + if (drawable.getVisible() && drawable.getUniforms().u_ghost !== 0) { + drawable.updateMatrix(); + drawable.skin.updateSilhouette(); + return true; + } + return false; }); if (candidateIDs.length === 0) { return false; @@ -1085,6 +1140,8 @@ class RenderWebGL extends EventEmitter { /** @todo remove this once URL-based skin setting is removed. */ if (!drawable.skin || !drawable.skin.getTexture([100, 100])) return null; + drawable.updateMatrix(); + drawable.skin.updateSilhouette(); const bounds = drawable.getFastBounds(); // Limit queries to the stage size. @@ -1110,24 +1167,31 @@ class RenderWebGL extends EventEmitter { */ _candidatesTouching (drawableID, candidateIDs) { const bounds = this._touchingBounds(drawableID); - if (!bounds) { - return []; + const result = []; + if (bounds === null) { + return result; } - return candidateIDs.reduce((result, id) => { + // iterate through the drawables list BACKWARDS - we want the top most item to be the first we check + for (let index = candidateIDs.length - 1; index >= 0; index--) { + const id = candidateIDs[index]; if (id !== drawableID) { const drawable = this._allDrawables[id]; - const candidateBounds = drawable.getFastBounds(); - - if (bounds.intersects(candidateBounds)) { - result.push({ - id, - drawable, - intersection: Rectangle.intersect(bounds, candidateBounds) - }); + if (drawable.skin && drawable._visible) { + // Update the CPU position data + drawable.updateMatrix(); + drawable.skin.updateSilhouette(); + const candidateBounds = drawable.getFastBounds(); + if (bounds.intersects(candidateBounds)) { + result.push({ + id, + drawable, + intersection: Rectangle.intersect(bounds, candidateBounds) + }); + } } } - return result; - }, []); + } + return result; } /** @@ -1556,6 +1620,43 @@ class RenderWebGL extends EventEmitter { // Simplify boundary points using convex hull. return hull(boundaryPoints, Infinity); } + + /** + * Sample a "final" color from an array of drawables at a given scratch space. + * Will blend any alpha values with the drawables "below" it. + * @param {twgl.v3} vec Scratch Vector Space to sample + * @param {Array} drawables A list of drawables with the "top most" + * drawable at index 0 + * @param {Uint8ClampedArray} dst The color3b space to store the answer in. + * @return {Uint8ClampedArray} The dst vector with everything blended down. + */ + static sampleColor3b (vec, drawables, dst) { + dst = dst || new Uint8ClampedArray(3); + dst.fill(0); + let blendAlpha = 1; + for (let index = 0; blendAlpha !== 0 && index < drawables.length; index++) { + /* + if (left > vec[0] || right < vec[0] || + bottom > vec[1] || top < vec[0]) { + continue; + } + */ + Drawable.sampleColor4b(vec, drawables[index].drawable, __blendColor); + // if we are fully transparent, go to the next one "down" + const sampleAlpha = __blendColor[3] / 255; + // premultiply alpha + dst[0] += __blendColor[0] * blendAlpha * sampleAlpha; + dst[1] += __blendColor[1] * blendAlpha * sampleAlpha; + dst[2] += __blendColor[2] * blendAlpha * sampleAlpha; + blendAlpha *= (1 - sampleAlpha); + } + // Backdrop could be transparent, so we need to go to the "clear color" of the + // draw scene (white) as a fallback if everything was alpha + dst[0] += blendAlpha * 255; + dst[1] += blendAlpha * 255; + dst[2] += blendAlpha * 255; + return dst; + } } // :3 From 591e78499372eed01f7bb561501bd0a8f8e41227 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Wed, 8 Aug 2018 14:30:51 -0400 Subject: [PATCH 1852/1971] Framebuffer PenSkin (#319) * draw pen skin lines and stamps to a framebuffer * skip reading pixels and draw stamp to framebuffer * update silhouette with readPixels * draw pen canvas to buffer when its dirty Composite lines and stamps on browser preferred side (cpu/gpu) until the export texture is needed. Then blend the canvas with the current buffer contents. Updating this way invalidates useProgram optimization and the renderer currently does not have a way to know this. * draw lines on framebuffer through fragment shader * optimize draw regions and pen skin matrix creation * control draw regions * mobile gpus need high precision floats for line drawing * optimize cpu pen line math * sampled pen line caps * sampleless pen skin lines, lint, document pen skin buffer ops * add PenSkin._canvasDirty to constructor * remove DRAW_MODE_line * comment PenSkin reused memory, use memory in drawRectangle * turn draw region id's into optional method handlers A region ID object may have an enter and exit method on it that are used by default when entering and exiting that region. * remove old DRAW_MODE_line precision setting * standardize vert lines on 4 spaces * fixup! turn draw region id's into optional method handlers * do not draw when updating pen skin silhouette * do not premultiply stamp colors by alpha * fixup! do not draw when updating pen skin silhouette * do not premultiply line color * add a small rim around the line for aliasing * variable pen line alias amount * reverse offset pen line on y axis by relative alias amount Reverse the offset to keep small line overlap to a minimum. * fixup! reverse offset pen line on y axis by relative alias amount * medium precision gpu floats --- packages/scratch-render/src/RenderWebGL.js | 77 +++++++++++++++++----- 1 file changed, 62 insertions(+), 15 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index dbd55985b3..91e17e8c6c 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -172,6 +172,12 @@ class RenderWebGL extends EventEmitter { /** @type {HTMLCanvasElement} */ this._tempCanvas = document.createElement('canvas'); + /** @type {any} */ + this._regionId = null; + + /** @type {function} */ + this._exitRegion = null; + this._svgTextBubble = new SVGTextBubble(); this._createGeometry(); @@ -573,6 +579,8 @@ class RenderWebGL extends EventEmitter { * Draw all current drawables and present the frame on the canvas. */ draw () { + this._doExitDrawRegion(); + const gl = this._gl; twgl.bindFramebufferInfo(gl, null); @@ -729,6 +737,8 @@ class RenderWebGL extends EventEmitter { } _isTouchingColorGpuStart (drawableID, candidateIDs, bounds, color3b, mask3b) { + this._doExitDrawRegion(); + const gl = this._gl; twgl.bindFramebufferInfo(gl, this._queryBufferInfo); @@ -997,6 +1007,8 @@ class RenderWebGL extends EventEmitter { * @return {?DrawableExtraction} Data about the picked drawable */ extractDrawable (drawableID, x, y) { + this._doExitDrawRegion(); + const drawable = this._allDrawables[drawableID]; if (!drawable) return null; @@ -1077,6 +1089,8 @@ class RenderWebGL extends EventEmitter { * @return {?ColorExtraction} Data about the picked color */ extractColor (x, y, radius) { + this._doExitDrawRegion(); + const scratchX = Math.round(this._nativeSize[0] * ((x / this._gl.canvas.clientWidth) - 0.5)); const scratchY = Math.round(-this._nativeSize[1] * ((y / this._gl.canvas.clientHeight) - 0.5)); @@ -1313,6 +1327,8 @@ class RenderWebGL extends EventEmitter { * @param {int} stampID - the unique ID of the Drawable to use as the stamp. */ penStamp (penSkinID, stampID) { + this._doExitDrawRegion(); + const stampDrawable = this._allDrawables[stampID]; if (!stampDrawable) { return; @@ -1337,24 +1353,12 @@ class RenderWebGL extends EventEmitter { try { gl.disable(gl.BLEND); - this._drawThese([stampID], ShaderManager.DRAW_MODE.default, projection, {ignoreVisibility: true}); + this._drawThese([stampID], ShaderManager.DRAW_MODE.stamp, projection, {ignoreVisibility: true}); } finally { gl.enable(gl.BLEND); } - const stampPixels = new Uint8Array(Math.floor(bounds.width * bounds.height * 4)); - gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, stampPixels); - - const stampCanvas = this._tempCanvas; - stampCanvas.width = bounds.width; - stampCanvas.height = bounds.height; - - const stampContext = stampCanvas.getContext('2d'); - const stampImageData = stampContext.createImageData(bounds.width, bounds.height); - stampImageData.data.set(stampPixels); - stampContext.putImageData(stampImageData, 0, 0); - - skin.drawStamp(stampCanvas, bounds.left, bounds.top); + skin._drawToBuffer(this._queryBufferInfo.attachments[0], bounds.left, bounds.top); } /* ****** @@ -1422,6 +1426,41 @@ class RenderWebGL extends EventEmitter { } } + /** + * Enter a draw region. + * + * A draw region is where multiple draw operations are performed with the + * same GL state. WebGL performs poorly when it changes state like blend + * mode. Marking a collection of state values as a "region" the renderer + * can skip superfluous extra state calls when it is already in that + * region. Since one region may be entered from within another a exit + * handle can also be registered that is called when a new region is about + * to be entered to restore a common inbetween state. + * + * @param {any} regionId - id of the region to enter + * @param {function} enter - handle to call when first entering a region + * @param {function} exit - handle to call when leaving a region + */ + enterDrawRegion (regionId, enter = regionId.enter, exit = regionId.exit) { + if (this._regionId !== regionId) { + this._doExitDrawRegion(); + this._regionId = regionId; + enter(); + this._exitRegion = exit; + } + } + + /** + * Forcefully exit the current region returning to a common inbetween GL + * state. + */ + _doExitDrawRegion () { + if (this._exitRegion !== null) { + this._exitRegion(); + } + this._exitRegion = null; + } + /** * Draw a set of Drawables, by drawable ID * @param {Array} drawables The Drawable IDs to draw, possibly this._drawList. @@ -1467,7 +1506,13 @@ class RenderWebGL extends EventEmitter { let effectBits = drawable.getEnabledEffects(); effectBits &= opts.hasOwnProperty('effectMask') ? opts.effectMask : effectBits; const newShader = this._shaderManager.getShader(drawMode, effectBits); - if (currentShader !== newShader) { + + // Manually perform region check. Do not create functions inside a + // loop. + if (this._regionId !== newShader) { + this._doExitDrawRegion(); + this._regionId = newShader; + currentShader = newShader; gl.useProgram(currentShader.program); twgl.setBuffersAndAttributes(gl, currentShader, this._bufferInfo); @@ -1496,6 +1541,8 @@ class RenderWebGL extends EventEmitter { twgl.drawBufferInfo(gl, this._bufferInfo, gl.TRIANGLES); } + + this._regionId = null; } /** From 610e73280c52cb822d286a5cb760234b3aa64eb3 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 18 Sep 2018 16:01:53 -0400 Subject: [PATCH 1853/1971] Merge pull request #348 from rschamp/get-canvas Add getter for the renderer's canvas --- packages/scratch-render/src/RenderWebGL.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 91e17e8c6c..c6c7efab55 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -201,6 +201,13 @@ class RenderWebGL extends EventEmitter { return this._gl; } + /** + * @returns {HTMLCanvasElement} the canvas of the WebGL rendering context associated with this renderer. + */ + get canvas () { + return this._gl && this._gl.canvas; + } + /** * Set the physical size of the stage in device-independent pixels. * This will be multiplied by the device's pixel ratio on high-DPI displays. From c64ac869db0b481c03a924be95d8025b3c3ba8d4 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 18 Sep 2018 16:06:46 -0400 Subject: [PATCH 1854/1971] Merge pull request #347 from rschamp/snapshots Add ability to get a snapshot of the next frame --- packages/scratch-render/src/RenderWebGL.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index c6c7efab55..406fb43204 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -178,6 +178,9 @@ class RenderWebGL extends EventEmitter { /** @type {function} */ this._exitRegion = null; + /** @type {Array.} */ + this._snapshotCallbacks = []; + this._svgTextBubble = new SVGTextBubble(); this._createGeometry(); @@ -596,6 +599,11 @@ class RenderWebGL extends EventEmitter { gl.clear(gl.COLOR_BUFFER_BIT); this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, this._projection); + if (this._snapshotCallbacks.length > 0) { + const snapshot = gl.canvas.toDataURL(); + this._snapshotCallbacks.forEach(cb => cb(snapshot)); + this._snapshotCallbacks = []; + } } /** @@ -1711,6 +1719,18 @@ class RenderWebGL extends EventEmitter { dst[2] += blendAlpha * 255; return dst; } + + /** + * @callback RenderWebGL#snapshotCallback + * @param {string} dataURI Data URI of the snapshot of the renderer + */ + + /** + * @param {snapshotCallback} callback Function called in the next frame with the snapshot data + */ + requestSnapshot (callback) { + this._snapshotCallbacks.push(callback); + } } // :3 From d7bafaadd9dd6d04dcb0cb4f50f9b72b8aa25cc5 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Tue, 2 Oct 2018 12:18:21 -0700 Subject: [PATCH 1855/1971] Merge pull request #349 from wdr-data/fix/infinite-bounds-loop Fix bug when bounds would be infinite and cause never ending loop --- packages/scratch-render/src/RenderWebGL.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 406fb43204..452ed00415 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -968,7 +968,12 @@ class RenderWebGL extends EventEmitter { if (candidateIDs.length === 0) { return false; } + const bounds = this.clientSpaceToScratchBounds(centerX, centerY, touchWidth, touchHeight); + if (bounds.left === -Infinity || bounds.bottom === -Infinity) { + return false; + } + const hits = []; const worldPos = twgl.v3.create(0, 0, 0); // Iterate over the scratch pixels and check if any candidate can be From 0b8d48f36c01f7822487d33dc5f1d11248f7b8ed Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Fri, 2 Nov 2018 08:59:07 -0400 Subject: [PATCH 1856/1971] Merge pull request #354 from mzgoddard/fix-touching-mouse Fix touching mouse --- packages/scratch-render/src/RenderWebGL.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 452ed00415..7261c50400 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -930,6 +930,9 @@ class RenderWebGL extends EventEmitter { const bounds = this.clientSpaceToScratchBounds(centerX, centerY, touchWidth, touchHeight); const worldPos = twgl.v3.create(); + drawable.updateMatrix(); + drawable.skin.updateSilhouette(); + for (worldPos[1] = bounds.bottom; worldPos[1] <= bounds.top; worldPos[1]++) { for (worldPos[0] = bounds.left; worldPos[0] <= bounds.right; worldPos[0]++) { if (drawable.isTouching(worldPos)) { From 1f3cb55f03241996933f6f7bc93f3f28e6e517e1 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 7 Jan 2019 11:19:49 -0500 Subject: [PATCH 1857/1971] Merge pull request #392 from kchadha/fix-uncaught-type-error Fix uncaught error `cannot find property updateSilhouette of null` --- packages/scratch-render/src/RenderWebGL.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 7261c50400..dcd444e281 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -931,7 +931,11 @@ class RenderWebGL extends EventEmitter { const worldPos = twgl.v3.create(); drawable.updateMatrix(); - drawable.skin.updateSilhouette(); + if (drawable.skin) { + drawable.skin.updateSilhouette(); + } else { + log.warn(`Could not find skin for drawable with id: ${drawableID}`); + } for (worldPos[1] = bounds.bottom; worldPos[1] <= bounds.top; worldPos[1]++) { for (worldPos[0] = bounds.left; worldPos[0] <= bounds.right; worldPos[0]++) { @@ -963,7 +967,11 @@ class RenderWebGL extends EventEmitter { // default pick list ignores visible and ghosted sprites. if (drawable.getVisible() && drawable.getUniforms().u_ghost !== 0) { drawable.updateMatrix(); - drawable.skin.updateSilhouette(); + if (drawable.skin) { + drawable.skin.updateSilhouette(); + } else { + log.warn(`Could not find skin for drawable with id: ${id}`); + } return true; } return false; From 7c0c5f63a2fdcbe115b35efceee40d1f205e44eb Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 8 Jan 2019 12:41:49 -0500 Subject: [PATCH 1858/1971] Merge pull request #391 from LLK/blurrybg Increase maximum texture size and back it off if GL fails to render --- packages/scratch-render/src/SVGSkin.js | 28 +++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index b99998c542..bc4ab1e5ed 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -3,7 +3,7 @@ const twgl = require('twgl.js'); const Skin = require('./Skin'); const SvgRenderer = require('scratch-svg-renderer').SVGRenderer; -const MAX_TEXTURE_DIMENSION = 2048; +const MAX_TEXTURE_DIMENSION = 10240; class SVGSkin extends Skin { /** @@ -68,20 +68,30 @@ class SVGSkin extends Skin { getTexture (scale) { // The texture only ever gets uniform scale. Take the larger of the two axes. const scaleMax = scale ? Math.max(Math.abs(scale[0]), Math.abs(scale[1])) : 100; - const requestedScale = Math.min(scaleMax / 100, this._maxTextureScale); + const requestedScale = scaleMax / 100; let newScale = this._textureScale; while ((newScale < this._maxTextureScale) && (requestedScale >= 1.5 * newScale)) { newScale *= 2; } + + let error; + const callback = () => { + if (this._textureScale === newScale) { + const gl = this._renderer.gl; + gl.bindTexture(gl.TEXTURE_2D, this._texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._svgRenderer.canvas); + error = gl.getError(); + if (error && newScale > 1) { + newScale /= 2; + this._textureScale = newScale; + this._maxTextureScale = newScale; + this._svgRenderer._draw(this._textureScale, callback); + } + } + }; if (this._textureScale !== newScale) { this._textureScale = newScale; - this._svgRenderer._draw(this._textureScale, () => { - if (this._textureScale === newScale) { - const gl = this._renderer.gl; - gl.bindTexture(gl.TEXTURE_2D, this._texture); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._svgRenderer.canvas); - } - }); + this._svgRenderer._draw(this._textureScale, callback); } return this._texture; From 971f4f88c8b46f24cd2ab1e667bd242cda999f5d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 8 Jan 2019 16:00:30 -0500 Subject: [PATCH 1859/1971] Merge pull request #393 from LLK/revert-391-blurrybg Revert "Increase maximum texture size and back it off if GL fails to render" --- packages/scratch-render/src/SVGSkin.js | 28 +++++++++----------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index bc4ab1e5ed..b99998c542 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -3,7 +3,7 @@ const twgl = require('twgl.js'); const Skin = require('./Skin'); const SvgRenderer = require('scratch-svg-renderer').SVGRenderer; -const MAX_TEXTURE_DIMENSION = 10240; +const MAX_TEXTURE_DIMENSION = 2048; class SVGSkin extends Skin { /** @@ -68,30 +68,20 @@ class SVGSkin extends Skin { getTexture (scale) { // The texture only ever gets uniform scale. Take the larger of the two axes. const scaleMax = scale ? Math.max(Math.abs(scale[0]), Math.abs(scale[1])) : 100; - const requestedScale = scaleMax / 100; + const requestedScale = Math.min(scaleMax / 100, this._maxTextureScale); let newScale = this._textureScale; while ((newScale < this._maxTextureScale) && (requestedScale >= 1.5 * newScale)) { newScale *= 2; } - - let error; - const callback = () => { - if (this._textureScale === newScale) { - const gl = this._renderer.gl; - gl.bindTexture(gl.TEXTURE_2D, this._texture); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._svgRenderer.canvas); - error = gl.getError(); - if (error && newScale > 1) { - newScale /= 2; - this._textureScale = newScale; - this._maxTextureScale = newScale; - this._svgRenderer._draw(this._textureScale, callback); - } - } - }; if (this._textureScale !== newScale) { this._textureScale = newScale; - this._svgRenderer._draw(this._textureScale, callback); + this._svgRenderer._draw(this._textureScale, () => { + if (this._textureScale === newScale) { + const gl = this._renderer.gl; + gl.bindTexture(gl.TEXTURE_2D, this._texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._svgRenderer.canvas); + } + }); } return this._texture; From f8a921e6cb1b44bd872425c2fdf6766441fec4b7 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 9 Jan 2019 14:59:56 -0500 Subject: [PATCH 1860/1971] Merge pull request #394 from paulkaplan/defer-silhouette-updates Implement updateSilhouette to allow updates to happen when needed --- packages/scratch-render/src/SVGSkin.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index b99998c542..98c7397b38 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -101,7 +101,6 @@ class SVGSkin extends Skin { if (this._texture) { gl.bindTexture(gl.TEXTURE_2D, this._texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._svgRenderer.canvas); - this._silhouette.update(this._svgRenderer.canvas); } else { // TODO: mipmaps? const textureOptions = { @@ -111,8 +110,8 @@ class SVGSkin extends Skin { }; this._texture = twgl.createTexture(gl, textureOptions); - this._silhouette.update(this._svgRenderer.canvas); } + this._silhouetteDirty = true; const maxDimension = Math.max(this._svgRenderer.canvas.width, this._svgRenderer.canvas.height); let testScale = 2; @@ -126,6 +125,12 @@ class SVGSkin extends Skin { }); } + updateSilhouette () { + if (this._silhouetteDirty) { + this._silhouette.update(this._svgRenderer.canvas); + this._silhouetteDirty = false; + } + } } module.exports = SVGSkin; From 12d44bec0b014ecb3a0ef0df38c0c1e90e6aaf57 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 15 Jan 2019 12:25:37 -0500 Subject: [PATCH 1861/1971] Merge pull request #398 from paulkaplan/revert-silhouette MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "Merge pull request #394 from paulkaplan/defer-silhouette-upda… --- packages/scratch-render/src/SVGSkin.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 98c7397b38..b99998c542 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -101,6 +101,7 @@ class SVGSkin extends Skin { if (this._texture) { gl.bindTexture(gl.TEXTURE_2D, this._texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._svgRenderer.canvas); + this._silhouette.update(this._svgRenderer.canvas); } else { // TODO: mipmaps? const textureOptions = { @@ -110,8 +111,8 @@ class SVGSkin extends Skin { }; this._texture = twgl.createTexture(gl, textureOptions); + this._silhouette.update(this._svgRenderer.canvas); } - this._silhouetteDirty = true; const maxDimension = Math.max(this._svgRenderer.canvas.width, this._svgRenderer.canvas.height); let testScale = 2; @@ -125,12 +126,6 @@ class SVGSkin extends Skin { }); } - updateSilhouette () { - if (this._silhouetteDirty) { - this._silhouette.update(this._svgRenderer.canvas); - this._silhouetteDirty = false; - } - } } module.exports = SVGSkin; From 62a479ace7f495032d64e40601d76fe4d6dd4e40 Mon Sep 17 00:00:00 2001 From: Katie Broida Date: Thu, 17 Jan 2019 13:25:37 -0500 Subject: [PATCH 1862/1971] Merge pull request #400 from ktbee/fence-width-compat Add inset logic that is closer to Scratch 2's inset --- packages/scratch-render/src/RenderWebGL.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index dcd444e281..001aac386c 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1301,14 +1301,15 @@ class RenderWebGL extends EventEmitter { const dy = y - drawable._position[1]; const aabb = drawable.getFastBounds(); + const inset = Math.floor(Math.min(aabb.width, aabb.height) / 2); - const sx = this._xRight - Math.min(FENCE_WIDTH, Math.floor((aabb.right - aabb.left) / 2)); + const sx = this._xRight - Math.min(FENCE_WIDTH, inset); if (aabb.right + dx < -sx) { x = drawable._position[0] - (sx + aabb.right); } else if (aabb.left + dx > sx) { x = drawable._position[0] + (sx - aabb.left); } - const sy = this._yTop - Math.min(FENCE_WIDTH, Math.floor((aabb.top - aabb.bottom) / 2)); + const sy = this._yTop - Math.min(FENCE_WIDTH, inset); if (aabb.top + dy < -sy) { y = drawable._position[1] - (sy + aabb.top); } else if (aabb.bottom + dy > sy) { From f2d0fabae104732f541c25c992099eb09f6f2824 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 22 Jan 2019 17:29:43 -0500 Subject: [PATCH 1863/1971] Merge pull request #404 from LLK/touchingColor2 Update silhouette after getting texture at a new scale --- packages/scratch-render/src/SVGSkin.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index b99998c542..1054a280b1 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -80,6 +80,7 @@ class SVGSkin extends Skin { const gl = this._renderer.gl; gl.bindTexture(gl.TEXTURE_2D, this._texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._svgRenderer.canvas); + this._silhouette.update(this._svgRenderer.canvas); } }); } From e401df93aec2ec6492b9dd2c19c7771bb4eccc75 Mon Sep 17 00:00:00 2001 From: Evelyn Eastmond Date: Thu, 24 Jan 2019 21:33:50 -0500 Subject: [PATCH 1864/1971] Merge pull request #376 from evhan55/bug/extract-drawable Fix extraction of drawable to not clip bounds. --- packages/scratch-render/src/RenderWebGL.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 001aac386c..24b4696db8 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1048,11 +1048,28 @@ class RenderWebGL extends EventEmitter { const scratchY = this._nativeSize[1] * ((y / this._gl.canvas.clientHeight) - 0.5); const gl = this._gl; - twgl.bindFramebufferInfo(gl, this._queryBufferInfo); const bounds = drawable.getFastBounds(); bounds.snapToInt(); + // Set a reasonable max limit width and height for the bufferInfo bounds + const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); + const clampedWidth = Math.min(2048, bounds.width, maxTextureSize); + const clampedHeight = Math.min(2048, bounds.height, maxTextureSize); + + // Make a new bufferInfo since this._queryBufferInfo is limited to 480x360 + const attachments = [ + {format: gl.RGBA}, + {format: gl.DEPTH_STENCIL} + ]; + const bufferInfo = twgl.createFramebufferInfo(gl, attachments, clampedWidth, clampedHeight); + + // If the new bufferInfo is invalid, fall back to using the smaller _queryBufferInfo + twgl.bindFramebufferInfo(gl, bufferInfo); + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) { + twgl.bindFramebufferInfo(gl, this._queryBufferInfo); + } + // Translate to scratch units relative to the drawable const pickX = scratchX - bounds.left; const pickY = scratchY + bounds.top; From 5ccef2c8bbab840b6828c60266bf18b99d7e8d78 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 6 Feb 2019 13:31:52 -0800 Subject: [PATCH 1865/1971] Merge pull request #407 from cwillisf/coordinates-fixups Adjust CPU isTouchingColor to match GPU results --- packages/scratch-render/src/RenderWebGL.js | 73 +++++++++++++--------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 24b4696db8..432d283c14 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -256,8 +256,7 @@ class RenderWebGL extends EventEmitter { this._yBottom = yBottom; this._yTop = yTop; - // swap yBottom & yTop to fit Scratch convention of +y=up - this._projection = twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1); + this._projection = this._makeOrthoProjection(xLeft, xRight, yBottom, yTop); this._setNativeSize(Math.abs(xRight - xLeft), Math.abs(yBottom - yTop)); } @@ -281,6 +280,20 @@ class RenderWebGL extends EventEmitter { this.emit(RenderConstants.Events.NativeSizeChanged, {newSize: this._nativeSize}); } + /** + * Build a projection matrix for Scratch coordinates. For example, `_makeOrthoProjection(-240,240,-180,180)` will + * mean the lower-left pixel is at (-240,-179) and the upper right pixel is at (239,180), matching Scratch 2.0. + * @param {number} xLeft - the left edge of the projection volume (-240) + * @param {number} xRight - the right edge of the projection volume (240) + * @param {number} yBottom - the bottom edge of the projection volume (-180) + * @param {number} yTop - the top edge of the projection volume (180) + * @returns {module:twgl/m4.Mat4} - a projection matrix containing [xLeft,xRight) and (yBottom,yTop] + */ + _makeOrthoProjection (xLeft, xRight, yBottom, yTop) { + // swap yBottom & yTop to fit Scratch convention of +y=up + return twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1); + } + /** * Create a new bitmap skin from a snapshot of the provided bitmap data. * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin. @@ -518,7 +531,7 @@ class RenderWebGL extends EventEmitter { * Returns the position of the given drawableID in the draw list. This is * the absolute position irrespective of layer group. * @param {number} drawableID The drawable ID to find. - * @return {number} The postion of the given drawable ID. + * @return {number} The position of the given drawable ID. */ getDrawableOrder (drawableID) { return this._drawList.indexOf(drawableID); @@ -532,7 +545,7 @@ class RenderWebGL extends EventEmitter { * "go to back": setDrawableOrder(id, 1); (assuming stage at 0). * "go to front": setDrawableOrder(id, Infinity); * @param {int} drawableID ID of Drawable to reorder. - * @param {number} order New absolute order or relative order adjusment. + * @param {number} order New absolute order or relative order adjustment. * @param {string=} group Name of layer group drawable belongs to. * Reordering will not take place if drawable cannot be found within the bounds * of the layer group. @@ -703,7 +716,7 @@ class RenderWebGL extends EventEmitter { /** * Check if a particular Drawable is touching a particular color. - * Unlike touching drawable, if the "tester" is invisble, we will still test. + * Unlike touching drawable, if the "tester" is invisible, we will still test. * @param {int} drawableID The ID of the Drawable to check. * @param {Array} color3b Test if the Drawable is touching this color. * @param {Array} [mask3b] Optionally mask the check to this part of Drawable. @@ -728,23 +741,23 @@ class RenderWebGL extends EventEmitter { const color = __touchingColor; const hasMask = Boolean(mask3b); - for (let y = bounds.bottom; y <= bounds.top; y++) { - if (bounds.width * (y - bounds.bottom) * (candidates.length + 1) >= __cpuTouchingColorPixelCount) { - return this._isTouchingColorGpuFin(bounds, color3b, y - bounds.bottom); + // Scratch Space - +y is top + for (let y = 0; y < bounds.height; ++y) { + if (bounds.width * y * (candidates.length + 1) >= __cpuTouchingColorPixelCount) { + return this._isTouchingColorGpuFin(bounds, color3b, y); } - // Scratch Space - +y is top - for (let x = bounds.left; x <= bounds.right; x++) { - point[1] = y; - point[0] = x; - if ( - // if we use a mask, check our sample color - (hasMask ? - maskMatches(Drawable.sampleColor4b(point, drawable, color), mask3b) : - drawable.isTouching(point)) && - // and the target color is drawn at this pixel - colorMatches(RenderWebGL.sampleColor3b(point, candidates, color), color3b, 0) - ) { - return true; + for (let x = 0; x < bounds.width; ++x) { + point[0] = bounds.left + x; // bounds.left <= point[0] < bounds.right + point[1] = bounds.top - y; // bounds.bottom < point[1] <= bounds.top ("flipped") + // if we use a mask, check our sample color... + if (hasMask ? + maskMatches(Drawable.sampleColor4b(point, drawable, color), mask3b) : + drawable.isTouching(point)) { + RenderWebGL.sampleColor3b(point, candidates, color); + // ...and the target color is drawn at this pixel + if (colorMatches(color, color3b, 0)) { + return true; + } } } } @@ -760,7 +773,7 @@ class RenderWebGL extends EventEmitter { // Limit size of viewport to the bounds around the target Drawable, // and create the projection matrix for the draw. gl.viewport(0, 0, bounds.width, bounds.height); - const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); + const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom); let fillBackgroundColor = this._backgroundColor; @@ -843,7 +856,7 @@ class RenderWebGL extends EventEmitter { const candidates = this._candidatesTouching(drawableID, // even if passed an invisible drawable, we will NEVER touch it! candidateIDs.filter(id => this._allDrawables[id]._visible)); - // if we are invisble we don't touch anything. + // if we are invisible we don't touch anything. if (candidates.length === 0 || !this._allDrawables[drawableID]._visible) { return false; } @@ -876,7 +889,7 @@ class RenderWebGL extends EventEmitter { /** * Convert a client based x/y position on the canvas to a Scratch 3 world space - * Rectangle. This creates recangles with a radius to cover selecting multiple + * Rectangle. This creates rectangles with a radius to cover selecting multiple * scratch pixels with touch / small render areas. * * @param {int} centerX The client x coordinate of the picking location. @@ -993,7 +1006,7 @@ class RenderWebGL extends EventEmitter { for (worldPos[0] = bounds.left; worldPos[0] <= bounds.right; worldPos[0]++) { // Check candidates in the reverse order they would have been - // drawn. This will determine what candiate's silhouette pixel + // drawn. This will determine what candidate's silhouette pixel // would have been drawn at the point. for (let d = candidateIDs.length - 1; d >= 0; d--) { const id = candidateIDs[d]; @@ -1077,7 +1090,7 @@ class RenderWebGL extends EventEmitter { // Limit size of viewport to the bounds around the target Drawable, // and create the projection matrix for the draw. gl.viewport(0, 0, bounds.width, bounds.height); - const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); + const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom); gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); @@ -1152,7 +1165,7 @@ class RenderWebGL extends EventEmitter { const pickY = bounds.top - scratchY; gl.viewport(0, 0, bounds.width, bounds.height); - const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); + const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom); gl.clearColor.apply(gl, this._backgroundColor); gl.clear(gl.COLOR_BUFFER_BIT); @@ -1395,7 +1408,7 @@ class RenderWebGL extends EventEmitter { // Limit size of viewport to the bounds around the stamp Drawable and create the projection matrix for the draw. gl.viewport(0, 0, bounds.width, bounds.height); - const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); + const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom); gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); @@ -1484,7 +1497,7 @@ class RenderWebGL extends EventEmitter { * can skip superfluous extra state calls when it is already in that * region. Since one region may be entered from within another a exit * handle can also be registered that is called when a new region is about - * to be entered to restore a common inbetween state. + * to be entered to restore a common in-between state. * * @param {any} regionId - id of the region to enter * @param {function} enter - handle to call when first entering a region @@ -1616,7 +1629,7 @@ class RenderWebGL extends EventEmitter { * * The determinant is useful in this case to know if AC is counter * clockwise from AB. A positive value means the AC is counter - * clockwise from AC. A negative value menas AC is clockwise from AB. + * clockwise from AC. A negative value means AC is clockwise from AB. * * @param {Float32Array} A A 2d vector in space. * @param {Float32Array} B A 2d vector in space. From 42e6f5b7546de602f7eae2e6978a2836cb73c426 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Thu, 7 Feb 2019 13:11:57 -0800 Subject: [PATCH 1866/1971] Merge pull request #410 from LLK/revert-407-coordinates-fixups Revert "Adjust CPU isTouchingColor to match GPU results" --- packages/scratch-render/src/RenderWebGL.js | 73 +++++++++------------- 1 file changed, 30 insertions(+), 43 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 432d283c14..24b4696db8 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -256,7 +256,8 @@ class RenderWebGL extends EventEmitter { this._yBottom = yBottom; this._yTop = yTop; - this._projection = this._makeOrthoProjection(xLeft, xRight, yBottom, yTop); + // swap yBottom & yTop to fit Scratch convention of +y=up + this._projection = twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1); this._setNativeSize(Math.abs(xRight - xLeft), Math.abs(yBottom - yTop)); } @@ -280,20 +281,6 @@ class RenderWebGL extends EventEmitter { this.emit(RenderConstants.Events.NativeSizeChanged, {newSize: this._nativeSize}); } - /** - * Build a projection matrix for Scratch coordinates. For example, `_makeOrthoProjection(-240,240,-180,180)` will - * mean the lower-left pixel is at (-240,-179) and the upper right pixel is at (239,180), matching Scratch 2.0. - * @param {number} xLeft - the left edge of the projection volume (-240) - * @param {number} xRight - the right edge of the projection volume (240) - * @param {number} yBottom - the bottom edge of the projection volume (-180) - * @param {number} yTop - the top edge of the projection volume (180) - * @returns {module:twgl/m4.Mat4} - a projection matrix containing [xLeft,xRight) and (yBottom,yTop] - */ - _makeOrthoProjection (xLeft, xRight, yBottom, yTop) { - // swap yBottom & yTop to fit Scratch convention of +y=up - return twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1); - } - /** * Create a new bitmap skin from a snapshot of the provided bitmap data. * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin. @@ -531,7 +518,7 @@ class RenderWebGL extends EventEmitter { * Returns the position of the given drawableID in the draw list. This is * the absolute position irrespective of layer group. * @param {number} drawableID The drawable ID to find. - * @return {number} The position of the given drawable ID. + * @return {number} The postion of the given drawable ID. */ getDrawableOrder (drawableID) { return this._drawList.indexOf(drawableID); @@ -545,7 +532,7 @@ class RenderWebGL extends EventEmitter { * "go to back": setDrawableOrder(id, 1); (assuming stage at 0). * "go to front": setDrawableOrder(id, Infinity); * @param {int} drawableID ID of Drawable to reorder. - * @param {number} order New absolute order or relative order adjustment. + * @param {number} order New absolute order or relative order adjusment. * @param {string=} group Name of layer group drawable belongs to. * Reordering will not take place if drawable cannot be found within the bounds * of the layer group. @@ -716,7 +703,7 @@ class RenderWebGL extends EventEmitter { /** * Check if a particular Drawable is touching a particular color. - * Unlike touching drawable, if the "tester" is invisible, we will still test. + * Unlike touching drawable, if the "tester" is invisble, we will still test. * @param {int} drawableID The ID of the Drawable to check. * @param {Array} color3b Test if the Drawable is touching this color. * @param {Array} [mask3b] Optionally mask the check to this part of Drawable. @@ -741,23 +728,23 @@ class RenderWebGL extends EventEmitter { const color = __touchingColor; const hasMask = Boolean(mask3b); - // Scratch Space - +y is top - for (let y = 0; y < bounds.height; ++y) { - if (bounds.width * y * (candidates.length + 1) >= __cpuTouchingColorPixelCount) { - return this._isTouchingColorGpuFin(bounds, color3b, y); + for (let y = bounds.bottom; y <= bounds.top; y++) { + if (bounds.width * (y - bounds.bottom) * (candidates.length + 1) >= __cpuTouchingColorPixelCount) { + return this._isTouchingColorGpuFin(bounds, color3b, y - bounds.bottom); } - for (let x = 0; x < bounds.width; ++x) { - point[0] = bounds.left + x; // bounds.left <= point[0] < bounds.right - point[1] = bounds.top - y; // bounds.bottom < point[1] <= bounds.top ("flipped") - // if we use a mask, check our sample color... - if (hasMask ? - maskMatches(Drawable.sampleColor4b(point, drawable, color), mask3b) : - drawable.isTouching(point)) { - RenderWebGL.sampleColor3b(point, candidates, color); - // ...and the target color is drawn at this pixel - if (colorMatches(color, color3b, 0)) { - return true; - } + // Scratch Space - +y is top + for (let x = bounds.left; x <= bounds.right; x++) { + point[1] = y; + point[0] = x; + if ( + // if we use a mask, check our sample color + (hasMask ? + maskMatches(Drawable.sampleColor4b(point, drawable, color), mask3b) : + drawable.isTouching(point)) && + // and the target color is drawn at this pixel + colorMatches(RenderWebGL.sampleColor3b(point, candidates, color), color3b, 0) + ) { + return true; } } } @@ -773,7 +760,7 @@ class RenderWebGL extends EventEmitter { // Limit size of viewport to the bounds around the target Drawable, // and create the projection matrix for the draw. gl.viewport(0, 0, bounds.width, bounds.height); - const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom); + const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); let fillBackgroundColor = this._backgroundColor; @@ -856,7 +843,7 @@ class RenderWebGL extends EventEmitter { const candidates = this._candidatesTouching(drawableID, // even if passed an invisible drawable, we will NEVER touch it! candidateIDs.filter(id => this._allDrawables[id]._visible)); - // if we are invisible we don't touch anything. + // if we are invisble we don't touch anything. if (candidates.length === 0 || !this._allDrawables[drawableID]._visible) { return false; } @@ -889,7 +876,7 @@ class RenderWebGL extends EventEmitter { /** * Convert a client based x/y position on the canvas to a Scratch 3 world space - * Rectangle. This creates rectangles with a radius to cover selecting multiple + * Rectangle. This creates recangles with a radius to cover selecting multiple * scratch pixels with touch / small render areas. * * @param {int} centerX The client x coordinate of the picking location. @@ -1006,7 +993,7 @@ class RenderWebGL extends EventEmitter { for (worldPos[0] = bounds.left; worldPos[0] <= bounds.right; worldPos[0]++) { // Check candidates in the reverse order they would have been - // drawn. This will determine what candidate's silhouette pixel + // drawn. This will determine what candiate's silhouette pixel // would have been drawn at the point. for (let d = candidateIDs.length - 1; d >= 0; d--) { const id = candidateIDs[d]; @@ -1090,7 +1077,7 @@ class RenderWebGL extends EventEmitter { // Limit size of viewport to the bounds around the target Drawable, // and create the projection matrix for the draw. gl.viewport(0, 0, bounds.width, bounds.height); - const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom); + const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); @@ -1165,7 +1152,7 @@ class RenderWebGL extends EventEmitter { const pickY = bounds.top - scratchY; gl.viewport(0, 0, bounds.width, bounds.height); - const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom); + const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); gl.clearColor.apply(gl, this._backgroundColor); gl.clear(gl.COLOR_BUFFER_BIT); @@ -1408,7 +1395,7 @@ class RenderWebGL extends EventEmitter { // Limit size of viewport to the bounds around the stamp Drawable and create the projection matrix for the draw. gl.viewport(0, 0, bounds.width, bounds.height); - const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom); + const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); @@ -1497,7 +1484,7 @@ class RenderWebGL extends EventEmitter { * can skip superfluous extra state calls when it is already in that * region. Since one region may be entered from within another a exit * handle can also be registered that is called when a new region is about - * to be entered to restore a common in-between state. + * to be entered to restore a common inbetween state. * * @param {any} regionId - id of the region to enter * @param {function} enter - handle to call when first entering a region @@ -1629,7 +1616,7 @@ class RenderWebGL extends EventEmitter { * * The determinant is useful in this case to know if AC is counter * clockwise from AB. A positive value means the AC is counter - * clockwise from AC. A negative value means AC is clockwise from AB. + * clockwise from AC. A negative value menas AC is clockwise from AB. * * @param {Float32Array} A A 2d vector in space. * @param {Float32Array} B A 2d vector in space. From 6f739f1e1d66382d7eb2fd0c3cc73bdd73b2a579 Mon Sep 17 00:00:00 2001 From: Katie Broida Date: Fri, 8 Feb 2019 11:53:07 -0500 Subject: [PATCH 1867/1971] Merge pull request #408 from ktbee/compat-integer-x-y-off-stage Use Math.ceil and Math.floor to match Scratch 2 logic --- packages/scratch-render/src/RenderWebGL.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 24b4696db8..eaf7a64b3b 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1316,21 +1316,20 @@ class RenderWebGL extends EventEmitter { const dx = x - drawable._position[0]; const dy = y - drawable._position[1]; - const aabb = drawable.getFastBounds(); const inset = Math.floor(Math.min(aabb.width, aabb.height) / 2); const sx = this._xRight - Math.min(FENCE_WIDTH, inset); if (aabb.right + dx < -sx) { - x = drawable._position[0] - (sx + aabb.right); + x = Math.ceil(drawable._position[0] - (sx + aabb.right)); } else if (aabb.left + dx > sx) { - x = drawable._position[0] + (sx - aabb.left); + x = Math.floor(drawable._position[0] + (sx - aabb.left)); } const sy = this._yTop - Math.min(FENCE_WIDTH, inset); if (aabb.top + dy < -sy) { - y = drawable._position[1] - (sy + aabb.top); + y = Math.ceil(drawable._position[1] - (sy + aabb.top)); } else if (aabb.bottom + dy > sy) { - y = drawable._position[1] + (sy - aabb.bottom); + y = Math.floor(drawable._position[1] + (sy - aabb.bottom)); } return [x, y]; } From 25958b3aa226127eed2d6fc909bf6705d8bdc68d Mon Sep 17 00:00:00 2001 From: Katie Broida Date: Wed, 13 Feb 2019 13:29:26 -0500 Subject: [PATCH 1868/1971] Merge pull request #409 from ktbee/compat-bitmap-position-off-stage Compatibility fix for bitmap position off stage --- packages/scratch-render/src/RenderWebGL.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index eaf7a64b3b..1e6f87b42f 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1316,7 +1316,7 @@ class RenderWebGL extends EventEmitter { const dx = x - drawable._position[0]; const dy = y - drawable._position[1]; - const aabb = drawable.getFastBounds(); + const aabb = drawable._skin.getFenceBounds(drawable); const inset = Math.floor(Math.min(aabb.width, aabb.height) / 2); const sx = this._xRight - Math.min(FENCE_WIDTH, inset); From 019b6a25b72c1e7452e02a96e6f4db18c0ad0057 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Thu, 14 Feb 2019 10:33:31 -0800 Subject: [PATCH 1869/1971] Merge pull request #406 from cwillisf/playground-webpack Add "query playground" --- packages/scratch-render/src/RenderWebGL.js | 83 ++++++++++++++++++---- 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 1e6f87b42f..05b35e2eaf 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -132,6 +132,9 @@ class RenderWebGL extends EventEmitter { throw new Error('Could not get WebGL context: this browser or environment may not support WebGL.'); } + /** @type {RenderWebGL.UseGpuModes} */ + this._useGpuMode = RenderWebGL.UseGpuModes.Automatic; + /** @type {Drawable[]} */ this._allDrawables = []; @@ -243,6 +246,14 @@ class RenderWebGL extends EventEmitter { this._debugCanvas = canvas; } + /** + * Control the use of the GPU or CPU paths in `isTouchingColor`. + * @param {RenderWebGL.UseGpuModes} useGpuMode - automatically decide, force CPU, or force GPU. + */ + setUseGpuMode (useGpuMode) { + this._useGpuMode = useGpuMode; + } + /** * Set logical size of the stage in Scratch units. * @param {int} xLeft The left edge's x-coordinate. Scratch 2 uses -240. @@ -717,9 +728,16 @@ class RenderWebGL extends EventEmitter { const bounds = this._candidatesBounds(candidates); - // if there are just too many pixels to CPU render efficently, we - // need to let readPixels happen - if (bounds.width * bounds.height * (candidates.length + 1) >= __cpuTouchingColorPixelCount) { + const maxPixelsForCPU = this._getMaxPixelsForCPU(); + + const debugCanvasContext = this._debugCanvas && this._debugCanvas.getContext('2d'); + if (debugCanvasContext) { + this._debugCanvas.width = bounds.width; + this._debugCanvas.height = bounds.height; + } + + // if there are just too many pixels to CPU render efficiently, we need to let readPixels happen + if (bounds.width * bounds.height * (candidates.length + 1) >= maxPixelsForCPU) { this._isTouchingColorGpuStart(drawableID, candidates.map(({id}) => id).reverse(), bounds, color3b, mask3b); } @@ -728,29 +746,45 @@ class RenderWebGL extends EventEmitter { const color = __touchingColor; const hasMask = Boolean(mask3b); + // Scratch Space - +y is top for (let y = bounds.bottom; y <= bounds.top; y++) { - if (bounds.width * (y - bounds.bottom) * (candidates.length + 1) >= __cpuTouchingColorPixelCount) { + if (bounds.width * (y - bounds.bottom) * (candidates.length + 1) >= maxPixelsForCPU) { return this._isTouchingColorGpuFin(bounds, color3b, y - bounds.bottom); } - // Scratch Space - +y is top for (let x = bounds.left; x <= bounds.right; x++) { point[1] = y; point[0] = x; - if ( - // if we use a mask, check our sample color - (hasMask ? - maskMatches(Drawable.sampleColor4b(point, drawable, color), mask3b) : - drawable.isTouching(point)) && - // and the target color is drawn at this pixel - colorMatches(RenderWebGL.sampleColor3b(point, candidates, color), color3b, 0) - ) { - return true; + // if we use a mask, check our sample color... + if (hasMask ? + maskMatches(Drawable.sampleColor4b(point, drawable, color), mask3b) : + drawable.isTouching(point)) { + RenderWebGL.sampleColor3b(point, candidates, color); + if (debugCanvasContext) { + debugCanvasContext.fillStyle = `rgb(${color[0]},${color[1]},${color[2]})`; + debugCanvasContext.fillRect(x - bounds.left, bounds.bottom - y, 1, 1); + } + // ...and the target color is drawn at this pixel + if (colorMatches(color, color3b, 0)) { + return true; + } } } } return false; } + _getMaxPixelsForCPU () { + switch (this._useGpuMode) { + case RenderWebGL.UseGpuModes.ForceCPU: + return Infinity; + case RenderWebGL.UseGpuModes.ForceGPU: + return 0; + case RenderWebGL.UseGpuModes.Automatic: + default: + return __cpuTouchingColorPixelCount; + } + } + _isTouchingColorGpuStart (drawableID, candidateIDs, bounds, color3b, mask3b) { this._doExitDrawRegion(); @@ -1769,4 +1803,25 @@ class RenderWebGL extends EventEmitter { // :3 RenderWebGL.prototype.canHazPixels = RenderWebGL.prototype.extractDrawable; +/** + * Values for setUseGPU() + * @enum {string} + */ +RenderWebGL.UseGpuModes = { + /** + * Heuristically decide whether to use the GPU path, the CPU path, or a dynamic mixture of the two. + */ + Automatic: 'Automatic', + + /** + * Always use the GPU path. + */ + ForceGPU: 'ForceGPU', + + /** + * Always use the CPU path. + */ + ForceCPU: 'ForceCPU' +}; + module.exports = RenderWebGL; From a7906d702297133dea05a45c5c3d3908c448b708 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Tue, 26 Mar 2019 12:12:57 -0400 Subject: [PATCH 1870/1971] Merge pull request #414 from mzgoddard/image-data-texture ImageData WebGL Textures --- packages/scratch-render/src/SVGSkin.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 1054a280b1..90e39081ed 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -77,10 +77,14 @@ class SVGSkin extends Skin { this._textureScale = newScale; this._svgRenderer._draw(this._textureScale, () => { if (this._textureScale === newScale) { + const canvas = this._svgRenderer.canvas; + const context = canvas.getContext('2d'); + const textureData = context.getImageData(0, 0, canvas.width, canvas.height); + const gl = this._renderer.gl; gl.bindTexture(gl.TEXTURE_2D, this._texture); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._svgRenderer.canvas); - this._silhouette.update(this._svgRenderer.canvas); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureData); + this._silhouette.update(textureData); } }); } @@ -99,20 +103,28 @@ class SVGSkin extends Skin { this._svgRenderer.fromString(svgData, 1, () => { const gl = this._renderer.gl; this._textureScale = this._maxTextureScale = 1; + + // Pull out the ImageData from the canvas. ImageData speeds up + // updating Silhouette and is better handled by more browsers in + // regards to memory. + const canvas = this._svgRenderer.canvas; + const context = canvas.getContext('2d'); + const textureData = context.getImageData(0, 0, canvas.width, canvas.height); + if (this._texture) { gl.bindTexture(gl.TEXTURE_2D, this._texture); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._svgRenderer.canvas); - this._silhouette.update(this._svgRenderer.canvas); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureData); + this._silhouette.update(textureData); } else { // TODO: mipmaps? const textureOptions = { auto: true, wrap: gl.CLAMP_TO_EDGE, - src: this._svgRenderer.canvas + src: textureData }; this._texture = twgl.createTexture(gl, textureOptions); - this._silhouette.update(this._svgRenderer.canvas); + this._silhouette.update(textureData); } const maxDimension = Math.max(this._svgRenderer.canvas.width, this._svgRenderer.canvas.height); From ce849a59ae763e06193c81d399dffe35338edff2 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 10 Apr 2019 11:35:25 -0700 Subject: [PATCH 1871/1971] Merge pull request #419 from cwillisf/coordinates-fixups-2 Adjust CPU `isTouchingColor` to match GPU results (again) --- packages/scratch-render/src/RenderWebGL.js | 70 ++++++++++++---------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 05b35e2eaf..9c34172445 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -267,8 +267,7 @@ class RenderWebGL extends EventEmitter { this._yBottom = yBottom; this._yTop = yTop; - // swap yBottom & yTop to fit Scratch convention of +y=up - this._projection = twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1); + this._projection = this._makeOrthoProjection(xLeft, xRight, yBottom, yTop); this._setNativeSize(Math.abs(xRight - xLeft), Math.abs(yBottom - yTop)); } @@ -292,6 +291,20 @@ class RenderWebGL extends EventEmitter { this.emit(RenderConstants.Events.NativeSizeChanged, {newSize: this._nativeSize}); } + /** + * Build a projection matrix for Scratch coordinates. For example, `_makeOrthoProjection(-240,240,-180,180)` will + * mean the lower-left pixel is at (-240,-179) and the upper right pixel is at (239,180), matching Scratch 2.0. + * @param {number} xLeft - the left edge of the projection volume (-240) + * @param {number} xRight - the right edge of the projection volume (240) + * @param {number} yBottom - the bottom edge of the projection volume (-180) + * @param {number} yTop - the top edge of the projection volume (180) + * @returns {module:twgl/m4.Mat4} - a projection matrix containing [xLeft,xRight) and (yBottom,yTop] + */ + _makeOrthoProjection (xLeft, xRight, yBottom, yTop) { + // swap yBottom & yTop to fit Scratch convention of +y=up + return twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1); + } + /** * Create a new bitmap skin from a snapshot of the provided bitmap data. * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin. @@ -529,7 +542,7 @@ class RenderWebGL extends EventEmitter { * Returns the position of the given drawableID in the draw list. This is * the absolute position irrespective of layer group. * @param {number} drawableID The drawable ID to find. - * @return {number} The postion of the given drawable ID. + * @return {number} The position of the given drawable ID. */ getDrawableOrder (drawableID) { return this._drawList.indexOf(drawableID); @@ -543,7 +556,7 @@ class RenderWebGL extends EventEmitter { * "go to back": setDrawableOrder(id, 1); (assuming stage at 0). * "go to front": setDrawableOrder(id, Infinity); * @param {int} drawableID ID of Drawable to reorder. - * @param {number} order New absolute order or relative order adjusment. + * @param {number} order New absolute order or relative order adjustment. * @param {string=} group Name of layer group drawable belongs to. * Reordering will not take place if drawable cannot be found within the bounds * of the layer group. @@ -714,7 +727,7 @@ class RenderWebGL extends EventEmitter { /** * Check if a particular Drawable is touching a particular color. - * Unlike touching drawable, if the "tester" is invisble, we will still test. + * Unlike touching drawable, if the "tester" is invisible, we will still test. * @param {int} drawableID The ID of the Drawable to check. * @param {Array} color3b Test if the Drawable is touching this color. * @param {Array} [mask3b] Optionally mask the check to this part of Drawable. @@ -738,7 +751,7 @@ class RenderWebGL extends EventEmitter { // if there are just too many pixels to CPU render efficiently, we need to let readPixels happen if (bounds.width * bounds.height * (candidates.length + 1) >= maxPixelsForCPU) { - this._isTouchingColorGpuStart(drawableID, candidates.map(({id}) => id).reverse(), bounds, color3b, mask3b); + this._isTouchingColorGpuStart(drawableID, candidates.map(({id}) => id), bounds, color3b, mask3b); } const drawable = this._allDrawables[drawableID]; @@ -747,13 +760,13 @@ class RenderWebGL extends EventEmitter { const hasMask = Boolean(mask3b); // Scratch Space - +y is top - for (let y = bounds.bottom; y <= bounds.top; y++) { - if (bounds.width * (y - bounds.bottom) * (candidates.length + 1) >= maxPixelsForCPU) { - return this._isTouchingColorGpuFin(bounds, color3b, y - bounds.bottom); + for (let y = 0; y < bounds.height; ++y) { + if (bounds.width * y * (candidates.length + 1) >= maxPixelsForCPU) { + return this._isTouchingColorGpuFin(bounds, color3b, y); } - for (let x = bounds.left; x <= bounds.right; x++) { - point[1] = y; - point[0] = x; + for (let x = 0; x < bounds.width; ++x) { + point[0] = bounds.left + x; // bounds.left <= point[0] < bounds.right + point[1] = bounds.top - y; // bounds.bottom < point[1] <= bounds.top ("flipped") // if we use a mask, check our sample color... if (hasMask ? maskMatches(Drawable.sampleColor4b(point, drawable, color), mask3b) : @@ -761,10 +774,10 @@ class RenderWebGL extends EventEmitter { RenderWebGL.sampleColor3b(point, candidates, color); if (debugCanvasContext) { debugCanvasContext.fillStyle = `rgb(${color[0]},${color[1]},${color[2]})`; - debugCanvasContext.fillRect(x - bounds.left, bounds.bottom - y, 1, 1); + debugCanvasContext.fillRect(x, y, 1, 1); } // ...and the target color is drawn at this pixel - if (colorMatches(color, color3b, 0)) { + if (colorMatches(color3b, color, 0)) { return true; } } @@ -794,7 +807,7 @@ class RenderWebGL extends EventEmitter { // Limit size of viewport to the bounds around the target Drawable, // and create the projection matrix for the draw. gl.viewport(0, 0, bounds.width, bounds.height); - const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); + const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom); let fillBackgroundColor = this._backgroundColor; @@ -877,7 +890,7 @@ class RenderWebGL extends EventEmitter { const candidates = this._candidatesTouching(drawableID, // even if passed an invisible drawable, we will NEVER touch it! candidateIDs.filter(id => this._allDrawables[id]._visible)); - // if we are invisble we don't touch anything. + // if we are invisible we don't touch anything. if (candidates.length === 0 || !this._allDrawables[drawableID]._visible) { return false; } @@ -910,7 +923,7 @@ class RenderWebGL extends EventEmitter { /** * Convert a client based x/y position on the canvas to a Scratch 3 world space - * Rectangle. This creates recangles with a radius to cover selecting multiple + * Rectangle. This creates rectangles with a radius to cover selecting multiple * scratch pixels with touch / small render areas. * * @param {int} centerX The client x coordinate of the picking location. @@ -1027,7 +1040,7 @@ class RenderWebGL extends EventEmitter { for (worldPos[0] = bounds.left; worldPos[0] <= bounds.right; worldPos[0]++) { // Check candidates in the reverse order they would have been - // drawn. This will determine what candiate's silhouette pixel + // drawn. This will determine what candidate's silhouette pixel // would have been drawn at the point. for (let d = candidateIDs.length - 1; d >= 0; d--) { const id = candidateIDs[d]; @@ -1111,7 +1124,7 @@ class RenderWebGL extends EventEmitter { // Limit size of viewport to the bounds around the target Drawable, // and create the projection matrix for the draw. gl.viewport(0, 0, bounds.width, bounds.height); - const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); + const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom); gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); @@ -1186,7 +1199,7 @@ class RenderWebGL extends EventEmitter { const pickY = bounds.top - scratchY; gl.viewport(0, 0, bounds.width, bounds.height); - const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); + const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom); gl.clearColor.apply(gl, this._backgroundColor); gl.clear(gl.COLOR_BUFFER_BIT); @@ -1255,8 +1268,7 @@ class RenderWebGL extends EventEmitter { } /** - * Filter a list of candidates for a touching query into only those that - * could possibly intersect the given bounds. + * Filter a list of candidates for a touching query into only those that could possibly intersect the given bounds. * @param {int} drawableID - ID for drawable of query. * @param {Array} candidateIDs - Candidates for touching query. * @return {?Array< {id, drawable, intersection} >} Filtered candidates with useful data. @@ -1267,8 +1279,7 @@ class RenderWebGL extends EventEmitter { if (bounds === null) { return result; } - // iterate through the drawables list BACKWARDS - we want the top most item to be the first we check - for (let index = candidateIDs.length - 1; index >= 0; index--) { + for (let index = 0; index < candidateIDs.length; ++index) { const id = candidateIDs[index]; if (id !== drawableID) { const drawable = this._allDrawables[id]; @@ -1428,7 +1439,7 @@ class RenderWebGL extends EventEmitter { // Limit size of viewport to the bounds around the stamp Drawable and create the projection matrix for the draw. gl.viewport(0, 0, bounds.width, bounds.height); - const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); + const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom); gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); @@ -1517,7 +1528,7 @@ class RenderWebGL extends EventEmitter { * can skip superfluous extra state calls when it is already in that * region. Since one region may be entered from within another a exit * handle can also be registered that is called when a new region is about - * to be entered to restore a common inbetween state. + * to be entered to restore a common in-between state. * * @param {any} regionId - id of the region to enter * @param {function} enter - handle to call when first entering a region @@ -1649,7 +1660,7 @@ class RenderWebGL extends EventEmitter { * * The determinant is useful in this case to know if AC is counter * clockwise from AB. A positive value means the AC is counter - * clockwise from AC. A negative value menas AC is clockwise from AB. + * clockwise from AC. A negative value means AC is clockwise from AB. * * @param {Float32Array} A A 2d vector in space. * @param {Float32Array} B A 2d vector in space. @@ -1754,8 +1765,7 @@ class RenderWebGL extends EventEmitter { * Sample a "final" color from an array of drawables at a given scratch space. * Will blend any alpha values with the drawables "below" it. * @param {twgl.v3} vec Scratch Vector Space to sample - * @param {Array} drawables A list of drawables with the "top most" - * drawable at index 0 + * @param {Array} drawables A list of drawables with the "bottom most" drawable at index 0 * @param {Uint8ClampedArray} dst The color3b space to store the answer in. * @return {Uint8ClampedArray} The dst vector with everything blended down. */ @@ -1763,7 +1773,7 @@ class RenderWebGL extends EventEmitter { dst = dst || new Uint8ClampedArray(3); dst.fill(0); let blendAlpha = 1; - for (let index = 0; blendAlpha !== 0 && index < drawables.length; index++) { + for (let index = drawables.length - 1; blendAlpha !== 0 && index >= 0; --index) { /* if (left > vec[0] || right < vec[0] || bottom > vec[1] || top < vec[0]) { From aff0019ed22e392fc73aa892ba58dea78faa44c3 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 19 Apr 2019 16:14:10 -0400 Subject: [PATCH 1872/1971] Merge pull request #440 from LLK/revert-419-coordinates-fixups-2 Revert "Adjust CPU `isTouchingColor` to match GPU results (again)" --- packages/scratch-render/src/RenderWebGL.js | 70 ++++++++++------------ 1 file changed, 30 insertions(+), 40 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 9c34172445..05b35e2eaf 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -267,7 +267,8 @@ class RenderWebGL extends EventEmitter { this._yBottom = yBottom; this._yTop = yTop; - this._projection = this._makeOrthoProjection(xLeft, xRight, yBottom, yTop); + // swap yBottom & yTop to fit Scratch convention of +y=up + this._projection = twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1); this._setNativeSize(Math.abs(xRight - xLeft), Math.abs(yBottom - yTop)); } @@ -291,20 +292,6 @@ class RenderWebGL extends EventEmitter { this.emit(RenderConstants.Events.NativeSizeChanged, {newSize: this._nativeSize}); } - /** - * Build a projection matrix for Scratch coordinates. For example, `_makeOrthoProjection(-240,240,-180,180)` will - * mean the lower-left pixel is at (-240,-179) and the upper right pixel is at (239,180), matching Scratch 2.0. - * @param {number} xLeft - the left edge of the projection volume (-240) - * @param {number} xRight - the right edge of the projection volume (240) - * @param {number} yBottom - the bottom edge of the projection volume (-180) - * @param {number} yTop - the top edge of the projection volume (180) - * @returns {module:twgl/m4.Mat4} - a projection matrix containing [xLeft,xRight) and (yBottom,yTop] - */ - _makeOrthoProjection (xLeft, xRight, yBottom, yTop) { - // swap yBottom & yTop to fit Scratch convention of +y=up - return twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1); - } - /** * Create a new bitmap skin from a snapshot of the provided bitmap data. * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin. @@ -542,7 +529,7 @@ class RenderWebGL extends EventEmitter { * Returns the position of the given drawableID in the draw list. This is * the absolute position irrespective of layer group. * @param {number} drawableID The drawable ID to find. - * @return {number} The position of the given drawable ID. + * @return {number} The postion of the given drawable ID. */ getDrawableOrder (drawableID) { return this._drawList.indexOf(drawableID); @@ -556,7 +543,7 @@ class RenderWebGL extends EventEmitter { * "go to back": setDrawableOrder(id, 1); (assuming stage at 0). * "go to front": setDrawableOrder(id, Infinity); * @param {int} drawableID ID of Drawable to reorder. - * @param {number} order New absolute order or relative order adjustment. + * @param {number} order New absolute order or relative order adjusment. * @param {string=} group Name of layer group drawable belongs to. * Reordering will not take place if drawable cannot be found within the bounds * of the layer group. @@ -727,7 +714,7 @@ class RenderWebGL extends EventEmitter { /** * Check if a particular Drawable is touching a particular color. - * Unlike touching drawable, if the "tester" is invisible, we will still test. + * Unlike touching drawable, if the "tester" is invisble, we will still test. * @param {int} drawableID The ID of the Drawable to check. * @param {Array} color3b Test if the Drawable is touching this color. * @param {Array} [mask3b] Optionally mask the check to this part of Drawable. @@ -751,7 +738,7 @@ class RenderWebGL extends EventEmitter { // if there are just too many pixels to CPU render efficiently, we need to let readPixels happen if (bounds.width * bounds.height * (candidates.length + 1) >= maxPixelsForCPU) { - this._isTouchingColorGpuStart(drawableID, candidates.map(({id}) => id), bounds, color3b, mask3b); + this._isTouchingColorGpuStart(drawableID, candidates.map(({id}) => id).reverse(), bounds, color3b, mask3b); } const drawable = this._allDrawables[drawableID]; @@ -760,13 +747,13 @@ class RenderWebGL extends EventEmitter { const hasMask = Boolean(mask3b); // Scratch Space - +y is top - for (let y = 0; y < bounds.height; ++y) { - if (bounds.width * y * (candidates.length + 1) >= maxPixelsForCPU) { - return this._isTouchingColorGpuFin(bounds, color3b, y); + for (let y = bounds.bottom; y <= bounds.top; y++) { + if (bounds.width * (y - bounds.bottom) * (candidates.length + 1) >= maxPixelsForCPU) { + return this._isTouchingColorGpuFin(bounds, color3b, y - bounds.bottom); } - for (let x = 0; x < bounds.width; ++x) { - point[0] = bounds.left + x; // bounds.left <= point[0] < bounds.right - point[1] = bounds.top - y; // bounds.bottom < point[1] <= bounds.top ("flipped") + for (let x = bounds.left; x <= bounds.right; x++) { + point[1] = y; + point[0] = x; // if we use a mask, check our sample color... if (hasMask ? maskMatches(Drawable.sampleColor4b(point, drawable, color), mask3b) : @@ -774,10 +761,10 @@ class RenderWebGL extends EventEmitter { RenderWebGL.sampleColor3b(point, candidates, color); if (debugCanvasContext) { debugCanvasContext.fillStyle = `rgb(${color[0]},${color[1]},${color[2]})`; - debugCanvasContext.fillRect(x, y, 1, 1); + debugCanvasContext.fillRect(x - bounds.left, bounds.bottom - y, 1, 1); } // ...and the target color is drawn at this pixel - if (colorMatches(color3b, color, 0)) { + if (colorMatches(color, color3b, 0)) { return true; } } @@ -807,7 +794,7 @@ class RenderWebGL extends EventEmitter { // Limit size of viewport to the bounds around the target Drawable, // and create the projection matrix for the draw. gl.viewport(0, 0, bounds.width, bounds.height); - const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom); + const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); let fillBackgroundColor = this._backgroundColor; @@ -890,7 +877,7 @@ class RenderWebGL extends EventEmitter { const candidates = this._candidatesTouching(drawableID, // even if passed an invisible drawable, we will NEVER touch it! candidateIDs.filter(id => this._allDrawables[id]._visible)); - // if we are invisible we don't touch anything. + // if we are invisble we don't touch anything. if (candidates.length === 0 || !this._allDrawables[drawableID]._visible) { return false; } @@ -923,7 +910,7 @@ class RenderWebGL extends EventEmitter { /** * Convert a client based x/y position on the canvas to a Scratch 3 world space - * Rectangle. This creates rectangles with a radius to cover selecting multiple + * Rectangle. This creates recangles with a radius to cover selecting multiple * scratch pixels with touch / small render areas. * * @param {int} centerX The client x coordinate of the picking location. @@ -1040,7 +1027,7 @@ class RenderWebGL extends EventEmitter { for (worldPos[0] = bounds.left; worldPos[0] <= bounds.right; worldPos[0]++) { // Check candidates in the reverse order they would have been - // drawn. This will determine what candidate's silhouette pixel + // drawn. This will determine what candiate's silhouette pixel // would have been drawn at the point. for (let d = candidateIDs.length - 1; d >= 0; d--) { const id = candidateIDs[d]; @@ -1124,7 +1111,7 @@ class RenderWebGL extends EventEmitter { // Limit size of viewport to the bounds around the target Drawable, // and create the projection matrix for the draw. gl.viewport(0, 0, bounds.width, bounds.height); - const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom); + const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); @@ -1199,7 +1186,7 @@ class RenderWebGL extends EventEmitter { const pickY = bounds.top - scratchY; gl.viewport(0, 0, bounds.width, bounds.height); - const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom); + const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); gl.clearColor.apply(gl, this._backgroundColor); gl.clear(gl.COLOR_BUFFER_BIT); @@ -1268,7 +1255,8 @@ class RenderWebGL extends EventEmitter { } /** - * Filter a list of candidates for a touching query into only those that could possibly intersect the given bounds. + * Filter a list of candidates for a touching query into only those that + * could possibly intersect the given bounds. * @param {int} drawableID - ID for drawable of query. * @param {Array} candidateIDs - Candidates for touching query. * @return {?Array< {id, drawable, intersection} >} Filtered candidates with useful data. @@ -1279,7 +1267,8 @@ class RenderWebGL extends EventEmitter { if (bounds === null) { return result; } - for (let index = 0; index < candidateIDs.length; ++index) { + // iterate through the drawables list BACKWARDS - we want the top most item to be the first we check + for (let index = candidateIDs.length - 1; index >= 0; index--) { const id = candidateIDs[index]; if (id !== drawableID) { const drawable = this._allDrawables[id]; @@ -1439,7 +1428,7 @@ class RenderWebGL extends EventEmitter { // Limit size of viewport to the bounds around the stamp Drawable and create the projection matrix for the draw. gl.viewport(0, 0, bounds.width, bounds.height); - const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom); + const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); @@ -1528,7 +1517,7 @@ class RenderWebGL extends EventEmitter { * can skip superfluous extra state calls when it is already in that * region. Since one region may be entered from within another a exit * handle can also be registered that is called when a new region is about - * to be entered to restore a common in-between state. + * to be entered to restore a common inbetween state. * * @param {any} regionId - id of the region to enter * @param {function} enter - handle to call when first entering a region @@ -1660,7 +1649,7 @@ class RenderWebGL extends EventEmitter { * * The determinant is useful in this case to know if AC is counter * clockwise from AB. A positive value means the AC is counter - * clockwise from AC. A negative value means AC is clockwise from AB. + * clockwise from AC. A negative value menas AC is clockwise from AB. * * @param {Float32Array} A A 2d vector in space. * @param {Float32Array} B A 2d vector in space. @@ -1765,7 +1754,8 @@ class RenderWebGL extends EventEmitter { * Sample a "final" color from an array of drawables at a given scratch space. * Will blend any alpha values with the drawables "below" it. * @param {twgl.v3} vec Scratch Vector Space to sample - * @param {Array} drawables A list of drawables with the "bottom most" drawable at index 0 + * @param {Array} drawables A list of drawables with the "top most" + * drawable at index 0 * @param {Uint8ClampedArray} dst The color3b space to store the answer in. * @return {Uint8ClampedArray} The dst vector with everything blended down. */ @@ -1773,7 +1763,7 @@ class RenderWebGL extends EventEmitter { dst = dst || new Uint8ClampedArray(3); dst.fill(0); let blendAlpha = 1; - for (let index = drawables.length - 1; blendAlpha !== 0 && index >= 0; --index) { + for (let index = 0; blendAlpha !== 0 && index < drawables.length; index++) { /* if (left > vec[0] || right < vec[0] || bottom > vec[1] || top < vec[0]) { From ed09d3dc4dc447717ec0d249dd71a5d6fbacabcf Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Mon, 29 Apr 2019 17:04:15 -0700 Subject: [PATCH 1873/1971] Merge pull request #418 from peabrainiac/develop pen transparency fix --- packages/scratch-render/src/RenderWebGL.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 05b35e2eaf..f16b02d459 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1620,7 +1620,14 @@ class RenderWebGL extends EventEmitter { } twgl.setUniforms(currentShader, uniforms); - + + /* adjust blend function for this skin */ + if (drawable.skin.hasPremultipliedAlpha){ + gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + } else { + gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + } + twgl.drawBufferInfo(gl, this._bufferInfo, gl.TRIANGLES); } From 99dbae40def1f454aed3dd03cc6088b1da15029e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 24 May 2019 08:54:11 -0400 Subject: [PATCH 1874/1971] Merge pull request #451 from adroitwhiz/canvas-text-bubble Implement canvas-based TextBubbleSkin --- packages/scratch-render/src/RenderWebGL.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index f16b02d459..4782455121 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -10,7 +10,7 @@ const PenSkin = require('./PenSkin'); const RenderConstants = require('./RenderConstants'); const ShaderManager = require('./ShaderManager'); const SVGSkin = require('./SVGSkin'); -const SVGTextBubble = require('./util/svg-text-bubble'); +const TextBubbleSkin = require('./TextBubbleSkin'); const EffectTransform = require('./EffectTransform'); const log = require('./util/log'); @@ -184,8 +184,6 @@ class RenderWebGL extends EventEmitter { /** @type {Array.} */ this._snapshotCallbacks = []; - this._svgTextBubble = new SVGTextBubble(); - this._createGeometry(); this.on(RenderConstants.Events.NativeSizeChanged, this.onNativeSizeChanged); @@ -343,8 +341,11 @@ class RenderWebGL extends EventEmitter { * @returns {!int} the ID for the new skin. */ createTextSkin (type, text, pointsLeft) { - const bubbleSvg = this._svgTextBubble.buildString(type, text, pointsLeft); - return this.createSVGSkin(bubbleSvg, [0, 0]); + const skinId = this._nextSkinId++; + const newSkin = new TextBubbleSkin(skinId, this); + newSkin.setTextBubble(type, text, pointsLeft); + this._allSkins[skinId] = newSkin; + return skinId; } /** @@ -407,8 +408,14 @@ class RenderWebGL extends EventEmitter { * @param {!boolean} pointsLeft - which side the bubble is pointing. */ updateTextSkin (skinId, type, text, pointsLeft) { - const bubbleSvg = this._svgTextBubble.buildString(type, text, pointsLeft); - this.updateSVGSkin(skinId, bubbleSvg, [0, 0]); + if (this._allSkins[skinId] instanceof TextBubbleSkin) { + this._allSkins[skinId].setTextBubble(type, text, pointsLeft); + return; + } + + const newSkin = new TextBubbleSkin(skinId, this); + newSkin.setTextBubble(type, text, pointsLeft); + this._reskin(skinId, newSkin); } From cb22fb33c86e2abec25be529e6f353ef864bccb1 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Mon, 1 Jul 2019 10:31:54 -0700 Subject: [PATCH 1875/1971] Merge pull request #467 from mzgoddard/rect-init-matrix Initialialize AABB Rectangle --- packages/scratch-render/src/RenderWebGL.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 4782455121..ba629eacb3 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -16,6 +16,7 @@ const log = require('./util/log'); const __isTouchingDrawablesPoint = twgl.v3.create(); const __candidatesBounds = new Rectangle(); +const __fenceBounds = new Rectangle(); const __touchingColor = new Uint8ClampedArray(4); const __blendColor = new Uint8ClampedArray(4); @@ -1357,7 +1358,7 @@ class RenderWebGL extends EventEmitter { const dx = x - drawable._position[0]; const dy = y - drawable._position[1]; - const aabb = drawable._skin.getFenceBounds(drawable); + const aabb = drawable._skin.getFenceBounds(drawable, __fenceBounds); const inset = Math.floor(Math.min(aabb.width, aabb.height) / 2); const sx = this._xRight - Math.min(FENCE_WIDTH, inset); @@ -1627,14 +1628,14 @@ class RenderWebGL extends EventEmitter { } twgl.setUniforms(currentShader, uniforms); - + /* adjust blend function for this skin */ if (drawable.skin.hasPremultipliedAlpha){ gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); } else { gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); } - + twgl.drawBufferInfo(gl, this._bufferInfo, gl.TRIANGLES); } From 191d3d61eb771c58184dea6e614fd299c396f701 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Mon, 1 Jul 2019 10:43:11 -0700 Subject: [PATCH 1876/1971] Merge pull request #470 from mzgoddard/skin-alter-push Skin alter push --- packages/scratch-render/src/RenderWebGL.js | 19 +++++++++++ packages/scratch-render/src/SVGSkin.js | 38 ++++++++++++++++------ 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index ba629eacb3..d9f5ae28ec 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -3,6 +3,7 @@ const EventEmitter = require('events'); const hull = require('hull.js'); const twgl = require('twgl.js'); +const Skin = require('./Skin'); const BitmapSkin = require('./BitmapSkin'); const Drawable = require('./Drawable'); const Rectangle = require('./Rectangle'); @@ -291,6 +292,20 @@ class RenderWebGL extends EventEmitter { this.emit(RenderConstants.Events.NativeSizeChanged, {newSize: this._nativeSize}); } + /** + * Notify Drawables whose skin is the skin that changed. + * @param {Skin} skin - the skin that changed. + * @private + */ + _skinWasAltered (skin) { + for (let i = 0; i < this._allDrawables.length; i++) { + const drawable = this._allDrawables[i]; + if (drawable && drawable._skin === skin) { + drawable._skinWasAltered(); + } + } + } + /** * Create a new bitmap skin from a snapshot of the provided bitmap data. * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin. @@ -303,6 +318,7 @@ class RenderWebGL extends EventEmitter { const skinId = this._nextSkinId++; const newSkin = new BitmapSkin(skinId, this); newSkin.setBitmap(bitmapData, costumeResolution, rotationCenter); + newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } @@ -318,6 +334,7 @@ class RenderWebGL extends EventEmitter { const skinId = this._nextSkinId++; const newSkin = new SVGSkin(skinId, this); newSkin.setSVG(svgData, rotationCenter); + newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } @@ -329,6 +346,7 @@ class RenderWebGL extends EventEmitter { createPenSkin () { const skinId = this._nextSkinId++; const newSkin = new PenSkin(skinId, this); + newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } @@ -345,6 +363,7 @@ class RenderWebGL extends EventEmitter { const skinId = this._nextSkinId++; const newSkin = new TextBubbleSkin(skinId, this); newSkin.setTextBubble(type, text, pointsLeft); + newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 90e39081ed..9c10e61db7 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -30,6 +30,24 @@ class SVGSkin extends Skin { /** @type {Number} */ this._maxTextureScale = 0; + + /** + * The natural size, in Scratch units, of this skin. + * @type {Array} + */ + this.size = [0, 0]; + + /** + * The viewbox offset of the svg. + * @type {Array} + */ + this._viewOffset = [0, 0]; + + /** + * The rotation center before offset by _viewOffset. + * @type {Array} + */ + this._rawRotationCenter = [NaN, NaN]; } /** @@ -43,21 +61,17 @@ class SVGSkin extends Skin { super.dispose(); } - /** - * @return {Array} the natural size, in Scratch units, of this skin. - */ - get size () { - return this._svgRenderer.size; - } - /** * Set the origin, in object space, about which this Skin should rotate. * @param {number} x - The x coordinate of the new rotation center. * @param {number} y - The y coordinate of the new rotation center. */ setRotationCenter (x, y) { - const viewOffset = this._svgRenderer.viewOffset; - super.setRotationCenter(x - viewOffset[0], y - viewOffset[1]); + if (x !== this._rawRotationCenter[0] || y !== this._rawRotationCenter[1]) { + this._rawRotationCenter[0] = x; + this._rawRotationCenter[1] = y; + super.setRotationCenter(x - this._viewOffset[0], y - this._viewOffset[1]); + } } /** @@ -134,7 +148,11 @@ class SVGSkin extends Skin { } if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); - this.setRotationCenter.apply(this, rotationCenter); + this.size = this._svgRenderer.size; + this._viewOffset = this._svgRenderer.viewOffset; + // Reset rawRotationCenter when we update viewOffset. + this._rawRotationCenter = [NaN, NaN]; + this.setRotationCenter(rotationCenter[0], rotationCenter[1]); this.emit(Skin.Events.WasAltered); }); } From 3cb7449d6d5e9b49ccea3479cf4cc8a2818ee936 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Tue, 13 Aug 2019 11:38:24 -0400 Subject: [PATCH 1877/1971] Merge pull request #493 from LLK/revert-470-skin-alter-push Revert "Skin alter push" --- packages/scratch-render/src/RenderWebGL.js | 19 ----------- packages/scratch-render/src/SVGSkin.js | 38 ++++++---------------- 2 files changed, 10 insertions(+), 47 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index d9f5ae28ec..ba629eacb3 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -3,7 +3,6 @@ const EventEmitter = require('events'); const hull = require('hull.js'); const twgl = require('twgl.js'); -const Skin = require('./Skin'); const BitmapSkin = require('./BitmapSkin'); const Drawable = require('./Drawable'); const Rectangle = require('./Rectangle'); @@ -292,20 +291,6 @@ class RenderWebGL extends EventEmitter { this.emit(RenderConstants.Events.NativeSizeChanged, {newSize: this._nativeSize}); } - /** - * Notify Drawables whose skin is the skin that changed. - * @param {Skin} skin - the skin that changed. - * @private - */ - _skinWasAltered (skin) { - for (let i = 0; i < this._allDrawables.length; i++) { - const drawable = this._allDrawables[i]; - if (drawable && drawable._skin === skin) { - drawable._skinWasAltered(); - } - } - } - /** * Create a new bitmap skin from a snapshot of the provided bitmap data. * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin. @@ -318,7 +303,6 @@ class RenderWebGL extends EventEmitter { const skinId = this._nextSkinId++; const newSkin = new BitmapSkin(skinId, this); newSkin.setBitmap(bitmapData, costumeResolution, rotationCenter); - newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } @@ -334,7 +318,6 @@ class RenderWebGL extends EventEmitter { const skinId = this._nextSkinId++; const newSkin = new SVGSkin(skinId, this); newSkin.setSVG(svgData, rotationCenter); - newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } @@ -346,7 +329,6 @@ class RenderWebGL extends EventEmitter { createPenSkin () { const skinId = this._nextSkinId++; const newSkin = new PenSkin(skinId, this); - newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } @@ -363,7 +345,6 @@ class RenderWebGL extends EventEmitter { const skinId = this._nextSkinId++; const newSkin = new TextBubbleSkin(skinId, this); newSkin.setTextBubble(type, text, pointsLeft); - newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 9c10e61db7..90e39081ed 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -30,24 +30,6 @@ class SVGSkin extends Skin { /** @type {Number} */ this._maxTextureScale = 0; - - /** - * The natural size, in Scratch units, of this skin. - * @type {Array} - */ - this.size = [0, 0]; - - /** - * The viewbox offset of the svg. - * @type {Array} - */ - this._viewOffset = [0, 0]; - - /** - * The rotation center before offset by _viewOffset. - * @type {Array} - */ - this._rawRotationCenter = [NaN, NaN]; } /** @@ -61,17 +43,21 @@ class SVGSkin extends Skin { super.dispose(); } + /** + * @return {Array} the natural size, in Scratch units, of this skin. + */ + get size () { + return this._svgRenderer.size; + } + /** * Set the origin, in object space, about which this Skin should rotate. * @param {number} x - The x coordinate of the new rotation center. * @param {number} y - The y coordinate of the new rotation center. */ setRotationCenter (x, y) { - if (x !== this._rawRotationCenter[0] || y !== this._rawRotationCenter[1]) { - this._rawRotationCenter[0] = x; - this._rawRotationCenter[1] = y; - super.setRotationCenter(x - this._viewOffset[0], y - this._viewOffset[1]); - } + const viewOffset = this._svgRenderer.viewOffset; + super.setRotationCenter(x - viewOffset[0], y - viewOffset[1]); } /** @@ -148,11 +134,7 @@ class SVGSkin extends Skin { } if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); - this.size = this._svgRenderer.size; - this._viewOffset = this._svgRenderer.viewOffset; - // Reset rawRotationCenter when we update viewOffset. - this._rawRotationCenter = [NaN, NaN]; - this.setRotationCenter(rotationCenter[0], rotationCenter[1]); + this.setRotationCenter.apply(this, rotationCenter); this.emit(Skin.Events.WasAltered); }); } From 9340785c52e7c177e6a1e4f71eb01b21ef9fa620 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Tue, 13 Aug 2019 11:38:41 -0400 Subject: [PATCH 1878/1971] Merge pull request #494 from LLK/revert-467-rect-init-matrix Revert "Initialialize AABB Rectangle " --- packages/scratch-render/src/RenderWebGL.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index ba629eacb3..4782455121 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -16,7 +16,6 @@ const log = require('./util/log'); const __isTouchingDrawablesPoint = twgl.v3.create(); const __candidatesBounds = new Rectangle(); -const __fenceBounds = new Rectangle(); const __touchingColor = new Uint8ClampedArray(4); const __blendColor = new Uint8ClampedArray(4); @@ -1358,7 +1357,7 @@ class RenderWebGL extends EventEmitter { const dx = x - drawable._position[0]; const dy = y - drawable._position[1]; - const aabb = drawable._skin.getFenceBounds(drawable, __fenceBounds); + const aabb = drawable._skin.getFenceBounds(drawable); const inset = Math.floor(Math.min(aabb.width, aabb.height) / 2); const sx = this._xRight - Math.min(FENCE_WIDTH, inset); @@ -1628,14 +1627,14 @@ class RenderWebGL extends EventEmitter { } twgl.setUniforms(currentShader, uniforms); - + /* adjust blend function for this skin */ if (drawable.skin.hasPremultipliedAlpha){ gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); } else { gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); } - + twgl.drawBufferInfo(gl, this._bufferInfo, gl.TRIANGLES); } From 758ceafc715e68c64ea1f58db0fecc03e475f88c Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 30 Aug 2019 11:27:30 -0400 Subject: [PATCH 1879/1971] Merge pull request #502 from fsih/turnOffAntiAlias Turn off antialias --- packages/scratch-render/src/RenderWebGL.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 4782455121..3a2a30205e 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -104,7 +104,7 @@ class RenderWebGL extends EventEmitter { * @private */ static _getContext (canvas) { - return twgl.getWebGLContext(canvas, {alpha: false, stencil: true}); + return twgl.getWebGLContext(canvas, {alpha: false, stencil: true, antialias: false}); } /** From b36ad03a9620bce35f5455b53aff64636f831156 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Fri, 30 Aug 2019 19:51:20 +0100 Subject: [PATCH 1880/1971] Merge pull request #441 from adroitwhiz/region-exit-fix Clear _regionId when exiting draw region --- packages/scratch-render/src/RenderWebGL.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 3a2a30205e..2c87fb4564 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1548,6 +1548,7 @@ class RenderWebGL extends EventEmitter { this._exitRegion(); } this._exitRegion = null; + this._regionId = null; } /** From 10076071c66c41c8e5a0c73de719cf4c701b6087 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Fri, 30 Aug 2019 20:19:26 +0100 Subject: [PATCH 1881/1971] Merge pull request #442 from adroitwhiz/pen-stamp-region-optimization Don't _doExitDrawRegion in penStamp until we're sure we're stamping --- packages/scratch-render/src/RenderWebGL.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 2c87fb4564..618277ee81 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1416,8 +1416,6 @@ class RenderWebGL extends EventEmitter { * @param {int} stampID - the unique ID of the Drawable to use as the stamp. */ penStamp (penSkinID, stampID) { - this._doExitDrawRegion(); - const stampDrawable = this._allDrawables[stampID]; if (!stampDrawable) { return; @@ -1428,6 +1426,8 @@ class RenderWebGL extends EventEmitter { return; } + this._doExitDrawRegion(); + const skin = /** @type {PenSkin} */ this._allSkins[penSkinID]; const gl = this._gl; From 5a67c50d8ca12acfb79eabda44445fdcff9646af Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 30 Aug 2019 15:35:32 -0400 Subject: [PATCH 1882/1971] Merge pull request #495 from LLK/revert-494-revert-467-rect-init-matrix Revert "Revert "Initialialize AABB Rectangle "" --- packages/scratch-render/src/RenderWebGL.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 618277ee81..527723567c 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -16,6 +16,7 @@ const log = require('./util/log'); const __isTouchingDrawablesPoint = twgl.v3.create(); const __candidatesBounds = new Rectangle(); +const __fenceBounds = new Rectangle(); const __touchingColor = new Uint8ClampedArray(4); const __blendColor = new Uint8ClampedArray(4); @@ -1357,7 +1358,7 @@ class RenderWebGL extends EventEmitter { const dx = x - drawable._position[0]; const dy = y - drawable._position[1]; - const aabb = drawable._skin.getFenceBounds(drawable); + const aabb = drawable._skin.getFenceBounds(drawable, __fenceBounds); const inset = Math.floor(Math.min(aabb.width, aabb.height) / 2); const sx = this._xRight - Math.min(FENCE_WIDTH, inset); @@ -1628,14 +1629,14 @@ class RenderWebGL extends EventEmitter { } twgl.setUniforms(currentShader, uniforms); - + /* adjust blend function for this skin */ if (drawable.skin.hasPremultipliedAlpha){ gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); } else { gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); } - + twgl.drawBufferInfo(gl, this._bufferInfo, gl.TRIANGLES); } From e9fb165213ee4bc9bea86b4bd142ee66fa89e628 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 4 Sep 2019 18:14:41 -0700 Subject: [PATCH 1883/1971] Merge pull request #496 from LLK/revert-493-revert-470-skin-alter-push Revert "Revert "Skin alter push"" --- packages/scratch-render/src/RenderWebGL.js | 19 +++++++++ packages/scratch-render/src/SVGSkin.js | 48 ++++++++++++++++------ 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 527723567c..4186b11992 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -3,6 +3,7 @@ const EventEmitter = require('events'); const hull = require('hull.js'); const twgl = require('twgl.js'); +const Skin = require('./Skin'); const BitmapSkin = require('./BitmapSkin'); const Drawable = require('./Drawable'); const Rectangle = require('./Rectangle'); @@ -291,6 +292,20 @@ class RenderWebGL extends EventEmitter { this.emit(RenderConstants.Events.NativeSizeChanged, {newSize: this._nativeSize}); } + /** + * Notify Drawables whose skin is the skin that changed. + * @param {Skin} skin - the skin that changed. + * @private + */ + _skinWasAltered (skin) { + for (let i = 0; i < this._allDrawables.length; i++) { + const drawable = this._allDrawables[i]; + if (drawable && drawable._skin === skin) { + drawable._skinWasAltered(); + } + } + } + /** * Create a new bitmap skin from a snapshot of the provided bitmap data. * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin. @@ -303,6 +318,7 @@ class RenderWebGL extends EventEmitter { const skinId = this._nextSkinId++; const newSkin = new BitmapSkin(skinId, this); newSkin.setBitmap(bitmapData, costumeResolution, rotationCenter); + newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } @@ -318,6 +334,7 @@ class RenderWebGL extends EventEmitter { const skinId = this._nextSkinId++; const newSkin = new SVGSkin(skinId, this); newSkin.setSVG(svgData, rotationCenter); + newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } @@ -329,6 +346,7 @@ class RenderWebGL extends EventEmitter { createPenSkin () { const skinId = this._nextSkinId++; const newSkin = new PenSkin(skinId, this); + newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } @@ -345,6 +363,7 @@ class RenderWebGL extends EventEmitter { const skinId = this._nextSkinId++; const newSkin = new TextBubbleSkin(skinId, this); newSkin.setTextBubble(type, text, pointsLeft); + newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 90e39081ed..90852ca560 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -30,6 +30,24 @@ class SVGSkin extends Skin { /** @type {Number} */ this._maxTextureScale = 0; + + /** + * The natural size, in Scratch units, of this skin. + * @type {Array} + */ + this.size = [0, 0]; + + /** + * The viewbox offset of the svg. + * @type {Array} + */ + this._viewOffset = [0, 0]; + + /** + * The rotation center before offset by _viewOffset. + * @type {Array} + */ + this._rawRotationCenter = [NaN, NaN]; } /** @@ -43,21 +61,17 @@ class SVGSkin extends Skin { super.dispose(); } - /** - * @return {Array} the natural size, in Scratch units, of this skin. - */ - get size () { - return this._svgRenderer.size; - } - /** * Set the origin, in object space, about which this Skin should rotate. * @param {number} x - The x coordinate of the new rotation center. * @param {number} y - The y coordinate of the new rotation center. */ setRotationCenter (x, y) { - const viewOffset = this._svgRenderer.viewOffset; - super.setRotationCenter(x - viewOffset[0], y - viewOffset[1]); + if (x !== this._rawRotationCenter[0] || y !== this._rawRotationCenter[1]) { + this._rawRotationCenter[0] = x; + this._rawRotationCenter[1] = y; + super.setRotationCenter(x - this._viewOffset[0], y - this._viewOffset[1]); + } } /** @@ -100,7 +114,19 @@ class SVGSkin extends Skin { * @fires Skin.event:WasAltered */ setSVG (svgData, rotationCenter) { - this._svgRenderer.fromString(svgData, 1, () => { + this._svgRenderer.loadString(svgData); + + // Size must be updated synchronously because the VM sets the costume's `size` immediately after calling this. + // TODO: add either a callback to this function so the costume size can be set after this is done, + // or something in the VM to handle setting the costume size when Skin.Events.WasAltered is emitted. + this.size = this._svgRenderer.size; + if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); + this._viewOffset = this._svgRenderer.viewOffset; + // Reset rawRotationCenter when we update viewOffset. + this._rawRotationCenter = [NaN, NaN]; + this.setRotationCenter(rotationCenter[0], rotationCenter[1]); + + this._svgRenderer._draw(1, () => { const gl = this._renderer.gl; this._textureScale = this._maxTextureScale = 1; @@ -133,8 +159,6 @@ class SVGSkin extends Skin { this._maxTextureScale = testScale; } - if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); - this.setRotationCenter.apply(this, rotationCenter); this.emit(Skin.Events.WasAltered); }); } From 68d7f87772970b671ff1c095c567d74d00b3cef7 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 12 Sep 2019 13:42:23 -0400 Subject: [PATCH 1884/1971] Merge pull request #505 from LLK/revert-496-revert-493-revert-470-skin-alter-push Revert "Revert "Revert "Skin alter push""" --- packages/scratch-render/src/RenderWebGL.js | 19 --------- packages/scratch-render/src/SVGSkin.js | 48 ++++++---------------- 2 files changed, 12 insertions(+), 55 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 4186b11992..527723567c 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -3,7 +3,6 @@ const EventEmitter = require('events'); const hull = require('hull.js'); const twgl = require('twgl.js'); -const Skin = require('./Skin'); const BitmapSkin = require('./BitmapSkin'); const Drawable = require('./Drawable'); const Rectangle = require('./Rectangle'); @@ -292,20 +291,6 @@ class RenderWebGL extends EventEmitter { this.emit(RenderConstants.Events.NativeSizeChanged, {newSize: this._nativeSize}); } - /** - * Notify Drawables whose skin is the skin that changed. - * @param {Skin} skin - the skin that changed. - * @private - */ - _skinWasAltered (skin) { - for (let i = 0; i < this._allDrawables.length; i++) { - const drawable = this._allDrawables[i]; - if (drawable && drawable._skin === skin) { - drawable._skinWasAltered(); - } - } - } - /** * Create a new bitmap skin from a snapshot of the provided bitmap data. * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin. @@ -318,7 +303,6 @@ class RenderWebGL extends EventEmitter { const skinId = this._nextSkinId++; const newSkin = new BitmapSkin(skinId, this); newSkin.setBitmap(bitmapData, costumeResolution, rotationCenter); - newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } @@ -334,7 +318,6 @@ class RenderWebGL extends EventEmitter { const skinId = this._nextSkinId++; const newSkin = new SVGSkin(skinId, this); newSkin.setSVG(svgData, rotationCenter); - newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } @@ -346,7 +329,6 @@ class RenderWebGL extends EventEmitter { createPenSkin () { const skinId = this._nextSkinId++; const newSkin = new PenSkin(skinId, this); - newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } @@ -363,7 +345,6 @@ class RenderWebGL extends EventEmitter { const skinId = this._nextSkinId++; const newSkin = new TextBubbleSkin(skinId, this); newSkin.setTextBubble(type, text, pointsLeft); - newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 90852ca560..90e39081ed 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -30,24 +30,6 @@ class SVGSkin extends Skin { /** @type {Number} */ this._maxTextureScale = 0; - - /** - * The natural size, in Scratch units, of this skin. - * @type {Array} - */ - this.size = [0, 0]; - - /** - * The viewbox offset of the svg. - * @type {Array} - */ - this._viewOffset = [0, 0]; - - /** - * The rotation center before offset by _viewOffset. - * @type {Array} - */ - this._rawRotationCenter = [NaN, NaN]; } /** @@ -61,17 +43,21 @@ class SVGSkin extends Skin { super.dispose(); } + /** + * @return {Array} the natural size, in Scratch units, of this skin. + */ + get size () { + return this._svgRenderer.size; + } + /** * Set the origin, in object space, about which this Skin should rotate. * @param {number} x - The x coordinate of the new rotation center. * @param {number} y - The y coordinate of the new rotation center. */ setRotationCenter (x, y) { - if (x !== this._rawRotationCenter[0] || y !== this._rawRotationCenter[1]) { - this._rawRotationCenter[0] = x; - this._rawRotationCenter[1] = y; - super.setRotationCenter(x - this._viewOffset[0], y - this._viewOffset[1]); - } + const viewOffset = this._svgRenderer.viewOffset; + super.setRotationCenter(x - viewOffset[0], y - viewOffset[1]); } /** @@ -114,19 +100,7 @@ class SVGSkin extends Skin { * @fires Skin.event:WasAltered */ setSVG (svgData, rotationCenter) { - this._svgRenderer.loadString(svgData); - - // Size must be updated synchronously because the VM sets the costume's `size` immediately after calling this. - // TODO: add either a callback to this function so the costume size can be set after this is done, - // or something in the VM to handle setting the costume size when Skin.Events.WasAltered is emitted. - this.size = this._svgRenderer.size; - if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); - this._viewOffset = this._svgRenderer.viewOffset; - // Reset rawRotationCenter when we update viewOffset. - this._rawRotationCenter = [NaN, NaN]; - this.setRotationCenter(rotationCenter[0], rotationCenter[1]); - - this._svgRenderer._draw(1, () => { + this._svgRenderer.fromString(svgData, 1, () => { const gl = this._renderer.gl; this._textureScale = this._maxTextureScale = 1; @@ -159,6 +133,8 @@ class SVGSkin extends Skin { this._maxTextureScale = testScale; } + if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); + this.setRotationCenter.apply(this, rotationCenter); this.emit(Skin.Events.WasAltered); }); } From 3a6a5b2f4f0c47c25dce986b375513a094b2033c Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Wed, 25 Sep 2019 13:16:09 -0700 Subject: [PATCH 1885/1971] Merge pull request #506 from LLK/revert-505-revert-496-revert-493-revert-470-skin-alter-push Put Skin Alter Push Back In --- packages/scratch-render/src/RenderWebGL.js | 19 +++++++++ packages/scratch-render/src/SVGSkin.js | 47 +++++++++++++++++----- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 527723567c..4186b11992 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -3,6 +3,7 @@ const EventEmitter = require('events'); const hull = require('hull.js'); const twgl = require('twgl.js'); +const Skin = require('./Skin'); const BitmapSkin = require('./BitmapSkin'); const Drawable = require('./Drawable'); const Rectangle = require('./Rectangle'); @@ -291,6 +292,20 @@ class RenderWebGL extends EventEmitter { this.emit(RenderConstants.Events.NativeSizeChanged, {newSize: this._nativeSize}); } + /** + * Notify Drawables whose skin is the skin that changed. + * @param {Skin} skin - the skin that changed. + * @private + */ + _skinWasAltered (skin) { + for (let i = 0; i < this._allDrawables.length; i++) { + const drawable = this._allDrawables[i]; + if (drawable && drawable._skin === skin) { + drawable._skinWasAltered(); + } + } + } + /** * Create a new bitmap skin from a snapshot of the provided bitmap data. * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin. @@ -303,6 +318,7 @@ class RenderWebGL extends EventEmitter { const skinId = this._nextSkinId++; const newSkin = new BitmapSkin(skinId, this); newSkin.setBitmap(bitmapData, costumeResolution, rotationCenter); + newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } @@ -318,6 +334,7 @@ class RenderWebGL extends EventEmitter { const skinId = this._nextSkinId++; const newSkin = new SVGSkin(skinId, this); newSkin.setSVG(svgData, rotationCenter); + newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } @@ -329,6 +346,7 @@ class RenderWebGL extends EventEmitter { createPenSkin () { const skinId = this._nextSkinId++; const newSkin = new PenSkin(skinId, this); + newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } @@ -345,6 +363,7 @@ class RenderWebGL extends EventEmitter { const skinId = this._nextSkinId++; const newSkin = new TextBubbleSkin(skinId, this); newSkin.setTextBubble(type, text, pointsLeft); + newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 90e39081ed..7f490900fb 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -30,6 +30,24 @@ class SVGSkin extends Skin { /** @type {Number} */ this._maxTextureScale = 0; + + /** + * The natural size, in Scratch units, of this skin. + * @type {Array} + */ + this.size = [0, 0]; + + /** + * The viewbox offset of the svg. + * @type {Array} + */ + this._viewOffset = [0, 0]; + + /** + * The rotation center before offset by _viewOffset. + * @type {Array} + */ + this._rawRotationCenter = [NaN, NaN]; } /** @@ -43,21 +61,17 @@ class SVGSkin extends Skin { super.dispose(); } - /** - * @return {Array} the natural size, in Scratch units, of this skin. - */ - get size () { - return this._svgRenderer.size; - } - /** * Set the origin, in object space, about which this Skin should rotate. * @param {number} x - The x coordinate of the new rotation center. * @param {number} y - The y coordinate of the new rotation center. */ setRotationCenter (x, y) { - const viewOffset = this._svgRenderer.viewOffset; - super.setRotationCenter(x - viewOffset[0], y - viewOffset[1]); + if (x !== this._rawRotationCenter[0] || y !== this._rawRotationCenter[1]) { + this._rawRotationCenter[0] = x; + this._rawRotationCenter[1] = y; + super.setRotationCenter(x - this._viewOffset[0], y - this._viewOffset[1]); + } } /** @@ -100,7 +114,17 @@ class SVGSkin extends Skin { * @fires Skin.event:WasAltered */ setSVG (svgData, rotationCenter) { - this._svgRenderer.fromString(svgData, 1, () => { + this._svgRenderer.loadString(svgData); + + // Size must be updated synchronously because the VM sets the costume's + // `size` immediately after calling this. + this.size = this._svgRenderer.size; + this._viewOffset = this._svgRenderer.viewOffset; + // Reset rawRotationCenter when we update viewOffset. The rotation + // center used to render will be updated later. + this._rawRotationCenter = [NaN, NaN]; + + this._svgRenderer._draw(1, () => { const gl = this._renderer.gl; this._textureScale = this._maxTextureScale = 1; @@ -134,7 +158,8 @@ class SVGSkin extends Skin { } if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); - this.setRotationCenter.apply(this, rotationCenter); + this.setRotationCenter(rotationCenter[0], rotationCenter[1]); + this.emit(Skin.Events.WasAltered); }); } From 9d844890b1fb58746c3f0cfbd7ce32eedce31ae8 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 26 Sep 2019 09:57:26 -0500 Subject: [PATCH 1886/1971] Merge pull request #508 from LLK/revert-506-revert-505-revert-496-revert-493-revert-470-skin-alter-push Revert "Put Skin Alter Push Back In" --- packages/scratch-render/src/RenderWebGL.js | 19 --------- packages/scratch-render/src/SVGSkin.js | 47 +++++----------------- 2 files changed, 11 insertions(+), 55 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 4186b11992..527723567c 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -3,7 +3,6 @@ const EventEmitter = require('events'); const hull = require('hull.js'); const twgl = require('twgl.js'); -const Skin = require('./Skin'); const BitmapSkin = require('./BitmapSkin'); const Drawable = require('./Drawable'); const Rectangle = require('./Rectangle'); @@ -292,20 +291,6 @@ class RenderWebGL extends EventEmitter { this.emit(RenderConstants.Events.NativeSizeChanged, {newSize: this._nativeSize}); } - /** - * Notify Drawables whose skin is the skin that changed. - * @param {Skin} skin - the skin that changed. - * @private - */ - _skinWasAltered (skin) { - for (let i = 0; i < this._allDrawables.length; i++) { - const drawable = this._allDrawables[i]; - if (drawable && drawable._skin === skin) { - drawable._skinWasAltered(); - } - } - } - /** * Create a new bitmap skin from a snapshot of the provided bitmap data. * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin. @@ -318,7 +303,6 @@ class RenderWebGL extends EventEmitter { const skinId = this._nextSkinId++; const newSkin = new BitmapSkin(skinId, this); newSkin.setBitmap(bitmapData, costumeResolution, rotationCenter); - newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } @@ -334,7 +318,6 @@ class RenderWebGL extends EventEmitter { const skinId = this._nextSkinId++; const newSkin = new SVGSkin(skinId, this); newSkin.setSVG(svgData, rotationCenter); - newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } @@ -346,7 +329,6 @@ class RenderWebGL extends EventEmitter { createPenSkin () { const skinId = this._nextSkinId++; const newSkin = new PenSkin(skinId, this); - newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } @@ -363,7 +345,6 @@ class RenderWebGL extends EventEmitter { const skinId = this._nextSkinId++; const newSkin = new TextBubbleSkin(skinId, this); newSkin.setTextBubble(type, text, pointsLeft); - newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin)); this._allSkins[skinId] = newSkin; return skinId; } diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 7f490900fb..90e39081ed 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -30,24 +30,6 @@ class SVGSkin extends Skin { /** @type {Number} */ this._maxTextureScale = 0; - - /** - * The natural size, in Scratch units, of this skin. - * @type {Array} - */ - this.size = [0, 0]; - - /** - * The viewbox offset of the svg. - * @type {Array} - */ - this._viewOffset = [0, 0]; - - /** - * The rotation center before offset by _viewOffset. - * @type {Array} - */ - this._rawRotationCenter = [NaN, NaN]; } /** @@ -61,17 +43,21 @@ class SVGSkin extends Skin { super.dispose(); } + /** + * @return {Array} the natural size, in Scratch units, of this skin. + */ + get size () { + return this._svgRenderer.size; + } + /** * Set the origin, in object space, about which this Skin should rotate. * @param {number} x - The x coordinate of the new rotation center. * @param {number} y - The y coordinate of the new rotation center. */ setRotationCenter (x, y) { - if (x !== this._rawRotationCenter[0] || y !== this._rawRotationCenter[1]) { - this._rawRotationCenter[0] = x; - this._rawRotationCenter[1] = y; - super.setRotationCenter(x - this._viewOffset[0], y - this._viewOffset[1]); - } + const viewOffset = this._svgRenderer.viewOffset; + super.setRotationCenter(x - viewOffset[0], y - viewOffset[1]); } /** @@ -114,17 +100,7 @@ class SVGSkin extends Skin { * @fires Skin.event:WasAltered */ setSVG (svgData, rotationCenter) { - this._svgRenderer.loadString(svgData); - - // Size must be updated synchronously because the VM sets the costume's - // `size` immediately after calling this. - this.size = this._svgRenderer.size; - this._viewOffset = this._svgRenderer.viewOffset; - // Reset rawRotationCenter when we update viewOffset. The rotation - // center used to render will be updated later. - this._rawRotationCenter = [NaN, NaN]; - - this._svgRenderer._draw(1, () => { + this._svgRenderer.fromString(svgData, 1, () => { const gl = this._renderer.gl; this._textureScale = this._maxTextureScale = 1; @@ -158,8 +134,7 @@ class SVGSkin extends Skin { } if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); - this.setRotationCenter(rotationCenter[0], rotationCenter[1]); - + this.setRotationCenter.apply(this, rotationCenter); this.emit(Skin.Events.WasAltered); }); } From 00ddf3f95004dd846e5551e62810f75b6d82a0d0 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 1 Oct 2019 15:39:50 -0400 Subject: [PATCH 1887/1971] Merge pull request #469 from mzgoddard/update-drawable Add update drawable methods for each property --- packages/scratch-render/src/RenderWebGL.js | 122 ++++++++++++++++++++- 1 file changed, 117 insertions(+), 5 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 527723567c..26c4c2a318 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1314,9 +1314,122 @@ class RenderWebGL extends EventEmitter { }, null); } + /** + * Update a drawable's skin. + * @param {number} drawableID The drawable's id. + * @param {number} skinId The skin to update to. + */ + updateDrawableSkinId (drawableID, skinId) { + const drawable = this._allDrawables[drawableID]; + // TODO: https://github.com/LLK/scratch-vm/issues/2288 + if (!drawable) return; + drawable.skin = this._allSkins[skinId]; + } + + /** + * Update a drawable's skin rotation center. + * @param {number} drawableID The drawable's id. + * @param {Array.} rotationCenter The rotation center for the skin. + */ + updateDrawableRotationCenter (drawableID, rotationCenter) { + const drawable = this._allDrawables[drawableID]; + // TODO: https://github.com/LLK/scratch-vm/issues/2288 + if (!drawable) return; + drawable.skin.setRotationCenter(rotationCenter[0], rotationCenter[1]); + } + + /** + * Update a drawable's skin and rotation center together. + * @param {number} drawableID The drawable's id. + * @param {number} skinId The skin to update to. + * @param {Array.} rotationCenter The rotation center for the skin. + */ + updateDrawableSkinIdRotationCenter (drawableID, skinId, rotationCenter) { + const drawable = this._allDrawables[drawableID]; + // TODO: https://github.com/LLK/scratch-vm/issues/2288 + if (!drawable) return; + drawable.skin = this._allSkins[skinId]; + drawable.skin.setRotationCenter(rotationCenter[0], rotationCenter[1]); + } + + /** + * Update a drawable's position. + * @param {number} drawableID The drawable's id. + * @param {Array.} position The new position. + */ + updateDrawablePosition (drawableID, position) { + const drawable = this._allDrawables[drawableID]; + // TODO: https://github.com/LLK/scratch-vm/issues/2288 + if (!drawable) return; + drawable.updatePosition(position); + } + + /** + * Update a drawable's direction. + * @param {number} drawableID The drawable's id. + * @param {number} direction A new direction. + */ + updateDrawableDirection (drawableID, direction) { + const drawable = this._allDrawables[drawableID]; + // TODO: https://github.com/LLK/scratch-vm/issues/2288 + if (!drawable) return; + drawable.updateDirection(direction); + } + + /** + * Update a drawable's scale. + * @param {number} drawableID The drawable's id. + * @param {Array.} scale A new scale. + */ + updateDrawableScale (drawableID, scale) { + const drawable = this._allDrawables[drawableID]; + // TODO: https://github.com/LLK/scratch-vm/issues/2288 + if (!drawable) return; + drawable.updateScale(scale); + } + + /** + * Update a drawable's direction and scale together. + * @param {number} drawableID The drawable's id. + * @param {number} direction A new direction. + * @param {Array.} scale A new scale. + */ + updateDrawableDirectionScale (drawableID, direction, scale) { + const drawable = this._allDrawables[drawableID]; + // TODO: https://github.com/LLK/scratch-vm/issues/2288 + if (!drawable) return; + drawable.updateDirection(direction); + drawable.updateScale(scale); + } + + /** + * Update a drawable's visibility. + * @param {number} drawableID The drawable's id. + * @param {boolean} visible Will the drawable be visible? + */ + updateDrawableVisible (drawableID, visible) { + const drawable = this._allDrawables[drawableID]; + // TODO: https://github.com/LLK/scratch-vm/issues/2288 + if (!drawable) return; + drawable.updateVisible(visible); + } + + /** + * Update a drawable's visual effect. + * @param {number} drawableID The drawable's id. + * @param {string} effectName The effect to change. + * @param {number} value A new effect value. + */ + updateDrawableEffect (drawableID, effectName, value) { + const drawable = this._allDrawables[drawableID]; + // TODO: https://github.com/LLK/scratch-vm/issues/2288 + if (!drawable) return; + drawable.updateEffect(effectName, value); + } /** * Update the position, direction, scale, or effect properties of this Drawable. + * @deprecated Use specific updateDrawable* methods instead. * @param {int} drawableID The ID of the Drawable to update. * @param {object.} properties The new property values to set. */ @@ -1324,17 +1437,16 @@ class RenderWebGL extends EventEmitter { const drawable = this._allDrawables[drawableID]; if (!drawable) { /** - * @todo fix whatever's wrong in the VM which causes this, then add a warning or throw here. + * @todo(https://github.com/LLK/scratch-vm/issues/2288) fix whatever's wrong in the VM which causes this, then add a warning or throw here. * Right now this happens so much on some projects that a warning or exception here can hang the browser. */ return; } if ('skinId' in properties) { - drawable.skin = this._allSkins[properties.skinId]; + this.updateDrawableSkinId(drawableID, properties.skinId); } if ('rotationCenter' in properties) { - const newRotationCenter = properties.rotationCenter; - drawable.skin.setRotationCenter(newRotationCenter[0], newRotationCenter[1]); + this.updateDrawableRotationCenter(drawableID, properties.rotationCenter); } drawable.updateProperties(properties); } @@ -1351,7 +1463,7 @@ class RenderWebGL extends EventEmitter { const drawable = this._allDrawables[drawableID]; if (!drawable) { - // TODO: fix whatever's wrong in the VM which causes this, then add a warning or throw here. + // @todo(https://github.com/LLK/scratch-vm/issues/2288) fix whatever's wrong in the VM which causes this, then add a warning or throw here. // Right now this happens so much on some projects that a warning or exception here can hang the browser. return [x, y]; } From ad11b877cc33a8b8d092b1d481c0698d7aa87a09 Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Tue, 1 Oct 2019 13:02:15 -0700 Subject: [PATCH 1888/1971] Merge pull request #491 from adroitwhiz/dont-fudge-up-the-renderer Remove u_fudge --- packages/scratch-render/src/RenderWebGL.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 26c4c2a318..40b552a9ca 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1720,8 +1720,7 @@ class RenderWebGL extends EventEmitter { gl.useProgram(currentShader.program); twgl.setBuffersAndAttributes(gl, currentShader, this._bufferInfo); Object.assign(uniforms, { - u_projectionMatrix: projection, - u_fudge: window.fudge || 0 + u_projectionMatrix: projection }); } From c3eb5b75fe4c72d892fa37d817d22d4a89be6d40 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 21 Oct 2019 14:00:04 -0400 Subject: [PATCH 1889/1971] Merge pull request #512 from kchadha/skin-empty-image-data Add Support for Empty Skins --- packages/scratch-render/src/SVGSkin.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 90e39081ed..8b3780b580 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -66,6 +66,10 @@ class SVGSkin extends Skin { */ // eslint-disable-next-line no-unused-vars getTexture (scale) { + if (!this._svgRenderer.canvas.width || !this._svgRenderer.canvas.height) { + return super.getTexture(); + } + // The texture only ever gets uniform scale. Take the larger of the two axes. const scaleMax = scale ? Math.max(Math.abs(scale[0]), Math.abs(scale[1])) : 100; const requestedScale = Math.min(scaleMax / 100, this._maxTextureScale); @@ -108,6 +112,12 @@ class SVGSkin extends Skin { // updating Silhouette and is better handled by more browsers in // regards to memory. const canvas = this._svgRenderer.canvas; + + if (!canvas.width || !canvas.height) { + super.setEmptyImageData(); + return; + } + const context = canvas.getContext('2d'); const textureData = context.getImageData(0, 0, canvas.width, canvas.height); From ff0231235701ef64b866e0cc635d9593d10d8cee Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 6 Nov 2019 11:36:38 -0500 Subject: [PATCH 1890/1971] Merge pull request #514 from adroitwhiz/simplify-reskin Remove setConvexHullDirty and setTransformDirty calls from _reskin --- packages/scratch-render/src/RenderWebGL.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 40b552a9ca..717265e48d 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -394,8 +394,6 @@ class RenderWebGL extends EventEmitter { for (const drawable of this._allDrawables) { if (drawable && drawable.skin === oldSkin) { drawable.skin = newSkin; - drawable.setConvexHullDirty(); - drawable.setTransformDirty(); } } oldSkin.dispose(); From f50ea2bfe787050bc2867fdc2269e9961b46f0a0 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 6 Nov 2019 15:43:06 -0500 Subject: [PATCH 1891/1971] Merge pull request #480 from adroitwhiz/usenearest-fix-2 Fix useNearest() to take renderer scale into account, take two --- packages/scratch-render/src/RenderWebGL.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 717265e48d..6d047f4648 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1733,7 +1733,7 @@ class RenderWebGL extends EventEmitter { if (uniforms.u_skin) { twgl.setTextureParameters( - gl, uniforms.u_skin, {minMag: drawable.useNearest ? gl.NEAREST : gl.LINEAR} + gl, uniforms.u_skin, {minMag: drawable.useNearest(drawableScale) ? gl.NEAREST : gl.LINEAR} ); } From 7014ebe034d18a36f74620f6338023ec9ed7cc2d Mon Sep 17 00:00:00 2001 From: Chris Willis-Ford Date: Tue, 26 Nov 2019 12:57:51 -0800 Subject: [PATCH 1892/1971] Merge pull request #431 from ktbee/javascript-scaled-textures Add JavaScript MIPs for scaling textures larger and smaller --- packages/scratch-render/src/SVGSkin.js | 165 ++++++++++++++----------- 1 file changed, 96 insertions(+), 69 deletions(-) diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 8b3780b580..d33425844c 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -1,9 +1,16 @@ -const twgl = require('twgl.js'); - const Skin = require('./Skin'); +const SVGMIP = require('./SVGMIP'); const SvgRenderer = require('scratch-svg-renderer').SVGRenderer; const MAX_TEXTURE_DIMENSION = 2048; +const MIN_TEXTURE_SCALE = 1 / 256; +/** + * All scaled renderings of the SVG are stored in an array. The 1.0 scale of + * the SVG is stored at the 8th index. The smallest possible 1 / 256 scale + * rendering is stored at the 0th index. + * @const {number} + */ +const INDEX_OFFSET = 8; class SVGSkin extends Skin { /** @@ -25,11 +32,14 @@ class SVGSkin extends Skin { /** @type {WebGLTexture} */ this._texture = null; - /** @type {number} */ - this._textureScale = 1; + /** @type {Array.} */ + this._scaledMIPs = []; - /** @type {Number} */ - this._maxTextureScale = 0; + /** + * Ratio of the size of the SVG and the max size of the WebGL texture + * @type {Number} + */ + this._maxTextureScale = 1; } /** @@ -37,8 +47,13 @@ class SVGSkin extends Skin { */ dispose () { if (this._texture) { - this._renderer.gl.deleteTexture(this._texture); + for (const mip of this._scaledMIPs) { + if (mip) { + mip.dispose(); + } + } this._texture = null; + this._scaledMIPs.length = 0; } super.dispose(); } @@ -60,11 +75,33 @@ class SVGSkin extends Skin { super.setRotationCenter(x - viewOffset[0], y - viewOffset[1]); } + /** + * Create a MIP for a given scale and pass it a callback for updating + * state when switching between scales and MIPs. + * @param {number} scale - The relative size of the MIP + * @param {function} resetCallback - this is a callback for doing a hard reset + * of MIPs and a reset of the rotation center. Only passed in if the MIP scale is 1. + * @return {SVGMIP} An object that handles creating and updating SVG textures. + */ + createMIP (scale, resetCallback) { + const textureCallback = textureData => { + if (resetCallback) resetCallback(); + // Check if we have the largest MIP + // eslint-disable-next-line no-use-before-define + if (!this._scaledMIPs.length || this._scaledMIPs[this._scaledMIPs.length - 1]._scale <= scale) { + // Currently silhouette only gets scaled up + this._silhouette.update(textureData); + } + }; + const mip = new SVGMIP(this._renderer, this._svgRenderer, scale, textureCallback); + + return mip; + } + /** * @param {Array} scale - The scaling factors to be used, each in the [0,100] range. * @return {WebGLTexture} The GL texture representation of this skin when drawing at the given scale. */ - // eslint-disable-next-line no-unused-vars getTexture (scale) { if (!this._svgRenderer.canvas.width || !this._svgRenderer.canvas.height) { return super.getTexture(); @@ -73,80 +110,70 @@ class SVGSkin extends Skin { // The texture only ever gets uniform scale. Take the larger of the two axes. const scaleMax = scale ? Math.max(Math.abs(scale[0]), Math.abs(scale[1])) : 100; const requestedScale = Math.min(scaleMax / 100, this._maxTextureScale); - let newScale = this._textureScale; - while ((newScale < this._maxTextureScale) && (requestedScale >= 1.5 * newScale)) { - newScale *= 2; + let newScale = 1; + let textureIndex = 0; + + if (requestedScale < 1) { + while ((newScale > MIN_TEXTURE_SCALE) && (requestedScale <= newScale * .75)) { + newScale /= 2; + textureIndex -= 1; + } + } else { + while ((newScale < this._maxTextureScale) && (requestedScale >= 1.5 * newScale)) { + newScale *= 2; + textureIndex += 1; + } } - if (this._textureScale !== newScale) { - this._textureScale = newScale; - this._svgRenderer._draw(this._textureScale, () => { - if (this._textureScale === newScale) { - const canvas = this._svgRenderer.canvas; - const context = canvas.getContext('2d'); - const textureData = context.getImageData(0, 0, canvas.width, canvas.height); - - const gl = this._renderer.gl; - gl.bindTexture(gl.TEXTURE_2D, this._texture); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureData); - this._silhouette.update(textureData); - } - }); + + if (!this._scaledMIPs[textureIndex + INDEX_OFFSET]) { + this._scaledMIPs[textureIndex + INDEX_OFFSET] = this.createMIP(newScale); } - return this._texture; + return this._scaledMIPs[textureIndex + INDEX_OFFSET].getTexture(); } /** - * Set the contents of this skin to a snapshot of the provided SVG data. - * @param {string} svgData - new SVG to use. + * Do a hard reset of the existing MIPs by calling dispose(), setting a new + * scale 1 MIP in this._scaledMIPs, and finally updating the rotationCenter. + * @param {SVGMIPs} mip - An object that handles creating and updating SVG textures. * @param {Array} [rotationCenter] - Optional rotation center for the SVG. If not supplied, it will be * calculated from the bounding box - * @fires Skin.event:WasAltered + * @fires Skin.event:WasAltered */ - setSVG (svgData, rotationCenter) { - this._svgRenderer.fromString(svgData, 1, () => { - const gl = this._renderer.gl; - this._textureScale = this._maxTextureScale = 1; - - // Pull out the ImageData from the canvas. ImageData speeds up - // updating Silhouette and is better handled by more browsers in - // regards to memory. - const canvas = this._svgRenderer.canvas; - - if (!canvas.width || !canvas.height) { - super.setEmptyImageData(); - return; - } + resetMIPs (mip, rotationCenter) { + this._scaledMIPs.forEach(oldMIP => oldMIP.dispose()); + this._scaledMIPs.length = 0; - const context = canvas.getContext('2d'); - const textureData = context.getImageData(0, 0, canvas.width, canvas.height); + // Set new scale 1 MIP after outdated MIPs have been disposed + this._texture = this._scaledMIPs[INDEX_OFFSET] = mip; - if (this._texture) { - gl.bindTexture(gl.TEXTURE_2D, this._texture); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureData); - this._silhouette.update(textureData); - } else { - // TODO: mipmaps? - const textureOptions = { - auto: true, - wrap: gl.CLAMP_TO_EDGE, - src: textureData - }; - - this._texture = twgl.createTexture(gl, textureOptions); - this._silhouette.update(textureData); - } + if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); + this.setRotationCenter.apply(this, rotationCenter); + this.emit(Skin.Events.WasAltered); + } - const maxDimension = Math.max(this._svgRenderer.canvas.width, this._svgRenderer.canvas.height); - let testScale = 2; - for (testScale; maxDimension * testScale <= MAX_TEXTURE_DIMENSION; testScale *= 2) { - this._maxTextureScale = testScale; - } + /** + * Set the contents of this skin to a snapshot of the provided SVG data. + * @param {string} svgData - new SVG to use. + * @param {Array} [rotationCenter] - Optional rotation center for the SVG. + */ + setSVG (svgData, rotationCenter) { + this._svgRenderer.loadString(svgData); + + if (!this._svgRenderer.canvas.width || !this._svgRenderer.canvas.height) { + super.setEmptyImageData(); + return; + } + + const maxDimension = Math.ceil(Math.max(this.size[0], this.size[1])); + let testScale = 2; + for (testScale; maxDimension * testScale <= MAX_TEXTURE_DIMENSION; testScale *= 2) { + this._maxTextureScale = testScale; + } - if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); - this.setRotationCenter.apply(this, rotationCenter); - this.emit(Skin.Events.WasAltered); - }); + // Create the 1.0 scale MIP at INDEX_OFFSET. + const textureScale = 1; + const mip = this.createMIP(textureScale, () => this.resetMIPs(mip, rotationCenter)); } } From bf29c5a3a04fb9bf05f8f7a79f8443ff11d060d5 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 11 Dec 2019 15:30:28 -0500 Subject: [PATCH 1893/1971] Merge pull request #487 from adroitwhiz/pen-stamp-to-framebuffer penStamp() directly to the PenSkin's framebuffer --- packages/scratch-render/src/RenderWebGL.js | 23 ++++++++++------------ 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 6d047f4648..4957812200 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1542,23 +1542,20 @@ class RenderWebGL extends EventEmitter { const skin = /** @type {PenSkin} */ this._allSkins[penSkinID]; const gl = this._gl; - twgl.bindFramebufferInfo(gl, this._queryBufferInfo); + twgl.bindFramebufferInfo(gl, skin._framebuffer); // Limit size of viewport to the bounds around the stamp Drawable and create the projection matrix for the draw. - gl.viewport(0, 0, bounds.width, bounds.height); + gl.viewport( + (this._nativeSize[0] * 0.5) + bounds.left, + (this._nativeSize[1] * 0.5) - bounds.top, + bounds.width, + bounds.height + ); const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); - gl.clearColor(0, 0, 0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - try { - gl.disable(gl.BLEND); - this._drawThese([stampID], ShaderManager.DRAW_MODE.stamp, projection, {ignoreVisibility: true}); - } finally { - gl.enable(gl.BLEND); - } - - skin._drawToBuffer(this._queryBufferInfo.attachments[0], bounds.left, bounds.top); + // Draw the stamped sprite onto the PenSkin's framebuffer. + this._drawThese([stampID], ShaderManager.DRAW_MODE.stamp, projection, {ignoreVisibility: true}); + skin._silhouetteDirty = true; } /* ****** From cb8a7e410bbb5fd621d7db9f705d9620ffa4d367 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 18 Sep 2020 10:25:56 -0400 Subject: [PATCH 1894/1971] Add test for local linking? --- packages/scratch-render/src/RenderWebGL.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 4957812200..25aed70348 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -125,6 +125,7 @@ class RenderWebGL extends EventEmitter { * @listens RenderWebGL#event:NativeSizeChanged */ constructor (canvas, xLeft, xRight, yBottom, yTop) { + console.log("LOCAL LINK CONFIRMED") super(); /** @type {WebGLRenderingContext} */ From 3dd729aea682df91e71a7752841bba25d9b871cd Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 23 Sep 2020 16:20:53 -0400 Subject: [PATCH 1895/1971] Add basic sprite text --- .../scratch-gui/src/containers/blocks.jsx | 5 +++++ .../src/lib/libraries/extensions/index.jsx | 20 +++++++++++++++++++ .../extension-support/extension-manager.js | 3 ++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index f5d52388ff..8dff46b110 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -135,6 +135,11 @@ class Blocks extends React.Component { if (this.props.isVisible) { this.setLocale(); } + setTimeout(() => { + this.props.vm.extensionManager.loadExtensionURL('text').then(() => { + this.handleCategorySelected('text'); + }) + }, 500) } shouldComponentUpdate (nextProps, nextState) { return ( diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index ba18b916b5..65a0f1ad9c 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -47,6 +47,26 @@ import gdxforConnectionIconURL from './gdxfor/gdxfor-illustration.svg'; import gdxforConnectionSmallIconURL from './gdxfor/gdxfor-small.svg'; export default [ + { + name: ( + + ), + extensionId: 'text', + iconURL: makeymakeyIconURL, + insetIconURL: makeymakeyInsetIconURL, + description: ( + + ), + featured: true + }, { name: ( require('../extensions/scratch3_ev3'), makeymakey: () => require('../extensions/scratch3_makeymakey'), boost: () => require('../extensions/scratch3_boost'), - gdxfor: () => require('../extensions/scratch3_gdx_for') + gdxfor: () => require('../extensions/scratch3_gdx_for'), + text: () => require('../extensions/scratch3_text') }; /** From 9639c258973705c2798b3e7cb5a5b240fe961c99 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 6 Oct 2020 13:53:47 -0400 Subject: [PATCH 1896/1971] Add TextCostumeSkin, revert TextBubbleSkin to develop --- packages/scratch-render/src/RenderWebGL.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 25aed70348..10f3e8e917 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -11,6 +11,7 @@ const RenderConstants = require('./RenderConstants'); const ShaderManager = require('./ShaderManager'); const SVGSkin = require('./SVGSkin'); const TextBubbleSkin = require('./TextBubbleSkin'); +const TextCostumeSkin = require('./TextCostumeSkin'); const EffectTransform = require('./EffectTransform'); const log = require('./util/log'); @@ -350,6 +351,14 @@ class RenderWebGL extends EventEmitter { return skinId; } + createTextCostumeSkin (text) { + const skinId = this._nextSkinId++; + const newSkin = new TextCostumeSkin(skinId, this); + newSkin.setText(text); + this._allSkins[skinId] = newSkin; + return skinId; + } + /** * Update an existing SVG skin, or create an SVG skin if the previous skin was not SVG. * @param {!int} skinId the ID for the skin to change. @@ -418,6 +427,17 @@ class RenderWebGL extends EventEmitter { this._reskin(skinId, newSkin); } + updateTextCostumeSkin (skinId, text) { + if (this._allSkins[skinId] instanceof TextCostumeSkin) { + this._allSkins[skinId].setText(text); + return; + } + + const newSkin = new TextCostumeSkin(skinId, this); + newSkin.setText(text); + this._reskin(skinId, newSkin); + } + /** * Destroy an existing skin. Do not use the skin or its ID after calling this. From 487edd2c24004c091dc6c61eb77a31ac050dbd92 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 9 Oct 2020 17:30:18 -0400 Subject: [PATCH 1897/1971] put textCostume functions next to each other --- packages/scratch-render/src/RenderWebGL.js | 23 +++++++++++----------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 10f3e8e917..5f9181b78e 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -359,6 +359,17 @@ class RenderWebGL extends EventEmitter { return skinId; } + updateTextCostumeSkin (skinId, text) { + if (this._allSkins[skinId] instanceof TextCostumeSkin) { + this._allSkins[skinId].setText(text); + return; + } + + const newSkin = new TextCostumeSkin(skinId, this); + newSkin.setText(text); + this._reskin(skinId, newSkin); + } + /** * Update an existing SVG skin, or create an SVG skin if the previous skin was not SVG. * @param {!int} skinId the ID for the skin to change. @@ -427,18 +438,6 @@ class RenderWebGL extends EventEmitter { this._reskin(skinId, newSkin); } - updateTextCostumeSkin (skinId, text) { - if (this._allSkins[skinId] instanceof TextCostumeSkin) { - this._allSkins[skinId].setText(text); - return; - } - - const newSkin = new TextCostumeSkin(skinId, this); - newSkin.setText(text); - this._reskin(skinId, newSkin); - } - - /** * Destroy an existing skin. Do not use the skin or its ID after calling this. * @param {!int} skinId - The ID of the skin to destroy. From 2764abdacf8d239a8f00fbb3359860243428fe7d Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Sat, 10 Oct 2020 13:59:21 -0400 Subject: [PATCH 1898/1971] extension owns style state --- packages/scratch-render/src/RenderWebGL.js | 24 ++++++++++------------ 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 5f9181b78e..4a8e86be7e 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -351,23 +351,21 @@ class RenderWebGL extends EventEmitter { return skinId; } - createTextCostumeSkin (text) { + updateTextCostumeSkin (textState) { + // update existing skin + if (textState.skinId && (this._allSkins[textState.skinId] instanceof TextCostumeSkin)) { + this._allSkins[textState.skinId].setTextAndStyle(textState); + return textState.skinId; + } + + // create and update a new skin const skinId = this._nextSkinId++; const newSkin = new TextCostumeSkin(skinId, this); - newSkin.setText(text); this._allSkins[skinId] = newSkin; - return skinId; - } - - updateTextCostumeSkin (skinId, text) { - if (this._allSkins[skinId] instanceof TextCostumeSkin) { - this._allSkins[skinId].setText(text); - return; - } + newSkin.setTextAndStyle(textState); + // this._reskin(textState.skinId, newSkin); // this is erroring- might be necessary for clones to work right? - const newSkin = new TextCostumeSkin(skinId, this); - newSkin.setText(text); - this._reskin(skinId, newSkin); + return skinId; } /** From d1bd37c6376318c42c3d366f048009ca9d545b0e Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Sat, 10 Oct 2020 19:50:53 -0400 Subject: [PATCH 1899/1971] trying to get text working for clones --- packages/scratch-render/src/RenderWebGL.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 4a8e86be7e..a422feba9d 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -363,7 +363,7 @@ class RenderWebGL extends EventEmitter { const newSkin = new TextCostumeSkin(skinId, this); this._allSkins[skinId] = newSkin; newSkin.setTextAndStyle(textState); - // this._reskin(textState.skinId, newSkin); // this is erroring- might be necessary for clones to work right? + // this._reskin(skinId, newSkin); // this is erroring- might be necessary for clones to work right? return skinId; } From 3a5e95529d7b81f2d83c33bf16e6af56a12763e8 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Sat, 10 Oct 2020 23:47:36 -0400 Subject: [PATCH 1900/1971] Clones get their own skins --- packages/scratch-render/src/RenderWebGL.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index a422feba9d..75948655f4 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -363,7 +363,7 @@ class RenderWebGL extends EventEmitter { const newSkin = new TextCostumeSkin(skinId, this); this._allSkins[skinId] = newSkin; newSkin.setTextAndStyle(textState); - // this._reskin(skinId, newSkin); // this is erroring- might be necessary for clones to work right? + // this._reskin(skinId, newSkin); // this is erroring- might be necessary? return skinId; } From 417ad7ed66be26a08908b42d3be4aaf5cbb25857 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 13 Oct 2020 09:55:41 -0400 Subject: [PATCH 1901/1971] Update to most recent scratch-render, dropping previous changes (i'll bring them back next) --- packages/scratch-render/src/RenderWebGL.js | 594 +++++++++++++-------- packages/scratch-render/src/SVGSkin.js | 160 +++--- 2 files changed, 448 insertions(+), 306 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 75948655f4..df91555eb3 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -11,7 +11,6 @@ const RenderConstants = require('./RenderConstants'); const ShaderManager = require('./ShaderManager'); const SVGSkin = require('./SVGSkin'); const TextBubbleSkin = require('./TextBubbleSkin'); -const TextCostumeSkin = require('./TextCostumeSkin'); const EffectTransform = require('./EffectTransform'); const log = require('./util/log'); @@ -44,6 +43,12 @@ const MAX_TOUCH_SIZE = [3, 3]; */ const MASK_TOUCHING_COLOR_TOLERANCE = 2; +/** + * Maximum number of pixels in either dimension of "extracted drawable" data + * @type {int} + */ +const MAX_EXTRACTED_DRAWABLE_DIMENSION = 2048; + /** * Determines if the mask color is "close enough" (only test the 6 top bits for * each color). These bit masks are what scratch 2 used to use, so we do the same. @@ -126,7 +131,6 @@ class RenderWebGL extends EventEmitter { * @listens RenderWebGL#event:NativeSizeChanged */ constructor (canvas, xLeft, xRight, yBottom, yTop) { - console.log("LOCAL LINK CONFIRMED") super(); /** @type {WebGLRenderingContext} */ @@ -184,9 +188,23 @@ class RenderWebGL extends EventEmitter { /** @type {function} */ this._exitRegion = null; + /** @type {object} */ + this._backgroundDrawRegionId = { + enter: () => this._enterDrawBackground(), + exit: () => this._exitDrawBackground() + }; + /** @type {Array.} */ this._snapshotCallbacks = []; + /** @type {Array} */ + // Don't set this directly-- use setBackgroundColor so it stays in sync with _backgroundColor3b + this._backgroundColor4f = [0, 0, 0, 1]; + + /** @type {Uint8ClampedArray} */ + // Don't set this directly-- use setBackgroundColor so it stays in sync with _backgroundColor4f + this._backgroundColor3b = new Uint8ClampedArray(3); + this._createGeometry(); this.on(RenderConstants.Events.NativeSizeChanged, this.onNativeSizeChanged); @@ -198,7 +216,7 @@ class RenderWebGL extends EventEmitter { gl.disable(gl.DEPTH_TEST); /** @todo disable when no partial transparency? */ gl.enable(gl.BLEND); - gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE); + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); } /** @@ -222,9 +240,20 @@ class RenderWebGL extends EventEmitter { * @param {int} pixelsTall The desired height in device-independent pixels. */ resize (pixelsWide, pixelsTall) { + const {canvas} = this._gl; const pixelRatio = window.devicePixelRatio || 1; - this._gl.canvas.width = pixelsWide * pixelRatio; - this._gl.canvas.height = pixelsTall * pixelRatio; + const newWidth = pixelsWide * pixelRatio; + const newHeight = pixelsTall * pixelRatio; + + // Certain operations, such as moving the color picker, call `resize` once per frame, even though the canvas + // size doesn't change. To avoid unnecessary canvas updates, check that we *really* need to resize the canvas. + if (canvas.width !== newWidth || canvas.height !== newHeight) { + canvas.width = newWidth; + canvas.height = newHeight; + // Resizing the canvas causes it to be cleared, so redraw it. + this.draw(); + } + } /** @@ -235,7 +264,14 @@ class RenderWebGL extends EventEmitter { * @param {number} blue The blue component for the background. */ setBackgroundColor (red, green, blue) { - this._backgroundColor = [red, green, blue, 1]; + this._backgroundColor4f[0] = red; + this._backgroundColor4f[1] = green; + this._backgroundColor4f[2] = blue; + + this._backgroundColor3b[0] = red * 255; + this._backgroundColor3b[1] = green * 255; + this._backgroundColor3b[2] = blue * 255; + } /** @@ -351,23 +387,6 @@ class RenderWebGL extends EventEmitter { return skinId; } - updateTextCostumeSkin (textState) { - // update existing skin - if (textState.skinId && (this._allSkins[textState.skinId] instanceof TextCostumeSkin)) { - this._allSkins[textState.skinId].setTextAndStyle(textState); - return textState.skinId; - } - - // create and update a new skin - const skinId = this._nextSkinId++; - const newSkin = new TextCostumeSkin(skinId, this); - this._allSkins[skinId] = newSkin; - newSkin.setTextAndStyle(textState); - // this._reskin(skinId, newSkin); // this is erroring- might be necessary? - - return skinId; - } - /** * Update an existing SVG skin, or create an SVG skin if the previous skin was not SVG. * @param {!int} skinId the ID for the skin to change. @@ -436,6 +455,7 @@ class RenderWebGL extends EventEmitter { this._reskin(skinId, newSkin); } + /** * Destroy an existing skin. Do not use the skin or its ID after calling this. * @param {!int} skinId - The ID of the skin to destroy. @@ -630,7 +650,7 @@ class RenderWebGL extends EventEmitter { twgl.bindFramebufferInfo(gl, null); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); - gl.clearColor.apply(gl, this._backgroundColor); + gl.clearColor.apply(gl, this._backgroundColor4f); gl.clear(gl.COLOR_BUFFER_BIT); this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, this._projection); @@ -746,12 +766,22 @@ class RenderWebGL extends EventEmitter { */ isTouchingColor (drawableID, color3b, mask3b) { const candidates = this._candidatesTouching(drawableID, this._visibleDrawList); - if (candidates.length === 0) { + + let bounds; + if (colorMatches(color3b, this._backgroundColor3b, 0)) { + // If the color we're checking for is the background color, don't confine the check to + // candidate drawables' bounds--since the background spans the entire stage, we must check + // everything that lies inside the drawable. + bounds = this._touchingBounds(drawableID); + // e.g. empty costume, or off the stage + if (bounds === null) return false; + } else if (candidates.length === 0) { + // If not checking for the background color, we can return early if there are no candidate drawables. return false; + } else { + bounds = this._candidatesBounds(candidates); } - const bounds = this._candidatesBounds(candidates); - const maxPixelsForCPU = this._getMaxPixelsForCPU(); const debugCanvasContext = this._debugCanvas && this._debugCanvas.getContext('2d'); @@ -770,6 +800,9 @@ class RenderWebGL extends EventEmitter { const color = __touchingColor; const hasMask = Boolean(mask3b); + // Masked drawable ignores ghost effect + const effectMask = ~ShaderManager.EFFECT_INFO.ghost.mask; + // Scratch Space - +y is top for (let y = bounds.bottom; y <= bounds.top; y++) { if (bounds.width * (y - bounds.bottom) * (candidates.length + 1) >= maxPixelsForCPU) { @@ -780,7 +813,7 @@ class RenderWebGL extends EventEmitter { point[0] = x; // if we use a mask, check our sample color... if (hasMask ? - maskMatches(Drawable.sampleColor4b(point, drawable, color), mask3b) : + maskMatches(Drawable.sampleColor4b(point, drawable, color, effectMask), mask3b) : drawable.isTouching(point)) { RenderWebGL.sampleColor3b(point, candidates, color); if (debugCanvasContext) { @@ -809,6 +842,19 @@ class RenderWebGL extends EventEmitter { } } + _enterDrawBackground () { + const gl = this.gl; + const currentShader = this._shaderManager.getShader(ShaderManager.DRAW_MODE.background, 0); + gl.disable(gl.BLEND); + gl.useProgram(currentShader.program); + twgl.setBuffersAndAttributes(gl, currentShader, this._bufferInfo); + } + + _exitDrawBackground () { + const gl = this.gl; + gl.enable(gl.BLEND); + } + _isTouchingColorGpuStart (drawableID, candidateIDs, bounds, color3b, mask3b) { this._doExitDrawRegion(); @@ -820,15 +866,8 @@ class RenderWebGL extends EventEmitter { gl.viewport(0, 0, bounds.width, bounds.height); const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); - let fillBackgroundColor = this._backgroundColor; - - // When using masking such that the background fill color will showing through, ensure we don't - // fill using the same color that we are trying to detect! - if (color3b[0] > 196 && color3b[1] > 196 && color3b[2] > 196) { - fillBackgroundColor = [0, 0, 0, 255]; - } - - gl.clearColor.apply(gl, fillBackgroundColor); + // Clear the query buffer to fully transparent. This will be the color of pixels that fail the stencil test. + gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); let extraUniforms; @@ -840,6 +879,9 @@ class RenderWebGL extends EventEmitter { } try { + // Using the stencil buffer, mask out the drawing to either the drawable's alpha channel + // or pixels of the drawable which match the mask color, depending on whether a mask color is given. + // Masked-out pixels will not be checked. gl.enable(gl.STENCIL_TEST); gl.stencilFunc(gl.ALWAYS, 1, 1); gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); @@ -852,19 +894,33 @@ class RenderWebGL extends EventEmitter { projection, { extraUniforms, - ignoreVisibility: true // Touching color ignores sprite visibility + ignoreVisibility: true, // Touching color ignores sprite visibility, + effectMask: ~ShaderManager.EFFECT_INFO.ghost.mask }); gl.stencilFunc(gl.EQUAL, 1, 1); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); gl.colorMask(true, true, true, true); + // Draw the background as a quad. Drawing a background with gl.clear will not mask to the stenciled area. + this.enterDrawRegion(this._backgroundDrawRegionId); + + const uniforms = { + u_backgroundColor: this._backgroundColor4f + }; + + const currentShader = this._shaderManager.getShader(ShaderManager.DRAW_MODE.background, 0); + twgl.setUniforms(currentShader, uniforms); + twgl.drawBufferInfo(gl, this._bufferInfo, gl.TRIANGLES); + + // Draw the candidate drawables on top of the background. this._drawThese(candidateIDs, ShaderManager.DRAW_MODE.default, projection, {idFilterFunc: testID => testID !== drawableID} ); } finally { gl.colorMask(true, true, true, true); gl.disable(gl.STENCIL_TEST); + this._doExitDrawRegion(); } } @@ -883,7 +939,8 @@ class RenderWebGL extends EventEmitter { } for (let pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { - if (colorMatches(color3b, pixels, pixelBase)) { + // Transparent pixels are masked (either by the drawable's alpha channel or color mask). + if (pixels[pixelBase + 3] !== 0 && colorMatches(color3b, pixels, pixelBase)) { return true; } } @@ -988,12 +1045,7 @@ class RenderWebGL extends EventEmitter { const bounds = this.clientSpaceToScratchBounds(centerX, centerY, touchWidth, touchHeight); const worldPos = twgl.v3.create(); - drawable.updateMatrix(); - if (drawable.skin) { - drawable.skin.updateSilhouette(); - } else { - log.warn(`Could not find skin for drawable with id: ${drawableID}`); - } + drawable.updateCPURenderAttributes(); for (worldPos[1] = bounds.bottom; worldPos[1] <= bounds.top; worldPos[1]++) { for (worldPos[0] = bounds.left; worldPos[0] <= bounds.right; worldPos[0]++) { @@ -1020,16 +1072,20 @@ class RenderWebGL extends EventEmitter { * RenderConstants.ID_NONE if there is no Drawable at that location. */ pick (centerX, centerY, touchWidth, touchHeight, candidateIDs) { + const bounds = this.clientSpaceToScratchBounds(centerX, centerY, touchWidth, touchHeight); + if (bounds.left === -Infinity || bounds.bottom === -Infinity) { + return false; + } + candidateIDs = (candidateIDs || this._drawList).filter(id => { const drawable = this._allDrawables[id]; // default pick list ignores visible and ghosted sprites. if (drawable.getVisible() && drawable.getUniforms().u_ghost !== 0) { - drawable.updateMatrix(); - if (drawable.skin) { - drawable.skin.updateSilhouette(); - } else { - log.warn(`Could not find skin for drawable with id: ${id}`); - } + const drawableBounds = drawable.getFastBounds(); + const inRange = bounds.intersects(drawableBounds); + if (!inRange) return false; + + drawable.updateCPURenderAttributes(); return true; } return false; @@ -1038,11 +1094,6 @@ class RenderWebGL extends EventEmitter { return false; } - const bounds = this.clientSpaceToScratchBounds(centerX, centerY, touchWidth, touchHeight); - if (bounds.left === -Infinity || bounds.bottom === -Infinity) { - return false; - } - const hits = []; const worldPos = twgl.v3.create(0, 0, 0); // Iterate over the scratch pixels and check if any candidate can be @@ -1078,7 +1129,7 @@ class RenderWebGL extends EventEmitter { } /** - * @typedef DrawableExtraction + * @typedef DrawableExtractionOld * @property {Uint8Array} data Raw pixel data for the drawable * @property {int} width Drawable bounding box width * @property {int} height Drawable bounding box height @@ -1093,7 +1144,8 @@ class RenderWebGL extends EventEmitter { * @param {int} drawableID The ID of the drawable to get pixel data for * @param {int} x The client x coordinate of the picking location. * @param {int} y The client y coordinate of the picking location. - * @return {?DrawableExtraction} Data about the picked drawable + * @return {?DrawableExtractionOld} Data about the picked drawable + * @deprecated Use {@link extractDrawableScreenSpace} instead. */ extractDrawable (drawableID, x, y) { this._doExitDrawRegion(); @@ -1122,61 +1174,172 @@ class RenderWebGL extends EventEmitter { ]; const bufferInfo = twgl.createFramebufferInfo(gl, attachments, clampedWidth, clampedHeight); - // If the new bufferInfo is invalid, fall back to using the smaller _queryBufferInfo - twgl.bindFramebufferInfo(gl, bufferInfo); - if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) { - twgl.bindFramebufferInfo(gl, this._queryBufferInfo); - } + try { + // If the new bufferInfo is invalid, fall back to using the smaller _queryBufferInfo + twgl.bindFramebufferInfo(gl, bufferInfo); + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) { + twgl.bindFramebufferInfo(gl, this._queryBufferInfo); + } - // Translate to scratch units relative to the drawable - const pickX = scratchX - bounds.left; - const pickY = scratchY + bounds.top; + // Translate to scratch units relative to the drawable + const pickX = scratchX - bounds.left; + const pickY = scratchY + bounds.top; + + // Limit size of viewport to the bounds around the target Drawable, + // and create the projection matrix for the draw. + gl.viewport(0, 0, bounds.width, bounds.height); + const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); + + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + try { + gl.disable(gl.BLEND); + // ImageData objects store alpha un-premultiplied, so draw with the `straightAlpha` draw mode. + this._drawThese([drawableID], ShaderManager.DRAW_MODE.straightAlpha, projection, + {effectMask: ~ShaderManager.EFFECT_INFO.ghost.mask}); + } finally { + gl.enable(gl.BLEND); + } - // Limit size of viewport to the bounds around the target Drawable, - // and create the projection matrix for the draw. - gl.viewport(0, 0, bounds.width, bounds.height); - const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); + const data = new Uint8Array(Math.floor(bounds.width * bounds.height * 4)); + gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, data); + + if (this._debugCanvas) { + this._debugCanvas.width = bounds.width; + this._debugCanvas.height = bounds.height; + const ctx = this._debugCanvas.getContext('2d'); + const imageData = ctx.createImageData(bounds.width, bounds.height); + imageData.data.set(data); + ctx.putImageData(imageData, 0, 0); + ctx.beginPath(); + ctx.arc(pickX, pickY, 3, 0, 2 * Math.PI, false); + ctx.fillStyle = 'white'; + ctx.fill(); + ctx.lineWidth = 1; + ctx.strokeStyle = 'black'; + ctx.stroke(); + } - gl.clearColor(0, 0, 0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - try { - gl.disable(gl.BLEND); - this._drawThese([drawableID], ShaderManager.DRAW_MODE.default, projection, - {effectMask: ~ShaderManager.EFFECT_INFO.ghost.mask}); + return { + data: data, + width: bounds.width, + height: bounds.height, + scratchOffset: [ + -scratchX + drawable._position[0], + -scratchY - drawable._position[1] + ], + x: pickX, + y: pickY + }; } finally { - gl.enable(gl.BLEND); + gl.deleteFramebuffer(bufferInfo.framebuffer); } + } - const data = new Uint8Array(Math.floor(bounds.width * bounds.height * 4)); - gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, data); + /** + * @typedef DrawableExtraction + * @property {ImageData} data Raw pixel data for the drawable + * @property {number} x The x coordinate of the drawable's bounding box's top-left corner, in 'CSS pixels' + * @property {number} y The y coordinate of the drawable's bounding box's top-left corner, in 'CSS pixels' + * @property {number} width The drawable's bounding box width, in 'CSS pixels' + * @property {number} height The drawable's bounding box height, in 'CSS pixels' + */ - if (this._debugCanvas) { - this._debugCanvas.width = bounds.width; - this._debugCanvas.height = bounds.height; - const ctx = this._debugCanvas.getContext('2d'); - const imageData = ctx.createImageData(bounds.width, bounds.height); - imageData.data.set(data); - ctx.putImageData(imageData, 0, 0); - ctx.beginPath(); - ctx.arc(pickX, pickY, 3, 0, 2 * Math.PI, false); - ctx.fillStyle = 'white'; - ctx.fill(); - ctx.lineWidth = 1; - ctx.strokeStyle = 'black'; - ctx.stroke(); - } + /** + * Return a drawable's pixel data and bounds in screen space. + * @param {int} drawableID The ID of the drawable to get pixel data for + * @return {DrawableExtraction} Data about the picked drawable + */ + extractDrawableScreenSpace (drawableID) { + const drawable = this._allDrawables[drawableID]; + if (!drawable) throw new Error(`Could not extract drawable with ID ${drawableID}; it does not exist`); - return { - data: data, - width: bounds.width, - height: bounds.height, - scratchOffset: [ - -scratchX + drawable._position[0], - -scratchY - drawable._position[1] - ], - x: pickX, - y: pickY - }; + this._doExitDrawRegion(); + + const nativeCenterX = this._nativeSize[0] * 0.5; + const nativeCenterY = this._nativeSize[1] * 0.5; + + const scratchBounds = drawable.getFastBounds(); + + const canvas = this.canvas; + // Ratio of the screen-space scale of the stage's canvas to the "native size" of the stage + const scaleFactor = canvas.width / this._nativeSize[0]; + + // Bounds of the extracted drawable, in "canvas pixel space" + // (origin is 0, 0, destination is the canvas width, height). + const canvasSpaceBounds = new Rectangle(); + canvasSpaceBounds.initFromBounds( + (scratchBounds.left + nativeCenterX) * scaleFactor, + (scratchBounds.right + nativeCenterX) * scaleFactor, + // in "canvas space", +y is down, but Rectangle methods assume bottom < top, so swap them + (nativeCenterY - scratchBounds.top) * scaleFactor, + (nativeCenterY - scratchBounds.bottom) * scaleFactor + ); + canvasSpaceBounds.snapToInt(); + + // undo the transformation to transform the bounds, snapped to "canvas-pixel space", back to "Scratch space" + // We have to transform -> snap -> invert transform so that the "Scratch-space" bounds are snapped in + // "canvas-pixel space". + scratchBounds.initFromBounds( + (canvasSpaceBounds.left / scaleFactor) - nativeCenterX, + (canvasSpaceBounds.right / scaleFactor) - nativeCenterX, + nativeCenterY - (canvasSpaceBounds.top / scaleFactor), + nativeCenterY - (canvasSpaceBounds.bottom / scaleFactor) + ); + + const gl = this._gl; + + // Set a reasonable max limit width and height for the bufferInfo bounds + const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); + const clampedWidth = Math.min(MAX_EXTRACTED_DRAWABLE_DIMENSION, canvasSpaceBounds.width, maxTextureSize); + const clampedHeight = Math.min(MAX_EXTRACTED_DRAWABLE_DIMENSION, canvasSpaceBounds.height, maxTextureSize); + + // Make a new bufferInfo since this._queryBufferInfo is limited to 480x360 + const bufferInfo = twgl.createFramebufferInfo(gl, [{format: gl.RGBA}], clampedWidth, clampedHeight); + + try { + twgl.bindFramebufferInfo(gl, bufferInfo); + + // Limit size of viewport to the bounds around the target Drawable, + // and create the projection matrix for the draw. + gl.viewport(0, 0, clampedWidth, clampedHeight); + const projection = twgl.m4.ortho( + scratchBounds.left, + scratchBounds.right, + scratchBounds.top, + scratchBounds.bottom, + -1, 1 + ); + + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + // Don't apply the ghost effect. TODO: is this an intentional design decision? + this._drawThese([drawableID], ShaderManager.DRAW_MODE.straightAlpha, projection, + {effectMask: ~ShaderManager.EFFECT_INFO.ghost.mask}); + + const data = new Uint8Array(Math.floor(clampedWidth * clampedHeight * 4)); + gl.readPixels(0, 0, clampedWidth, clampedHeight, gl.RGBA, gl.UNSIGNED_BYTE, data); + // readPixels can only read into a Uint8Array, but ImageData has to take a Uint8ClampedArray. + // We can share the same underlying buffer between them to avoid having to copy any data. + const imageData = new ImageData(new Uint8ClampedArray(data.buffer), clampedWidth, clampedHeight); + + // On high-DPI devices, the canvas' width (in canvas pixels) will be larger than its width in CSS pixels. + // We want to return the CSS-space bounds, + // so take into account the ratio between the canvas' pixel dimensions and its layout dimensions. + // This is usually the same as 1 / window.devicePixelRatio, but if e.g. you zoom your browser window without + // the canvas resizing, then it'll differ. + const ratio = canvas.getBoundingClientRect().width / canvas.width; + + return { + imageData, + x: canvasSpaceBounds.left * ratio, + y: canvasSpaceBounds.bottom * ratio, + width: canvasSpaceBounds.width * ratio, + height: canvasSpaceBounds.height * ratio + }; + } finally { + gl.deleteFramebuffer(bufferInfo.framebuffer); + } } /** @@ -1212,7 +1375,7 @@ class RenderWebGL extends EventEmitter { gl.viewport(0, 0, bounds.width, bounds.height); const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); - gl.clearColor.apply(gl, this._backgroundColor); + gl.clearColor.apply(gl, this._backgroundColor4f); gl.clear(gl.COLOR_BUFFER_BIT); this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, projection); @@ -1260,8 +1423,8 @@ class RenderWebGL extends EventEmitter { /** @todo remove this once URL-based skin setting is removed. */ if (!drawable.skin || !drawable.skin.getTexture([100, 100])) return null; - drawable.updateMatrix(); - drawable.skin.updateSilhouette(); + + drawable.updateCPURenderAttributes(); const bounds = drawable.getFastBounds(); // Limit queries to the stage size. @@ -1296,11 +1459,19 @@ class RenderWebGL extends EventEmitter { const id = candidateIDs[index]; if (id !== drawableID) { const drawable = this._allDrawables[id]; + // Text bubbles aren't considered in "touching" queries + if (drawable.skin instanceof TextBubbleSkin) continue; if (drawable.skin && drawable._visible) { // Update the CPU position data - drawable.updateMatrix(); - drawable.skin.updateSilhouette(); + drawable.updateCPURenderAttributes(); const candidateBounds = drawable.getFastBounds(); + + // Push bounds out to integers. If a drawable extends out into half a pixel, that half-pixel still + // needs to be tested. Plus, in some areas we construct another rectangle from the union of these, + // and iterate over its pixels (width * height). Turns out that doesn't work so well when the + // width/height aren't integers. + candidateBounds.snapToInt(); + if (bounds.intersects(candidateBounds)) { result.push({ id, @@ -1342,32 +1513,6 @@ class RenderWebGL extends EventEmitter { drawable.skin = this._allSkins[skinId]; } - /** - * Update a drawable's skin rotation center. - * @param {number} drawableID The drawable's id. - * @param {Array.} rotationCenter The rotation center for the skin. - */ - updateDrawableRotationCenter (drawableID, rotationCenter) { - const drawable = this._allDrawables[drawableID]; - // TODO: https://github.com/LLK/scratch-vm/issues/2288 - if (!drawable) return; - drawable.skin.setRotationCenter(rotationCenter[0], rotationCenter[1]); - } - - /** - * Update a drawable's skin and rotation center together. - * @param {number} drawableID The drawable's id. - * @param {number} skinId The skin to update to. - * @param {Array.} rotationCenter The rotation center for the skin. - */ - updateDrawableSkinIdRotationCenter (drawableID, skinId, rotationCenter) { - const drawable = this._allDrawables[drawableID]; - // TODO: https://github.com/LLK/scratch-vm/issues/2288 - if (!drawable) return; - drawable.skin = this._allSkins[skinId]; - drawable.skin.setRotationCenter(rotationCenter[0], rotationCenter[1]); - } - /** * Update a drawable's position. * @param {number} drawableID The drawable's id. @@ -1461,9 +1606,6 @@ class RenderWebGL extends EventEmitter { if ('skinId' in properties) { this.updateDrawableSkinId(drawableID, properties.skinId); } - if ('rotationCenter' in properties) { - this.updateDrawableRotationCenter(drawableID, properties.rotationCenter); - } drawable.updateProperties(properties); } @@ -1572,7 +1714,7 @@ class RenderWebGL extends EventEmitter { const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); // Draw the stamped sprite onto the PenSkin's framebuffer. - this._drawThese([stampID], ShaderManager.DRAW_MODE.stamp, projection, {ignoreVisibility: true}); + this._drawThese([stampID], ShaderManager.DRAW_MODE.default, projection, {ignoreVisibility: true}); skin._silhouetteDirty = true; } @@ -1677,6 +1819,18 @@ class RenderWebGL extends EventEmitter { this._regionId = null; } + /** + * Get the screen-space scale of a drawable, as percentages of the drawable's "normal" size. + * @param {Drawable} drawable The drawable whose screen-space scale we're fetching. + * @returns {Array} The screen-space X and Y dimensions of the drawable's scale, as percentages. + */ + _getDrawableScreenSpaceScale (drawable) { + return [ + drawable.scale[0] * this._gl.canvas.width / this._nativeSize[0], + drawable.scale[1] * this._gl.canvas.height / this._nativeSize[1] + ]; + } + /** * Draw a set of Drawables, by drawable ID * @param {Array} drawables The Drawable IDs to draw, possibly this._drawList. @@ -1709,17 +1863,14 @@ class RenderWebGL extends EventEmitter { if (!drawable.getVisible() && !opts.ignoreVisibility) continue; // Combine drawable scale with the native vs. backing pixel ratio - const drawableScale = [ - drawable.scale[0] * this._gl.canvas.width / this._nativeSize[0], - drawable.scale[1] * this._gl.canvas.height / this._nativeSize[1] - ]; + const drawableScale = this._getDrawableScreenSpaceScale(drawable); // If the skin or texture isn't ready yet, skip it. if (!drawable.skin || !drawable.skin.getTexture(drawableScale)) continue; const uniforms = {}; - let effectBits = drawable.getEnabledEffects(); + let effectBits = drawable.enabledEffects; effectBits &= opts.hasOwnProperty('effectMask') ? opts.effectMask : effectBits; const newShader = this._shaderManager.getShader(drawMode, effectBits); @@ -1753,14 +1904,6 @@ class RenderWebGL extends EventEmitter { } twgl.setUniforms(currentShader, uniforms); - - /* adjust blend function for this skin */ - if (drawable.skin.hasPremultipliedAlpha){ - gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - } else { - gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - } - twgl.drawBufferInfo(gl, this._bufferInfo, gl.TRIANGLES); } @@ -1769,14 +1912,15 @@ class RenderWebGL extends EventEmitter { /** * Get the convex hull points for a particular Drawable. - * To do this, draw the Drawable unrotated, unscaled, and untranslated. - * Read back the pixels and find all boundary points. - * Finally, apply a convex hull algorithm to simplify the set. + * To do this, calculate it based on the drawable's Silhouette. * @param {int} drawableID The Drawable IDs calculate convex hull for. * @return {Array>} points Convex hull points, as [[x, y], ...] */ _getConvexHullPointsForDrawable (drawableID) { const drawable = this._allDrawables[drawableID]; + + drawable.updateCPURenderAttributes(); + const [width, height] = drawable.skin.size; // No points in the hull if invisible or size is 0. if (!drawable.getVisible() || width === 0 || height === 0) { @@ -1784,110 +1928,120 @@ class RenderWebGL extends EventEmitter { } /** - * Return the determinant of two vectors, the vector from A to B and - * the vector from A to C. + * Return the determinant of two vectors, the vector from A to B and the vector from A to C. * - * The determinant is useful in this case to know if AC is counter - * clockwise from AB. A positive value means the AC is counter - * clockwise from AC. A negative value menas AC is clockwise from AB. + * The determinant is useful in this case to know if AC is counter-clockwise from AB. + * A positive value means that AC is counter-clockwise from AB. A negative value means AC is clockwise from AB. * * @param {Float32Array} A A 2d vector in space. * @param {Float32Array} B A 2d vector in space. * @param {Float32Array} C A 2d vector in space. - * @return {number} Greater than 0 if counter clockwise, less than if - * clockwise, 0 if all points are on a line. + * @return {number} Greater than 0 if counter clockwise, less than if clockwise, 0 if all points are on a line. */ - const CCW = function (A, B, C) { + const determinant = function (A, B, C) { // AB = B - A // AC = C - A // det (AB BC) = AB0 * AC1 - AB1 * AC0 return (((B[0] - A[0]) * (C[1] - A[1])) - ((B[1] - A[1]) * (C[0] - A[0]))); }; - // https://github.com/LLK/scratch-flash/blob/dcbeeb59d44c3be911545dfe54d - // 46a32404f8e69/src/scratch/ScratchCostume.as#L369-L413 Following - // RasterHull creation, compare and store left and right values that - // maintain a convex shape until that data can be passed to `hull` for - // further work. - const L = []; - const R = []; + // This algorithm for calculating the convex hull somewhat resembles the monotone chain algorithm. + // The main difference is that instead of sorting the points by x-coordinate, and y-coordinate in case of ties, + // it goes through them by y-coordinate in the outer loop and x-coordinate in the inner loop. + // This gives us "left" and "right" hulls, whereas the monotone chain algorithm gives "top" and "bottom" hulls. + // Adapted from https://github.com/LLK/scratch-flash/blob/dcbeeb59d44c3be911545dfe54d46a32404f8e69/src/scratch/ScratchCostume.as#L369-L413 + + const leftHull = []; + const rightHull = []; + + // While convex hull algorithms usually push and pop values from the list of hull points, + // here, we keep indices for the "last" point in each array. Any points past these indices are ignored. + // This is functionally equivalent to pushing and popping from a "stack" of hull points. + let leftEndPointIndex = -1; + let rightEndPointIndex = -1; + const _pixelPos = twgl.v3.create(); const _effectPos = twgl.v3.create(); - let ll = -1; - let rr = -1; - let Q; + + let currentPoint; + + // *Not* Scratch Space-- +y is bottom + // Loop over all rows of pixels, starting at the top for (let y = 0; y < height; y++) { _pixelPos[1] = y / height; - // Scan from left to right, looking for a touchable spot in the - // skin. + + // We start at the leftmost point, then go rightwards until we hit an opaque pixel let x = 0; for (; x < width; x++) { _pixelPos[0] = x / width; EffectTransform.transformPoint(drawable, _pixelPos, _effectPos); if (drawable.skin.isTouchingLinear(_effectPos)) { - Q = [x, y]; + currentPoint = [x, y]; break; } } - // If x is equal to the width there are no touchable points in the - // skin. Nothing we can add to L. And looping for R would find the - // same thing. + + // If we managed to loop all the way through, there are no opaque pixels on this row. Go to the next one if (x >= width) { continue; } - // Decrement ll until Q is clockwise (CCW returns negative) from the - // last two points in L. - while (ll > 0) { - if (CCW(L[ll - 1], L[ll], Q) < 0) { + + // Because leftEndPointIndex is initialized to -1, this is skipped for the first two rows. + // It runs only when there are enough points in the left hull to make at least one line. + // If appending the current point to the left hull makes a counter-clockwise turn, + // we want to append the current point. Otherwise, we decrement the index of the "last" hull point until the + // current point makes a counter-clockwise turn. + // This decrementing has the same effect as popping from the point list, but is hopefully faster. + while (leftEndPointIndex > 0) { + if (determinant(leftHull[leftEndPointIndex], leftHull[leftEndPointIndex - 1], currentPoint) > 0) { break; } else { - --ll; + // leftHull.pop(); + --leftEndPointIndex; } } - // Increment ll and then set L[ll] to Q. If ll was -1 before this - // line, this will set L[0] to Q. If ll was 0 before this line, this - // will set L[1] to Q. - L[++ll] = Q; - // Scan from right to left, looking for a touchable spot in the - // skin. + // This has the same effect as pushing to the point list. + // This "list head pointer" coding style leaves excess points dangling at the end of the list, + // but that doesn't matter; we simply won't copy them over to the final hull. + + // leftHull.push(currentPoint); + leftHull[++leftEndPointIndex] = currentPoint; + + // Now we repeat the process for the right side, looking leftwards for a pixel. for (x = width - 1; x >= 0; x--) { _pixelPos[0] = x / width; EffectTransform.transformPoint(drawable, _pixelPos, _effectPos); if (drawable.skin.isTouchingLinear(_effectPos)) { - Q = [x, y]; + currentPoint = [x, y]; break; } } - // Decrement rr until Q is counter clockwise (CCW returns positive) - // from the last two points in L. L takes clockwise points and R - // takes counter clockwise points. if y was decremented instead of - // incremented R would take clockwise points. We are going in the - // right direction for L and the wrong direction for R, so we - // compare the opposite value for R from L. - while (rr > 0) { - if (CCW(R[rr - 1], R[rr], Q) > 0) { + + // Because we're coming at this from the right, it goes clockwise this time. + while (rightEndPointIndex > 0) { + if (determinant(rightHull[rightEndPointIndex], rightHull[rightEndPointIndex - 1], currentPoint) < 0) { break; } else { - --rr; + --rightEndPointIndex; } } - // Increment rr and then set R[rr] to Q. - R[++rr] = Q; + + rightHull[++rightEndPointIndex] = currentPoint; } - // Known boundary points on left/right edges of pixels. - const boundaryPoints = L; - // Truncate boundaryPoints to the index of the last added Q to L. L may - // have more entries than the index for the last Q. - boundaryPoints.length = ll + 1; - // Add points in R to boundaryPoints in reverse so all points in - // boundaryPoints are clockwise from each other. - for (let j = rr; j >= 0; --j) { - boundaryPoints.push(R[j]); + // Start off "hullPoints" with the left hull points. + const hullPoints = leftHull; + // This is where we get rid of those dangling extra points. + hullPoints.length = leftEndPointIndex + 1; + // Add points from the right side in reverse order so all points are ordered clockwise. + for (let j = rightEndPointIndex; j >= 0; --j) { + hullPoints.push(rightHull[j]); } - // Simplify boundary points using convex hull. - return hull(boundaryPoints, Infinity); + + // Simplify boundary points using hull.js. + // TODO: Remove this; this algorithm already generates convex hulls. + return hull(hullPoints, Infinity); } /** @@ -1911,13 +2065,11 @@ class RenderWebGL extends EventEmitter { } */ Drawable.sampleColor4b(vec, drawables[index].drawable, __blendColor); - // if we are fully transparent, go to the next one "down" - const sampleAlpha = __blendColor[3] / 255; - // premultiply alpha - dst[0] += __blendColor[0] * blendAlpha * sampleAlpha; - dst[1] += __blendColor[1] * blendAlpha * sampleAlpha; - dst[2] += __blendColor[2] * blendAlpha * sampleAlpha; - blendAlpha *= (1 - sampleAlpha); + // Equivalent to gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) + dst[0] += __blendColor[0] * blendAlpha; + dst[1] += __blendColor[1] * blendAlpha; + dst[2] += __blendColor[2] * blendAlpha; + blendAlpha *= (1 - (__blendColor[3] / 255)); } // Backdrop could be transparent, so we need to go to the "clear color" of the // draw scene (white) as a fallback if everything was alpha diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index d33425844c..0b58fdcd3a 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -1,9 +1,10 @@ +const twgl = require('twgl.js'); + const Skin = require('./Skin'); -const SVGMIP = require('./SVGMIP'); const SvgRenderer = require('scratch-svg-renderer').SVGRenderer; const MAX_TEXTURE_DIMENSION = 2048; -const MIN_TEXTURE_SCALE = 1 / 256; + /** * All scaled renderings of the SVG are stored in an array. The 1.0 scale of * the SVG is stored at the 8th index. The smallest possible 1 / 256 scale @@ -29,12 +30,12 @@ class SVGSkin extends Skin { /** @type {SvgRenderer} */ this._svgRenderer = new SvgRenderer(); - /** @type {WebGLTexture} */ - this._texture = null; - - /** @type {Array.} */ + /** @type {Array} */ this._scaledMIPs = []; + /** @type {number} */ + this._largestMIPScale = 0; + /** * Ratio of the size of the SVG and the max size of the WebGL texture * @type {Number} @@ -46,15 +47,7 @@ class SVGSkin extends Skin { * Dispose of this object. Do not use it after calling this method. */ dispose () { - if (this._texture) { - for (const mip of this._scaledMIPs) { - if (mip) { - mip.dispose(); - } - } - this._texture = null; - this._scaledMIPs.length = 0; - } + this.resetMIPs(); super.dispose(); } @@ -66,90 +59,81 @@ class SVGSkin extends Skin { } /** - * Set the origin, in object space, about which this Skin should rotate. - * @param {number} x - The x coordinate of the new rotation center. - * @param {number} y - The y coordinate of the new rotation center. - */ - setRotationCenter (x, y) { - const viewOffset = this._svgRenderer.viewOffset; - super.setRotationCenter(x - viewOffset[0], y - viewOffset[1]); - } - - /** - * Create a MIP for a given scale and pass it a callback for updating - * state when switching between scales and MIPs. + * Create a MIP for a given scale. * @param {number} scale - The relative size of the MIP - * @param {function} resetCallback - this is a callback for doing a hard reset - * of MIPs and a reset of the rotation center. Only passed in if the MIP scale is 1. * @return {SVGMIP} An object that handles creating and updating SVG textures. */ - createMIP (scale, resetCallback) { - const textureCallback = textureData => { - if (resetCallback) resetCallback(); - // Check if we have the largest MIP - // eslint-disable-next-line no-use-before-define - if (!this._scaledMIPs.length || this._scaledMIPs[this._scaledMIPs.length - 1]._scale <= scale) { - // Currently silhouette only gets scaled up - this._silhouette.update(textureData); - } + createMIP (scale) { + this._svgRenderer.draw(scale); + + // Pull out the ImageData from the canvas. ImageData speeds up + // updating Silhouette and is better handled by more browsers in + // regards to memory. + const canvas = this._svgRenderer.canvas; + // If one of the canvas dimensions is 0, set this MIP to an empty image texture. + // This avoids an IndexSizeError from attempting to getImageData when one of the dimensions is 0. + if (canvas.width === 0 || canvas.height === 0) return super.getTexture(); + + const context = canvas.getContext('2d'); + const textureData = context.getImageData(0, 0, canvas.width, canvas.height); + + const textureOptions = { + auto: false, + wrap: this._renderer.gl.CLAMP_TO_EDGE, + src: textureData, + premultiplyAlpha: true }; - const mip = new SVGMIP(this._renderer, this._svgRenderer, scale, textureCallback); + + const mip = twgl.createTexture(this._renderer.gl, textureOptions); + + // Check if this is the largest MIP created so far. Currently, silhouettes only get scaled up. + if (this._largestMIPScale < scale) { + this._silhouette.update(textureData); + this._largestMIPScale = scale; + } return mip; } + updateSilhouette (scale = [100, 100]) { + // Ensure a silhouette exists. + this.getTexture(scale); + } + /** * @param {Array} scale - The scaling factors to be used, each in the [0,100] range. * @return {WebGLTexture} The GL texture representation of this skin when drawing at the given scale. */ getTexture (scale) { - if (!this._svgRenderer.canvas.width || !this._svgRenderer.canvas.height) { - return super.getTexture(); - } - // The texture only ever gets uniform scale. Take the larger of the two axes. const scaleMax = scale ? Math.max(Math.abs(scale[0]), Math.abs(scale[1])) : 100; const requestedScale = Math.min(scaleMax / 100, this._maxTextureScale); - let newScale = 1; - let textureIndex = 0; - if (requestedScale < 1) { - while ((newScale > MIN_TEXTURE_SCALE) && (requestedScale <= newScale * .75)) { - newScale /= 2; - textureIndex -= 1; - } - } else { - while ((newScale < this._maxTextureScale) && (requestedScale >= 1.5 * newScale)) { - newScale *= 2; - textureIndex += 1; - } - } + // Math.ceil(Math.log2(scale)) means we use the "1x" texture at (0.5, 1] scale, + // the "2x" texture at (1, 2] scale, the "4x" texture at (2, 4] scale, etc. + // This means that one texture pixel will always be between 0.5x and 1x the size of one rendered pixel, + // but never bigger than one rendered pixel--this prevents blurriness from blowing up the texture too much. + const mipLevel = Math.max(Math.ceil(Math.log2(requestedScale)) + INDEX_OFFSET, 0); + // Can't use bitwise stuff here because we need to handle negative exponents + const mipScale = Math.pow(2, mipLevel - INDEX_OFFSET); - if (!this._scaledMIPs[textureIndex + INDEX_OFFSET]) { - this._scaledMIPs[textureIndex + INDEX_OFFSET] = this.createMIP(newScale); + if (this._svgRenderer.loaded && !this._scaledMIPs[mipLevel]) { + this._scaledMIPs[mipLevel] = this.createMIP(mipScale); } - return this._scaledMIPs[textureIndex + INDEX_OFFSET].getTexture(); + return this._scaledMIPs[mipLevel] || super.getTexture(); } /** - * Do a hard reset of the existing MIPs by calling dispose(), setting a new - * scale 1 MIP in this._scaledMIPs, and finally updating the rotationCenter. - * @param {SVGMIPs} mip - An object that handles creating and updating SVG textures. + * Do a hard reset of the existing MIPs by deleting them. * @param {Array} [rotationCenter] - Optional rotation center for the SVG. If not supplied, it will be * calculated from the bounding box - * @fires Skin.event:WasAltered + * @fires Skin.event:WasAltered */ - resetMIPs (mip, rotationCenter) { - this._scaledMIPs.forEach(oldMIP => oldMIP.dispose()); + resetMIPs () { + this._scaledMIPs.forEach(oldMIP => this._renderer.gl.deleteTexture(oldMIP)); this._scaledMIPs.length = 0; - - // Set new scale 1 MIP after outdated MIPs have been disposed - this._texture = this._scaledMIPs[INDEX_OFFSET] = mip; - - if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); - this.setRotationCenter.apply(this, rotationCenter); - this.emit(Skin.Events.WasAltered); + this._largestMIPScale = 0; } /** @@ -158,22 +142,28 @@ class SVGSkin extends Skin { * @param {Array} [rotationCenter] - Optional rotation center for the SVG. */ setSVG (svgData, rotationCenter) { - this._svgRenderer.loadString(svgData); + this._svgRenderer.loadSVG(svgData, false, () => { + const svgSize = this._svgRenderer.size; + if (svgSize[0] === 0 || svgSize[1] === 0) { + super.setEmptyImageData(); + return; + } - if (!this._svgRenderer.canvas.width || !this._svgRenderer.canvas.height) { - super.setEmptyImageData(); - return; - } + const maxDimension = Math.ceil(Math.max(this.size[0], this.size[1])); + let testScale = 2; + for (testScale; maxDimension * testScale <= MAX_TEXTURE_DIMENSION; testScale *= 2) { + this._maxTextureScale = testScale; + } - const maxDimension = Math.ceil(Math.max(this.size[0], this.size[1])); - let testScale = 2; - for (testScale; maxDimension * testScale <= MAX_TEXTURE_DIMENSION; testScale *= 2) { - this._maxTextureScale = testScale; - } + this.resetMIPs(); + + if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); + const viewOffset = this._svgRenderer.viewOffset; + this._rotationCenter[0] = rotationCenter[0] - viewOffset[0]; + this._rotationCenter[1] = rotationCenter[1] - viewOffset[1]; - // Create the 1.0 scale MIP at INDEX_OFFSET. - const textureScale = 1; - const mip = this.createMIP(textureScale, () => this.resetMIPs(mip, rotationCenter)); + this.emit(Skin.Events.WasAltered); + }); } } From fdf39fc0f46968245bdbc8defbfe5784d5dd3981 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 13 Oct 2020 10:12:29 -0400 Subject: [PATCH 1902/1971] Add back TextCostumeSkin functionality after develop update I had to move the rotation center functionality into the renderer because the API for setting it externally was removed in the most recent renderer update --- packages/scratch-render/src/RenderWebGL.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index df91555eb3..ef6dca663b 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -11,6 +11,7 @@ const RenderConstants = require('./RenderConstants'); const ShaderManager = require('./ShaderManager'); const SVGSkin = require('./SVGSkin'); const TextBubbleSkin = require('./TextBubbleSkin'); +const TextCostumeSkin = require('./TextCostumeSkin'); const EffectTransform = require('./EffectTransform'); const log = require('./util/log'); @@ -424,6 +425,23 @@ class RenderWebGL extends EventEmitter { this._reskin(skinId, newSkin); } + updateTextCostumeSkin (textState) { + // update existing skin + if (textState.skinId && (this._allSkins[textState.skinId] instanceof TextCostumeSkin)) { + this._allSkins[textState.skinId].setTextAndStyle(textState); + return textState.skinId; + } + + // create and update a new skin + const skinId = this._nextSkinId++; + const newSkin = new TextCostumeSkin(skinId, this); + this._allSkins[skinId] = newSkin; + newSkin.setTextAndStyle(textState); + // this._reskin(skinId, newSkin); // this is erroring- might be necessary? + + return skinId; + } + _reskin (skinId, newSkin) { const oldSkin = this._allSkins[skinId]; this._allSkins[skinId] = newSkin; From dcd4e823983b31c3e791fcfcae812f6a18320444 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 21 Oct 2020 23:15:32 -0400 Subject: [PATCH 1903/1971] working dependencies for blazeface --- packages/scratch-gui/package.json | 3 + .../scratch-gui/src/containers/blocks.jsx | 4 +- .../extension-support/extension-manager.js | 3 +- .../extensions/scratch3_face_sensing/index.js | 297 ++++++++++++++++++ 4 files changed, 304 insertions(+), 3 deletions(-) create mode 100644 packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 804d8b6964..2d5df27450 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -26,6 +26,9 @@ "url": "git+ssh://git@github.com/LLK/scratch-gui.git" }, "dependencies": { + "@tensorflow-models/blazeface": "0.0.5", + "@tensorflow/tfjs-converter": "^1.2.7", + "@tensorflow/tfjs-core": "^1.2.7", "arraybuffer-loader": "^1.0.6", "autoprefixer": "^9.0.1", "base64-loader": "1.0.0", diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 8dff46b110..a3e55e93ad 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -136,8 +136,8 @@ class Blocks extends React.Component { this.setLocale(); } setTimeout(() => { - this.props.vm.extensionManager.loadExtensionURL('text').then(() => { - this.handleCategorySelected('text'); + this.props.vm.extensionManager.loadExtensionURL('faceSensing').then(() => { + this.handleCategorySelected('faceSensing'); }) }, 500) } diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 22e824eef7..743a9575b9 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -24,7 +24,8 @@ const builtinExtensions = { makeymakey: () => require('../extensions/scratch3_makeymakey'), boost: () => require('../extensions/scratch3_boost'), gdxfor: () => require('../extensions/scratch3_gdx_for'), - text: () => require('../extensions/scratch3_text') + text: () => require('../extensions/scratch3_text'), + faceSensing: () => require('../extensions/scratch3_face_sensing') }; /** diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js new file mode 100644 index 0000000000..ccbc584927 --- /dev/null +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -0,0 +1,297 @@ +const ArgumentType = require('../../extension-support/argument-type'); +const BlockType = require('../../extension-support/block-type'); +const MathUtil = require('../../util/math-util'); +const formatMessage = require('format-message'); +const Video = require('../../io/video'); +// const Posenet = require('@tensorflow-models/posenet'); + +const Blazeface = require('@tensorflow-models/blazeface'); +// import * as Blazeface from '@tensorflow-models/blazeface'; + +/** + * Icon svg to be displayed in the blocks category menu, encoded as a data URI. + * @type {string} + */ +// eslint-disable-next-line max-len +const menuIconURI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjBweCIgaGVpZ2h0PSIyMHB4IiB2aWV3Qm94PSIwIDAgMjAgMjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjIgKDY3MTQ1KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5FeHRlbnNpb25zL1NvZnR3YXJlL1ZpZGVvLVNlbnNpbmctTWVudTwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxnIGlkPSJFeHRlbnNpb25zL1NvZnR3YXJlL1ZpZGVvLVNlbnNpbmctTWVudSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9InZpZGVvLW1vdGlvbiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDUuMDAwMDAwKSIgZmlsbC1ydWxlPSJub256ZXJvIj4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjMEVCRDhDIiBvcGFjaXR5PSIwLjI1IiBjeD0iMTYiIGN5PSI4IiByPSIyIj48L2NpcmNsZT4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjMEVCRDhDIiBvcGFjaXR5PSIwLjUiIGN4PSIxNiIgY3k9IjYiIHI9IjIiPjwvY2lyY2xlPgogICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsLUNvcHkiIGZpbGw9IiMwRUJEOEMiIG9wYWNpdHk9IjAuNzUiIGN4PSIxNiIgY3k9IjQiIHI9IjIiPjwvY2lyY2xlPgogICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsIiBmaWxsPSIjMEVCRDhDIiBjeD0iMTYiIGN5PSIyIiByPSIyIj48L2NpcmNsZT4KICAgICAgICAgICAgPHBhdGggZD0iTTExLjMzNTk3MzksMi4yMDk3ODgyNSBMOC4yNSw0LjIwOTk1NjQ5IEw4LjI1LDMuMDUgQzguMjUsMi4wNDQ4ODIyNyA3LjQ2ODU5MDMxLDEuMjUgNi41LDEuMjUgTDIuMDUsMS4yNSBDMS4wMzgwNzExOSwxLjI1IDAuMjUsMi4wMzgwNzExOSAwLjI1LDMuMDUgTDAuMjUsNyBDMC4yNSw3Ljk2MzY5OTM3IDEuMDQyMjQ5MTksOC43NTU5NDg1NiAyLjA1LDguOCBMNi41LDguOCBDNy40NTA4MzAwOSw4LjggOC4yNSw3Ljk3MzI3MjUgOC4yNSw3IEw4LjI1LDUuODU4NDUyNDEgTDguNjI4NjIzOTQsNi4wODU2MjY3NyBMMTEuNDI2Nzc2Nyw3Ljc3MzIyMzMgQzExLjQzNjg5NDMsNy43ODMzNDA5MSAxMS40NzU3NjU1LDcuOCAxMS41LDcuOCBDMTEuNjMzNDkzMiw3LjggMTEuNzUsNy42OTEyNjAzNCAxMS43NSw3LjU1IEwxMS43NSwyLjQgQzExLjc1LDIuNDE4MzgyNjkgMTEuNzIxOTAyOSwyLjM1MjgyMjgyIDExLjY4NTYyNjgsMi4yNzg2MjM5NCBDMTEuNjEyOTUyOCwyLjE1NzUwMDY5IDExLjQ3MDc5NjgsMi4xMjkwNjk1IDExLjMzNTk3MzksMi4yMDk3ODgyNSBaIiBpZD0idmlkZW9fMzdfIiBzdHJva2Utb3BhY2l0eT0iMC4xNSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjAuNSIgZmlsbD0iIzRENEQ0RCI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+'; + +/** + * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI. + * @type {string} + */ +// eslint-disable-next-line max-len +const blockIconURI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSIwIDAgNDAgNDAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjIgKDY3MTQ1KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5FeHRlbnNpb25zL1NvZnR3YXJlL1ZpZGVvLVNlbnNpbmctQmxvY2s8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZyBpZD0iRXh0ZW5zaW9ucy9Tb2Z0d2FyZS9WaWRlby1TZW5zaW5nLUJsb2NrIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2Utb3BhY2l0eT0iMC4xNSI+CiAgICAgICAgPGcgaWQ9InZpZGVvLW1vdGlvbiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDEwLjAwMDAwMCkiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSIjMDAwMDAwIj4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjRkZGRkZGIiBvcGFjaXR5PSIwLjI1IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGN4PSIzMiIgY3k9IjE2IiByPSI0LjUiPjwvY2lyY2xlPgogICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsLUNvcHkiIGZpbGw9IiNGRkZGRkYiIG9wYWNpdHk9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjeD0iMzIiIGN5PSIxMiIgcj0iNC41Ij48L2NpcmNsZT4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjRkZGRkZGIiBvcGFjaXR5PSIwLjc1IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGN4PSIzMiIgY3k9IjgiIHI9IjQuNSI+PC9jaXJjbGU+CiAgICAgICAgICAgIDxjaXJjbGUgaWQ9Ik92YWwiIGZpbGw9IiNGRkZGRkYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgY3g9IjMyIiBjeT0iNCIgcj0iNC41Ij48L2NpcmNsZT4KICAgICAgICAgICAgPHBhdGggZD0iTTIyLjY3MTk0NzcsNC40MTk1NzY0OSBMMTYuNSw4LjQxOTkxMjk4IEwxNi41LDYuMSBDMTYuNSw0LjA4OTc2NDU0IDE0LjkzNzE4MDYsMi41IDEzLDIuNSBMNC4xLDIuNSBDMi4wNzYxNDIzNywyLjUgMC41LDQuMDc2MTQyMzcgMC41LDYuMSBMMC41LDE0IEMwLjUsMTUuOTI3Mzk4NyAyLjA4NDQ5ODM5LDE3LjUxMTg5NzEgNC4xLDE3LjYgTDEzLDE3LjYgQzE0LjkwMTY2MDIsMTcuNiAxNi41LDE1Ljk0NjU0NSAxNi41LDE0IEwxNi41LDExLjcxNjkwNDggTDIyLjc1NzI0NzksMTUuNDcxMjUzNSBMMjIuODUzNTUzNCwxNS41NDY0NDY2IEMyMi44NzM3ODg2LDE1LjU2NjY4MTggMjIuOTUxNTMxLDE1LjYgMjMsMTUuNiBDMjMuMjY2OTg2NSwxNS42IDIzLjUsMTUuMzgyNTIwNyAyMy41LDE1LjEgTDIzLjUsNC44IEMyMy41LDQuODM2NzY1MzggMjMuNDQzODA1OCw0LjcwNTY0NTYzIDIzLjM3MTI1MzUsNC41NTcyNDc4OCBDMjMuMjI1OTA1Niw0LjMxNTAwMTM5IDIyLjk0MTU5MzcsNC4yNTgxMzg5OSAyMi42NzE5NDc3LDQuNDE5NTc2NDkgWiIgaWQ9InZpZGVvXzM3XyIgZmlsbD0iIzRENEQ0RCI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+'; + +/** + * Class for the motion-related blocks in Scratch 3.0 + * @param {Runtime} runtime - the runtime instantiating this block package. + * @constructor + */ +class Scratch3FaceSensingBlocks { + constructor (runtime) { + /** + * The runtime instantiating this block package. + * @type {Runtime} + */ + this.runtime = runtime; + + // Posenet.load({ + // architecture: 'MobileNetV1', + // outputStride: 8, + // inputResolution: 257, + // multiplier: 0.5 + // }).then(net => { + // this.posenet = net; + // if (this.runtime.ioDevices) { + // // Kick off looping the analysis logic. + // this._loop(); + // } + // }); + + console.log('blazeface', Blazeface); + + Blazeface.load().then(model => { + console.log('loaded Blazeface'); + this.blazeface = model; + if (this.runtime.ioDevices) { + // Kick off looping the analysis logic. + this._loop(); + } + }); + + this.currentPose = null; + + /** + * The last millisecond epoch timestamp that the video stream was + * analyzed. + * @type {number} + */ + this._lastUpdate = null; + } + + /** + * After analyzing a frame the amount of milliseconds until another frame + * is analyzed. + * @type {number} + */ + static get INTERVAL () { + return 100; + } + + /** + * Dimensions the video stream is analyzed at after its rendered to the + * sample canvas. + * @type {Array.} + */ + static get DIMENSIONS () { + return [480, 360]; + } + + /** + * Reset the extension's data motion detection data. This will clear out + * for example old frames, so the first analyzed frame will not be compared + * against a frame from before reset was called. + */ + reset () { + + } + + /** + * Occasionally step a loop to sample the video, stamp it to the preview + * skin, and add a TypedArray copy of the canvas's pixel data. + * @private + */ + _loop () { + setTimeout(this._loop.bind(this), Math.max(this.runtime.currentStepTime, Scratch3FaceSensingBlocks.INTERVAL)); + + const time = Date.now(); + if (this._lastUpdate === null) { + this._lastUpdate = time; + } + const offset = time - this._lastUpdate; + if (offset > Scratch3FaceSensingBlocks.INTERVAL) { + const frame = this.runtime.ioDevices.video.getFrame({ + format: Video.FORMAT_IMAGE_DATA, + dimensions: Scratch3FaceSensingBlocks.DIMENSIONS + }); + if (frame) { + const faces = this.blazeface.estimateFaces(frame, false); + if (faces) { + console.log(faces); + } + // const scaleFactor = 0.5; + // const flipHorizontal = false; + // const outputStride = 8; + // this.posenet.estimateSinglePose(frame, scaleFactor, flipHorizontal, outputStride).then( + // pose => { + // this.currentPose = pose; + // this._lastUpdate = time; + // } + // ); + } + } + } + + /** + * @returns {object} metadata for this extension and its blocks. + */ + getInfo () { + // Enable the video layer + this.runtime.ioDevices.video.enableVideo(); + + // Return extension definition + return { + id: 'faceSensing', + name: formatMessage({ + id: 'bodySensing.categoryName', + default: 'Body Sensing', + description: 'Name of body sensing extension' + }), + blockIconURI: blockIconURI, + menuIconURI: menuIconURI, + blocks: [ + { + opcode: 'whenTilted', + text: formatMessage({ + id: 'bodySensing.whenTilted', + default: 'when head tilts [DIRECTION]', + description: '' + }), + blockType: BlockType.HAT, + arguments: { + DIRECTION: { + type: ArgumentType.STRING, + menu: 'TILT', + defaultValue: 'left' + } + } + }, + { + opcode: 'goToPart', + text: formatMessage({ + id: 'bodySensing.goToPart', + default: 'go to [PART]', + description: '' + }), + blockType: BlockType.COMMAND, + arguments: { + PART: { + type: ArgumentType.STRING, + menu: 'PART', + defaultValue: '0' + } + } + }, + { + opcode: 'headDirection', + text: formatMessage({ + id: 'bodySensing.headDirection', + default: 'head direction', + description: '' + }), + blockType: BlockType.REPORTER + }, + { + opcode: 'partX', + text: formatMessage({ + id: 'bodySensing.partX', + default: 'x position of [PART]', + description: '' + }), + arguments: { + PART: { + type: ArgumentType.NUMBER, + menu: 'PART', + defaultValue: '0' + } + }, + blockType: BlockType.REPORTER + }, + { + opcode: 'partY', + text: formatMessage({ + id: 'bodySensing.partY', + default: 'y position of [PART]', + description: '' + }), + arguments: { + PART: { + type: ArgumentType.NUMBER, + menu: 'PART', + defaultValue: '0' + } + }, + blockType: BlockType.REPORTER + } + ], + menus: { + PART: [ + {text: 'nose', value: '0'}, + {text: 'left eye', value: '2'}, // left/right seem reversed? + {text: 'right eye', value: '1'}, + {text: 'left ear', value: '4'}, + {text: 'right ear', value: '3'}, + {text: 'left wrist', value: '9'}, + {text: 'right wrist', value: '10'} + ], + TILT: [ + {text: 'left', value: 'left'}, + {text: 'right', value: 'right'} + ] + } + }; + } + + getPartPosition (part) { + const defaultPos = {x: 0, y: 0}; + if (!this.currentPose) return defaultPos; + if (!this.currentPose.keypoints) return defaultPos; + const result = this.currentPose.keypoints[Number(part)]; + if (result) { + return this.toScratchCoords(result.position); + } + return defaultPos; + } + + toScratchCoords (position) { + return { + x: position.x - 240, + y: 180 - position.y + }; + } + + partX (args) { + return this.getPartPosition(args.PART).x; + } + + partY (args) { + return this.getPartPosition(args.PART).y; + } + + whenTilted (args) { + const TILT_THRESHOLD = 10; + if (args.DIRECTION === 'left') { + return this.headDirection() < (90 - TILT_THRESHOLD); + } + if (args.DIRECTION === 'right') { + return this.headDirection() > (90 + TILT_THRESHOLD); + } + return false; + } + + goToPart (args, util) { + const pos = this.getPartPosition(args.PART); + util.target.setXY(pos.x, pos.y); + } + + headDirection () { + const leftEyePos = this.getPartPosition(2); + const rightEyePos = this.getPartPosition(1); + const dx = rightEyePos.x - leftEyePos.x; + const dy = rightEyePos.y - leftEyePos.y; + const direction = 90 - MathUtil.radToDeg(Math.atan2(dy, dx)); + return Math.round(direction); + } +} + +module.exports = Scratch3FaceSensingBlocks; From fa4081f364f81b220a154ff537af752f9d00db27 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 21 Oct 2020 23:35:45 -0400 Subject: [PATCH 1904/1971] all blocks working! --- .../extensions/scratch3_face_sensing/index.js | 43 ++++++++----------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index ccbc584927..a17016fe97 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -115,19 +115,13 @@ class Scratch3FaceSensingBlocks { dimensions: Scratch3FaceSensingBlocks.DIMENSIONS }); if (frame) { - const faces = this.blazeface.estimateFaces(frame, false); - if (faces) { - console.log(faces); - } - // const scaleFactor = 0.5; - // const flipHorizontal = false; - // const outputStride = 8; - // this.posenet.estimateSinglePose(frame, scaleFactor, flipHorizontal, outputStride).then( - // pose => { - // this.currentPose = pose; - // this._lastUpdate = time; - // } - // ); + this.blazeface.estimateFaces(frame, false).then(faces => { + if (faces) { + this.currentFace = faces[0]; + this._lastUpdate = time; + console.log(faces); + } + }) } } } @@ -226,13 +220,12 @@ class Scratch3FaceSensingBlocks { ], menus: { PART: [ - {text: 'nose', value: '0'}, - {text: 'left eye', value: '2'}, // left/right seem reversed? + {text: 'nose', value: '2'}, + {text: 'mouth', value: '3'}, + {text: 'left eye', value: '0'}, {text: 'right eye', value: '1'}, {text: 'left ear', value: '4'}, - {text: 'right ear', value: '3'}, - {text: 'left wrist', value: '9'}, - {text: 'right wrist', value: '10'} + {text: 'right ear', value: '5'} ], TILT: [ {text: 'left', value: 'left'}, @@ -244,19 +237,19 @@ class Scratch3FaceSensingBlocks { getPartPosition (part) { const defaultPos = {x: 0, y: 0}; - if (!this.currentPose) return defaultPos; - if (!this.currentPose.keypoints) return defaultPos; - const result = this.currentPose.keypoints[Number(part)]; + if (!this.currentFace) return defaultPos; + if (!this.currentFace.landmarks) return defaultPos; + const result = this.currentFace.landmarks[Number(part)]; if (result) { - return this.toScratchCoords(result.position); + return this.toScratchCoords(result); } return defaultPos; } toScratchCoords (position) { return { - x: position.x - 240, - y: 180 - position.y + x: position[0] - 240, + y: 180 - position[1] }; } @@ -285,7 +278,7 @@ class Scratch3FaceSensingBlocks { } headDirection () { - const leftEyePos = this.getPartPosition(2); + const leftEyePos = this.getPartPosition(0); const rightEyePos = this.getPartPosition(1); const dx = rightEyePos.x - leftEyePos.x; const dy = rightEyePos.y - leftEyePos.y; From f41353cf29f7c80a0733fb60c03dbccc9b24df8c Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 23 Oct 2020 13:27:22 -0400 Subject: [PATCH 1905/1971] run at 30fps --- .../scratch-vm/src/extensions/scratch3_face_sensing/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index a17016fe97..539455b78a 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -75,7 +75,7 @@ class Scratch3FaceSensingBlocks { * @type {number} */ static get INTERVAL () { - return 100; + return 33; } /** From e8b829d0e098f6b82142643abb194ef44c20742a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 26 Oct 2020 09:48:49 -0400 Subject: [PATCH 1906/1971] Override clamping behvior for text skins --- packages/scratch-render/src/RenderWebGL.js | 11 +++++++++++ packages/scratch-vm/src/sprites/rendered-target.js | 8 +++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index ef6dca663b..2f665d31dc 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -754,6 +754,17 @@ class RenderWebGL extends EventEmitter { return this.getSkinSize(drawable.skin.id); } + /** + * Get the max scale the skin prefers. Only relevant for + * skins that have do not scale up normally, like reflowing text. + * @param {int} drawableID The ID of the Drawable to measure. + * @return {number} Max scale preferred by the skin, or null. + */ + getCurrentSkinMaxScale(drawableID) { + const drawable = this._allDrawables[drawableID]; + return this._allSkins[drawable.skin.id].maxScale; + } + /** * Get the size of a skin by ID. * @param {int} skinID The ID of the Skin to measure. diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index b0c1ebfed9..70dc2dbeb0 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -374,10 +374,16 @@ class RenderedTarget extends Target { const origW = costumeSize[0]; const origH = costumeSize[1]; const minScale = Math.min(1, Math.max(5 / origW, 5 / origH)); - const maxScale = Math.min( + let maxScale = Math.min( (1.5 * this.runtime.constructor.STAGE_WIDTH) / origW, (1.5 * this.runtime.constructor.STAGE_HEIGHT) / origH ); + // Allow special skins to override max scale, like the textCostumeSkin which + // reflows text on its own so clamping is not needed. + let overrideScale = this.renderer.getCurrentSkinMaxScale(this.drawableID); + if (overrideScale !== null) { + maxScale = overrideScale; + } this.size = MathUtil.clamp(size / 100, minScale, maxScale) * 100; const {direction, scale} = this._getRenderedDirectionAndScale(); this.renderer.updateDrawableDirectionScale(this.drawableID, direction, scale); From 7cce888e03a6f13c8071e29c8f8c9d1dee65ad50 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 26 Oct 2020 12:54:48 -0400 Subject: [PATCH 1907/1971] Add browser entry point for gui --- packages/scratch-gui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 804d8b6964..9358bebe9a 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "description": "GraphicaL User Interface for creating and running Scratch 3.0 projects", "main": "./dist/scratch-gui.js", + "browser": "./src/index.js", "scripts": { "build": "npm run clean && webpack --colors --bail", "clean": "rimraf ./build && mkdirp build && rimraf ./dist && mkdirp dist", From 966c4751069fc6f2957b6c94af7fc25ccd86eb0b Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 27 Oct 2020 16:19:07 -0400 Subject: [PATCH 1908/1971] increase auto-scroll time delay --- packages/scratch-gui/src/containers/blocks.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 8dff46b110..fd05f35bc9 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -138,8 +138,8 @@ class Blocks extends React.Component { setTimeout(() => { this.props.vm.extensionManager.loadExtensionURL('text').then(() => { this.handleCategorySelected('text'); - }) - }, 500) + }); + }, 3000); } shouldComponentUpdate (nextProps, nextState) { return ( From af95e9171c22d5e46ecd81d621f95d7aef13a3be Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 27 Oct 2020 16:32:29 -0400 Subject: [PATCH 1909/1971] update extension library tile --- .../src/lib/libraries/extensions/index.jsx | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index 65a0f1ad9c..35ac7dddda 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -7,6 +7,9 @@ import musicInsetIconURL from './music/music-small.svg'; import penIconURL from './pen/pen.png'; import penInsetIconURL from './pen/pen-small.svg'; +import textIconURL from './text/text.png'; +import textInsetIconURL from './text/text-small.svg'; + import videoSensingIconURL from './videoSensing/video-sensing.png'; import videoSensingInsetIconURL from './videoSensing/video-sensing-small.svg'; @@ -47,26 +50,6 @@ import gdxforConnectionIconURL from './gdxfor/gdxfor-illustration.svg'; import gdxforConnectionSmallIconURL from './gdxfor/gdxfor-small.svg'; export default [ - { - name: ( - - ), - extensionId: 'text', - iconURL: makeymakeyIconURL, - insetIconURL: makeymakeyInsetIconURL, - description: ( - - ), - featured: true - }, { name: ( + ), + extensionId: 'text', + iconURL: textIconURL, + insetIconURL: textInsetIconURL, + description: ( + + ), + featured: true + }, { name: ( Date: Mon, 9 Nov 2020 11:25:26 -0500 Subject: [PATCH 1910/1971] add face detected hat, boolean and probability blocks --- .../extensions/scratch3_face_sensing/index.js | 85 ++++++++++++------- 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index 539455b78a..bf37c23392 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -35,23 +35,7 @@ class Scratch3FaceSensingBlocks { */ this.runtime = runtime; - // Posenet.load({ - // architecture: 'MobileNetV1', - // outputStride: 8, - // inputResolution: 257, - // multiplier: 0.5 - // }).then(net => { - // this.posenet = net; - // if (this.runtime.ioDevices) { - // // Kick off looping the analysis logic. - // this._loop(); - // } - // }); - - console.log('blazeface', Blazeface); - Blazeface.load().then(model => { - console.log('loaded Blazeface'); this.blazeface = model; if (this.runtime.ioDevices) { // Kick off looping the analysis logic. @@ -118,10 +102,9 @@ class Scratch3FaceSensingBlocks { this.blazeface.estimateFaces(frame, false).then(faces => { if (faces) { this.currentFace = faces[0]; - this._lastUpdate = time; - console.log(faces); } - }) + this._lastUpdate = time; + }); } } } @@ -137,17 +120,35 @@ class Scratch3FaceSensingBlocks { return { id: 'faceSensing', name: formatMessage({ - id: 'bodySensing.categoryName', - default: 'Body Sensing', - description: 'Name of body sensing extension' + id: 'faceSensing.categoryName', + default: 'Face Sensing', + description: 'Name of face sensing extension' }), blockIconURI: blockIconURI, menuIconURI: menuIconURI, blocks: [ + { + opcode: 'whenFaceDetected', + text: formatMessage({ + id: 'faceSensing.whenFaceDetected', + default: 'when a face is detected', + description: '' + }), + blockType: BlockType.HAT + }, + { + opcode: 'faceIsDetected', + text: formatMessage({ + id: 'faceSensing.faceDetected', + default: 'face is detected?', + description: '' + }), + blockType: BlockType.BOOLEAN + }, { opcode: 'whenTilted', text: formatMessage({ - id: 'bodySensing.whenTilted', + id: 'faceSensing.whenTilted', default: 'when head tilts [DIRECTION]', description: '' }), @@ -163,7 +164,7 @@ class Scratch3FaceSensingBlocks { { opcode: 'goToPart', text: formatMessage({ - id: 'bodySensing.goToPart', + id: 'faceSensing.goToPart', default: 'go to [PART]', description: '' }), @@ -172,14 +173,14 @@ class Scratch3FaceSensingBlocks { PART: { type: ArgumentType.STRING, menu: 'PART', - defaultValue: '0' + defaultValue: '2' } } }, { opcode: 'headDirection', text: formatMessage({ - id: 'bodySensing.headDirection', + id: 'faceSensing.headDirection', default: 'head direction', description: '' }), @@ -188,7 +189,7 @@ class Scratch3FaceSensingBlocks { { opcode: 'partX', text: formatMessage({ - id: 'bodySensing.partX', + id: 'faceSensing.partX', default: 'x position of [PART]', description: '' }), @@ -196,7 +197,7 @@ class Scratch3FaceSensingBlocks { PART: { type: ArgumentType.NUMBER, menu: 'PART', - defaultValue: '0' + defaultValue: '2' } }, blockType: BlockType.REPORTER @@ -204,7 +205,7 @@ class Scratch3FaceSensingBlocks { { opcode: 'partY', text: formatMessage({ - id: 'bodySensing.partY', + id: 'faceSensing.partY', default: 'y position of [PART]', description: '' }), @@ -212,10 +213,19 @@ class Scratch3FaceSensingBlocks { PART: { type: ArgumentType.NUMBER, menu: 'PART', - defaultValue: '0' + defaultValue: '2' } }, blockType: BlockType.REPORTER + }, + { + opcode: 'probability', + text: formatMessage({ + id: 'faceSensing.probability', + default: 'probability of face detection', + description: '' + }), + blockType: BlockType.REPORTER } ], menus: { @@ -235,6 +245,21 @@ class Scratch3FaceSensingBlocks { }; } + whenFaceDetected () { + return this.currentFace; + } + + faceIsDetected () { + return !!this.currentFace; + } + + probability () { + if (this.currentFace) { + return Math.round(this.currentFace.probability * 100); + } + return 0; + } + getPartPosition (part) { const defaultPos = {x: 0, y: 0}; if (!this.currentFace) return defaultPos; From 1481724f572f905a8d9723ffc09de26696778276 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 9 Nov 2020 11:58:13 -0500 Subject: [PATCH 1911/1971] Add number of faces and face size reporters --- .../extensions/scratch3_face_sensing/index.js | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index bf37c23392..b03ccf147d 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -102,6 +102,7 @@ class Scratch3FaceSensingBlocks { this.blazeface.estimateFaces(frame, false).then(faces => { if (faces) { this.currentFace = faces[0]; + this.allFaces = faces; } this._lastUpdate = time; }); @@ -218,6 +219,15 @@ class Scratch3FaceSensingBlocks { }, blockType: BlockType.REPORTER }, + { + opcode: 'faceSize', + text: formatMessage({ + id: 'faceSensing.faceSize', + default: 'face size', + description: '' + }), + blockType: BlockType.REPORTER + }, { opcode: 'probability', text: formatMessage({ @@ -226,6 +236,15 @@ class Scratch3FaceSensingBlocks { description: '' }), blockType: BlockType.REPORTER + }, + { + opcode: 'numberOfFaces', + text: formatMessage({ + id: 'faceSensing.numberOfFaces', + default: 'number of faces', + description: '' + }), + blockType: BlockType.REPORTER } ], menus: { @@ -253,6 +272,10 @@ class Scratch3FaceSensingBlocks { return !!this.currentFace; } + numberOfFaces () { + return this.allFaces.length; + } + probability () { if (this.currentFace) { return Math.round(this.currentFace.probability * 100); @@ -260,6 +283,13 @@ class Scratch3FaceSensingBlocks { return 0; } + faceSize () { + if (this.currentFace) { + return Math.round(this.currentFace.bottomRight[0] - this.currentFace.topLeft[0]); + } + return 0; + } + getPartPosition (part) { const defaultPos = {x: 0, y: 0}; if (!this.currentFace) return defaultPos; From 9a09c46e93bc26c89d94bc92dd1cf8f8963e3aa2 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 9 Nov 2020 17:07:40 -0500 Subject: [PATCH 1912/1971] add attach block --- .../extensions/scratch3_face_sensing/index.js | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index b03ccf147d..c3c406a87b 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -1,5 +1,6 @@ const ArgumentType = require('../../extension-support/argument-type'); const BlockType = require('../../extension-support/block-type'); +const Clone = require('../../util/clone'); const MathUtil = require('../../util/math-util'); const formatMessage = require('format-message'); const Video = require('../../io/video'); @@ -51,6 +52,9 @@ class Scratch3FaceSensingBlocks { * @type {number} */ this._lastUpdate = null; + + this._clearAttachments = this._clearAttachments.bind(this); + this.runtime.on('PROJECT_STOP_ALL', this._clearAttachments); } /** @@ -103,6 +107,7 @@ class Scratch3FaceSensingBlocks { if (faces) { this.currentFace = faces[0]; this.allFaces = faces; + this.updateAttachments(); } this._lastUpdate = time; }); @@ -110,6 +115,25 @@ class Scratch3FaceSensingBlocks { } } + _getFaceSensingState (target) { + let faceSensingState = target.getCustomState(Scratch3FaceSensingBlocks.STATE_KEY); + if (!faceSensingState) { + faceSensingState = Clone.simple(Scratch3FaceSensingBlocks.DEFAULT_FACE_SENSING_STATE); + target.setCustomState(Scratch3FaceSensingBlocks.STATE_KEY, faceSensingState); + } + return faceSensingState; + } + + static get STATE_KEY () { + return 'Scratch.faceSensing'; + } + + static get DEFAULT_FACE_SENSING_STATE () { + return { + attachedToPartNumber: null + }; + } + /** * @returns {object} metadata for this extension and its blocks. */ @@ -178,6 +202,22 @@ class Scratch3FaceSensingBlocks { } } }, + { + opcode: 'attachToPart', + text: formatMessage({ + id: 'faceSensing.attachToPart', + default: 'attach to [PART]', + description: '' + }), + blockType: BlockType.COMMAND, + arguments: { + PART: { + type: ArgumentType.STRING, + menu: 'PART', + defaultValue: '2' + } + } + }, { opcode: 'headDirection', text: formatMessage({ @@ -332,6 +372,30 @@ class Scratch3FaceSensingBlocks { util.target.setXY(pos.x, pos.y); } + attachToPart (args, util) { + const state = this._getFaceSensingState(util.target); + state.attachedToPartNumber = args.PART; + } + + updateAttachments () { + this.runtime.targets.forEach(target => { + const state = this._getFaceSensingState(target); + if (state.attachedToPartNumber) { + const pos = this.getPartPosition(state.attachedToPartNumber); + target.setXY(pos.x, pos.y); + target.setDirection(this.headDirection()); + target.setSize(this.faceSize()); + } + }); + } + + _clearAttachments () { + this.runtime.targets.forEach(target => { + const state = this._getFaceSensingState(target); + state.attachedToPartNumber = null; + }); + } + headDirection () { const leftEyePos = this.getPartPosition(0); const rightEyePos = this.getPartPosition(1); From 9e795dcc1bec16210dc76e94ee7be1b4621dae06 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 12 Nov 2020 22:37:44 -0500 Subject: [PATCH 1913/1971] make motion and size blocks work after attaching but in a slightly incorrect way (should really use matrix math to transform coordinate systems) --- .../extensions/scratch3_face_sensing/index.js | 42 ++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index c3c406a87b..c156baab0a 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -130,7 +130,15 @@ class Scratch3FaceSensingBlocks { static get DEFAULT_FACE_SENSING_STATE () { return { - attachedToPartNumber: null + attachedToPartNumber: null, + prevX: 0, + offsetX: 0, + prevY: 0, + offsetY: 0, + prevSize: 100, + offsetSize: 0, + prevDirection: 0, + offsetDirection: 0 }; } @@ -375,16 +383,40 @@ class Scratch3FaceSensingBlocks { attachToPart (args, util) { const state = this._getFaceSensingState(util.target); state.attachedToPartNumber = args.PART; + state.offsetX = 0; + state.offsetY = 0; + state.prevX = util.target.x; + state.prevY = util.target.y; + state.offsetDirection = 0; + state.prevDirection = util.target.direction; + state.offsetSize = 0; + state.prevSize = util.target.size; } updateAttachments () { this.runtime.targets.forEach(target => { const state = this._getFaceSensingState(target); if (state.attachedToPartNumber) { - const pos = this.getPartPosition(state.attachedToPartNumber); - target.setXY(pos.x, pos.y); - target.setDirection(this.headDirection()); - target.setSize(this.faceSize()); + const partPos = this.getPartPosition(state.attachedToPartNumber); + if (target.x !== state.prevX) { + state.offsetX += target.x - state.prevX; + } + if (target.y !== state.prevY) { + state.offsetY += target.y - state.prevY; + } + if (target.direction !== state.prevDirection) { + state.offsetDirection += target.direction - state.prevDirection; + } + if (target.size !== state.prevSize) { + state.offsetSize += target.size - state.prevSize; + } + target.setXY(partPos.x + state.offsetX, partPos.y + state.offsetY); + target.setDirection(this.headDirection() + state.offsetDirection); + target.setSize(this.faceSize() + state.offsetSize); + state.prevX = target.x; + state.prevY = target.y; + state.prevDirection = target.direction; + state.prevSize = target.size; } }); } From d0364110fa467725c1addd25c9c6ef99006a48f3 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 18 Nov 2020 16:59:06 -0500 Subject: [PATCH 1914/1971] =?UTF-8?q?always=20use=20=E2=80=9Cface=E2=80=9D?= =?UTF-8?q?=20instead=20of=20=E2=80=9Chead=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extensions/scratch3_face_sensing/index.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index c156baab0a..440d484585 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -182,7 +182,7 @@ class Scratch3FaceSensingBlocks { opcode: 'whenTilted', text: formatMessage({ id: 'faceSensing.whenTilted', - default: 'when head tilts [DIRECTION]', + default: 'when face tilts [DIRECTION]', description: '' }), blockType: BlockType.HAT, @@ -227,10 +227,10 @@ class Scratch3FaceSensingBlocks { } }, { - opcode: 'headDirection', + opcode: 'faceTilt', text: formatMessage({ - id: 'faceSensing.headDirection', - default: 'head direction', + id: 'faceSensing.faceTilt', + default: 'face tilt', description: '' }), blockType: BlockType.REPORTER @@ -367,10 +367,10 @@ class Scratch3FaceSensingBlocks { whenTilted (args) { const TILT_THRESHOLD = 10; if (args.DIRECTION === 'left') { - return this.headDirection() < (90 - TILT_THRESHOLD); + return this.faceTilt() < (90 - TILT_THRESHOLD); } if (args.DIRECTION === 'right') { - return this.headDirection() > (90 + TILT_THRESHOLD); + return this.faceTilt() > (90 + TILT_THRESHOLD); } return false; } @@ -411,7 +411,7 @@ class Scratch3FaceSensingBlocks { state.offsetSize += target.size - state.prevSize; } target.setXY(partPos.x + state.offsetX, partPos.y + state.offsetY); - target.setDirection(this.headDirection() + state.offsetDirection); + target.setDirection(this.faceTilt() + state.offsetDirection); target.setSize(this.faceSize() + state.offsetSize); state.prevX = target.x; state.prevY = target.y; @@ -428,7 +428,7 @@ class Scratch3FaceSensingBlocks { }); } - headDirection () { + faceTilt () { const leftEyePos = this.getPartPosition(0); const rightEyePos = this.getPartPosition(1); const dx = rightEyePos.x - leftEyePos.x; From 9b0f771ba2baf3082c8908958b9524e2a80090ee Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 18 Nov 2020 16:59:39 -0500 Subject: [PATCH 1915/1971] remove attach block, add point in face tilt dir and set size to face size --- .../extensions/scratch3_face_sensing/index.js | 50 ++++++++++++++----- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index 440d484585..4050bff71a 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -211,21 +211,39 @@ class Scratch3FaceSensingBlocks { } }, { - opcode: 'attachToPart', + opcode: 'pointInFaceTiltDirection', text: formatMessage({ - id: 'faceSensing.attachToPart', - default: 'attach to [PART]', + id: 'faceSensing.pointInFaceTiltDirection', + default: 'point in direction of face tilt', description: '' }), - blockType: BlockType.COMMAND, - arguments: { - PART: { - type: ArgumentType.STRING, - menu: 'PART', - defaultValue: '2' - } - } + blockType: BlockType.COMMAND + }, + { + opcode: 'setSizeToFaceSize', + text: formatMessage({ + id: 'faceSensing.setSizeToFaceSize', + default: 'set size to face size', + description: '' + }), + blockType: BlockType.COMMAND }, + // { + // opcode: 'attachToPart', + // text: formatMessage({ + // id: 'faceSensing.attachToPart', + // default: 'attach to [PART]', + // description: '' + // }), + // blockType: BlockType.COMMAND, + // arguments: { + // PART: { + // type: ArgumentType.STRING, + // menu: 'PART', + // defaultValue: '2' + // } + // } + // }, { opcode: 'faceTilt', text: formatMessage({ @@ -335,7 +353,7 @@ class Scratch3FaceSensingBlocks { if (this.currentFace) { return Math.round(this.currentFace.bottomRight[0] - this.currentFace.topLeft[0]); } - return 0; + return 100; } getPartPosition (part) { @@ -380,6 +398,14 @@ class Scratch3FaceSensingBlocks { util.target.setXY(pos.x, pos.y); } + pointInFaceTiltDirection (args, util) { + util.target.setDirection(this.faceTilt()); + } + + setSizeToFaceSize (args, util) { + util.target.setSize(this.faceSize()); + } + attachToPart (args, util) { const state = this._getFaceSensingState(util.target); state.attachedToPartNumber = args.PART; From 491dc3ff9ec33a0713fb580af6222989a1019093 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 18 Nov 2020 17:15:38 -0500 Subject: [PATCH 1916/1971] clean up and re-arrange blocks menu --- .../extensions/scratch3_face_sensing/index.js | 120 +++++++++--------- 1 file changed, 61 insertions(+), 59 deletions(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index 4050bff71a..4e33113fa6 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -169,15 +169,6 @@ class Scratch3FaceSensingBlocks { }), blockType: BlockType.HAT }, - { - opcode: 'faceIsDetected', - text: formatMessage({ - id: 'faceSensing.faceDetected', - default: 'face is detected?', - description: '' - }), - blockType: BlockType.BOOLEAN - }, { opcode: 'whenTilted', text: formatMessage({ @@ -194,6 +185,7 @@ class Scratch3FaceSensingBlocks { } } }, + '---', { opcode: 'goToPart', text: formatMessage({ @@ -228,6 +220,16 @@ class Scratch3FaceSensingBlocks { }), blockType: BlockType.COMMAND }, + '---', + { + opcode: 'faceIsDetected', + text: formatMessage({ + id: 'faceSensing.faceDetected', + default: 'face is detected?', + description: '' + }), + blockType: BlockType.BOOLEAN + }, // { // opcode: 'attachToPart', // text: formatMessage({ @@ -253,38 +255,38 @@ class Scratch3FaceSensingBlocks { }), blockType: BlockType.REPORTER }, - { - opcode: 'partX', - text: formatMessage({ - id: 'faceSensing.partX', - default: 'x position of [PART]', - description: '' - }), - arguments: { - PART: { - type: ArgumentType.NUMBER, - menu: 'PART', - defaultValue: '2' - } - }, - blockType: BlockType.REPORTER - }, - { - opcode: 'partY', - text: formatMessage({ - id: 'faceSensing.partY', - default: 'y position of [PART]', - description: '' - }), - arguments: { - PART: { - type: ArgumentType.NUMBER, - menu: 'PART', - defaultValue: '2' - } - }, - blockType: BlockType.REPORTER - }, + // { + // opcode: 'partX', + // text: formatMessage({ + // id: 'faceSensing.partX', + // default: 'x position of [PART]', + // description: '' + // }), + // arguments: { + // PART: { + // type: ArgumentType.NUMBER, + // menu: 'PART', + // defaultValue: '2' + // } + // }, + // blockType: BlockType.REPORTER + // }, + // { + // opcode: 'partY', + // text: formatMessage({ + // id: 'faceSensing.partY', + // default: 'y position of [PART]', + // description: '' + // }), + // arguments: { + // PART: { + // type: ArgumentType.NUMBER, + // menu: 'PART', + // defaultValue: '2' + // } + // }, + // blockType: BlockType.REPORTER + // }, { opcode: 'faceSize', text: formatMessage({ @@ -293,25 +295,25 @@ class Scratch3FaceSensingBlocks { description: '' }), blockType: BlockType.REPORTER - }, - { - opcode: 'probability', - text: formatMessage({ - id: 'faceSensing.probability', - default: 'probability of face detection', - description: '' - }), - blockType: BlockType.REPORTER - }, - { - opcode: 'numberOfFaces', - text: formatMessage({ - id: 'faceSensing.numberOfFaces', - default: 'number of faces', - description: '' - }), - blockType: BlockType.REPORTER } + // { + // opcode: 'probability', + // text: formatMessage({ + // id: 'faceSensing.probability', + // default: 'probability of face detection', + // description: '' + // }), + // blockType: BlockType.REPORTER + // }, + // { + // opcode: 'numberOfFaces', + // text: formatMessage({ + // id: 'faceSensing.numberOfFaces', + // default: 'number of faces', + // description: '' + // }), + // blockType: BlockType.REPORTER + // } ], menus: { PART: [ From 5b3d6bb0c7c08e5b14f9331fe4b98ef9ad74d2b0 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 18 Nov 2020 23:28:20 -0500 Subject: [PATCH 1917/1971] Add points between eyes and top of head --- .../extensions/scratch3_face_sensing/index.js | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index 4e33113fa6..fb16aeb61d 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -106,8 +106,6 @@ class Scratch3FaceSensingBlocks { this.blazeface.estimateFaces(frame, false).then(faces => { if (faces) { this.currentFace = faces[0]; - this.allFaces = faces; - this.updateAttachments(); } this._lastUpdate = time; }); @@ -321,8 +319,10 @@ class Scratch3FaceSensingBlocks { {text: 'mouth', value: '3'}, {text: 'left eye', value: '0'}, {text: 'right eye', value: '1'}, + {text: 'between eyes', value: '6'}, {text: 'left ear', value: '4'}, - {text: 'right ear', value: '5'} + {text: 'right ear', value: '5'}, + {text: 'top of head', value: '7'} ], TILT: [ {text: 'left', value: 'left'}, @@ -332,6 +332,40 @@ class Scratch3FaceSensingBlocks { }; } + getBetweenEyesPosition () { + // center point of a line between the eyes + const leftEye = this.getPartPosition(0); + const rightEye = this.getPartPosition(1); + const betweenEyes = {x: 0, y: 0}; + betweenEyes.x = leftEye.x + ((rightEye.x - leftEye.x) / 2); + betweenEyes.y = leftEye.y + ((rightEye.y - leftEye.y) / 2); + return betweenEyes; + } + + getTopOfHeadPosition () { + // Estimated top of the head point: + // Make a line perpendicular to the line between the eyes, through + // its center, and move upward along it the distance from the point + // between the eyes to the mouth. + const leftEyePos = this.getPartPosition(0); + const rightEyePos = this.getPartPosition(1); + const mouthPos = this.getPartPosition(3); + const dx = rightEyePos.x - leftEyePos.x; + const dy = rightEyePos.y - leftEyePos.y; + const directionRads = Math.atan2(dy, dx) + (Math.PI / 2); + const betweenEyesPos = this.getBetweenEyesPosition(); + + const distX = betweenEyesPos.x - mouthPos.x; + const distY = betweenEyesPos.y - mouthPos.y; + const mouthDistance = Math.sqrt((distX * distX) + (distY * distY)); + + const topOfHeadPosition = {x: 0, y: 0}; + topOfHeadPosition.x = betweenEyesPos.x + (mouthDistance * Math.cos(directionRads)); + topOfHeadPosition.y = betweenEyesPos.y + (mouthDistance * Math.sin(directionRads)); + + return topOfHeadPosition; + } + whenFaceDetected () { return this.currentFace; } @@ -362,6 +396,12 @@ class Scratch3FaceSensingBlocks { const defaultPos = {x: 0, y: 0}; if (!this.currentFace) return defaultPos; if (!this.currentFace.landmarks) return defaultPos; + if (Number(part) === 6) { + return this.getBetweenEyesPosition(); + } + if (Number(part) === 7) { + return this.getTopOfHeadPosition(); + } const result = this.currentFace.landmarks[Number(part)]; if (result) { return this.toScratchCoords(result); From 0dc1c6f4e9650597f63ae21dc8eaecdce23fa99c Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 20 Nov 2020 10:58:42 -0500 Subject: [PATCH 1918/1971] use a distance function --- .../src/extensions/scratch3_face_sensing/index.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index fb16aeb61d..72788fb8fd 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -354,10 +354,7 @@ class Scratch3FaceSensingBlocks { const dy = rightEyePos.y - leftEyePos.y; const directionRads = Math.atan2(dy, dx) + (Math.PI / 2); const betweenEyesPos = this.getBetweenEyesPosition(); - - const distX = betweenEyesPos.x - mouthPos.x; - const distY = betweenEyesPos.y - mouthPos.y; - const mouthDistance = Math.sqrt((distX * distX) + (distY * distY)); + const mouthDistance = this.distance(betweenEyesPos, mouthPos); const topOfHeadPosition = {x: 0, y: 0}; topOfHeadPosition.x = betweenEyesPos.x + (mouthDistance * Math.cos(directionRads)); @@ -366,6 +363,12 @@ class Scratch3FaceSensingBlocks { return topOfHeadPosition; } + distance (pointA, pointB) { + const dx = pointA.x - pointB.x; + const dy = pointA.y - pointB.y; + return Math.sqrt((dx * dx) + (dy * dy)); + } + whenFaceDetected () { return this.currentFace; } From 081b4aaf7d5bed1cc4c81816494c6246a0b70fb5 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 20 Nov 2020 12:20:27 -0500 Subject: [PATCH 1919/1971] =?UTF-8?q?Add=20=E2=80=9Cwhen=20this=20sprite?= =?UTF-8?q?=20touches=20nose=E2=80=9D=20block?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extensions/scratch3_face_sensing/index.js | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index 72788fb8fd..9cc50e6b04 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -167,6 +167,22 @@ class Scratch3FaceSensingBlocks { }), blockType: BlockType.HAT }, + { + opcode: 'whenSpriteTouchesPart', + text: formatMessage({ + id: 'faceSensing.whenSpriteTouchesPart', + default: 'when this sprite touches [PART]', + description: '' + }), + blockType: BlockType.HAT, + arguments: { + PART: { + type: ArgumentType.STRING, + menu: 'PART', + defaultValue: '2' + } + } + }, { opcode: 'whenTilted', text: formatMessage({ @@ -369,6 +385,19 @@ class Scratch3FaceSensingBlocks { return Math.sqrt((dx * dx) + (dy * dy)); } + whenSpriteTouchesPart (args, util) { + if (!this.currentFace) return false; + if (!this.currentFace.landmarks) return false; + const pos = this.getPartPosition(args.PART); + const drawable = this.runtime.renderer._allDrawables[util.target.drawableID]; + if (drawable) { + drawable.updateCPURenderAttributes(); + return drawable.isTouching([pos.x, pos.y]); + } + return false; + // return util.target.isTouchingPoint(pos.x, pos.y); // nope, this takes client coords, as from mouse position + } + whenFaceDetected () { return this.currentFace; } From ecb113ff4c5c11e68af1d02c043e229acadf55a8 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 20 Nov 2020 17:03:36 -0500 Subject: [PATCH 1920/1971] =?UTF-8?q?Simpler=20=E2=80=9Cwhen=20face=20touc?= =?UTF-8?q?hes=E2=80=9D=20using=20face=20bounds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/scratch-render/src/RenderWebGL.js | 21 +++++++++++++ .../extensions/scratch3_face_sensing/index.js | 30 ++++++------------- .../scratch-vm/src/sprites/rendered-target.js | 7 +++++ 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index ef6dca663b..e7f77eaa2f 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1075,6 +1075,27 @@ class RenderWebGL extends EventEmitter { return false; } + drawableTouchingScratchRect (drawableID, left, top, right, bottom) { + const drawable = this._allDrawables[drawableID]; + if (!drawable) { + return false; + } + const bounds = new Rectangle(); + bounds.initFromBounds(left, right, bottom, top); + const worldPos = twgl.v3.create(); + + drawable.updateCPURenderAttributes(); + + for (worldPos[1] = bounds.bottom; worldPos[1] <= bounds.top; worldPos[1]++) { + for (worldPos[0] = bounds.left; worldPos[0] <= bounds.right; worldPos[0]++) { + if (drawable.isTouching(worldPos)) { + return true; + } + } + } + return false; + } + /** * Detect which sprite, if any, is at the given location. * This function will pick all drawables that are visible, unless specific diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index 9cc50e6b04..a5845de7fd 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -168,20 +168,13 @@ class Scratch3FaceSensingBlocks { blockType: BlockType.HAT }, { - opcode: 'whenSpriteTouchesPart', + opcode: 'whenSpriteTouchesFace', text: formatMessage({ - id: 'faceSensing.whenSpriteTouchesPart', - default: 'when this sprite touches [PART]', + id: 'faceSensing.whenSpriteTouchesFace', + default: 'when face touches this sprite', description: '' }), - blockType: BlockType.HAT, - arguments: { - PART: { - type: ArgumentType.STRING, - menu: 'PART', - defaultValue: '2' - } - } + blockType: BlockType.HAT }, { opcode: 'whenTilted', @@ -385,17 +378,12 @@ class Scratch3FaceSensingBlocks { return Math.sqrt((dx * dx) + (dy * dy)); } - whenSpriteTouchesPart (args, util) { + whenSpriteTouchesFace (args, util) { if (!this.currentFace) return false; - if (!this.currentFace.landmarks) return false; - const pos = this.getPartPosition(args.PART); - const drawable = this.runtime.renderer._allDrawables[util.target.drawableID]; - if (drawable) { - drawable.updateCPURenderAttributes(); - return drawable.isTouching([pos.x, pos.y]); - } - return false; - // return util.target.isTouchingPoint(pos.x, pos.y); // nope, this takes client coords, as from mouse position + if (!this.currentFace.topLeft) return false; + const topLeft = this.toScratchCoords(this.currentFace.topLeft); + const bottomRight = this.toScratchCoords(this.currentFace.bottomRight); + return util.target.isTouchingRect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y); } whenFaceDetected () { diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index b0c1ebfed9..ce3b2b2126 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -765,6 +765,13 @@ class RenderedTarget extends Target { return false; } + isTouchingRect (left, top, right, bottom) { + if (this.renderer) { + return this.renderer.drawableTouchingScratchRect(this.drawableID, left, top, right, bottom); + } + return false; + } + /** * Return whether touching a stage edge. * @return {boolean} True iff the rendered target is touching the stage edge. From e5081e38044c0f964661e930fab004cec54c5de9 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 23 Nov 2020 12:21:56 -0500 Subject: [PATCH 1921/1971] =?UTF-8?q?change=20hat=20to=20=E2=80=9Cwhen=20t?= =?UTF-8?q?his=20sprite=20touches=20a=20nose=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/scratch-render/src/RenderWebGL.js | 9 +++++++++ .../src/extensions/scratch3_face_sensing/index.js | 15 +++++++-------- .../scratch-vm/src/sprites/rendered-target.js | 7 +++++++ 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index e7f77eaa2f..5d7a05f831 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -1096,6 +1096,15 @@ class RenderWebGL extends EventEmitter { return false; } + drawableTouchingScratchPoint (drawableID, x, y) { + const drawable = this._allDrawables[drawableID]; + if (!drawable) { + return false; + } + drawable.updateCPURenderAttributes(); + return drawable.isTouching([x, y]); + } + /** * Detect which sprite, if any, is at the given location. * This function will pick all drawables that are visible, unless specific diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index a5845de7fd..723e485e71 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -168,10 +168,10 @@ class Scratch3FaceSensingBlocks { blockType: BlockType.HAT }, { - opcode: 'whenSpriteTouchesFace', + opcode: 'whenSpriteTouchesNose', text: formatMessage({ - id: 'faceSensing.whenSpriteTouchesFace', - default: 'when face touches this sprite', + id: 'faceSensing.whenSpriteTouchesNose', + default: 'when this sprite touches a nose', description: '' }), blockType: BlockType.HAT @@ -378,12 +378,11 @@ class Scratch3FaceSensingBlocks { return Math.sqrt((dx * dx) + (dy * dy)); } - whenSpriteTouchesFace (args, util) { + whenSpriteTouchesNose (args, util) { if (!this.currentFace) return false; - if (!this.currentFace.topLeft) return false; - const topLeft = this.toScratchCoords(this.currentFace.topLeft); - const bottomRight = this.toScratchCoords(this.currentFace.bottomRight); - return util.target.isTouchingRect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y); + if (!this.currentFace.landmarks) return false; + const nosePos = this.getPartPosition(2); + return util.target.isTouchingScratchPoint(nosePos.x, nosePos.y); } whenFaceDetected () { diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index ce3b2b2126..03b26fd6ac 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -772,6 +772,13 @@ class RenderedTarget extends Target { return false; } + isTouchingScratchPoint (x, y) { + if (this.renderer) { + return this.renderer.drawableTouchingScratchPoint(this.drawableID, x, y); + } + return false; + } + /** * Return whether touching a stage edge. * @return {boolean} True iff the rendered target is touching the stage edge. From 9a6ee9f319d8a9a849ac3c76bfeee370dc1922bf Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 23 Nov 2020 16:52:51 -0500 Subject: [PATCH 1922/1971] Add temporary extension library tile --- .../src/lib/libraries/extensions/index.jsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index 65a0f1ad9c..b17499fa7b 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -50,19 +50,19 @@ export default [ { name: ( ), - extensionId: 'text', + extensionId: 'faceSensing', iconURL: makeymakeyIconURL, insetIconURL: makeymakeyInsetIconURL, description: ( ), featured: true From 4b4bbe30bf555b1ec67c373b68649fa7e71d61d7 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 23 Nov 2020 16:55:50 -0500 Subject: [PATCH 1923/1971] =?UTF-8?q?show=20=E2=80=9Cimporting=E2=80=9D=20?= =?UTF-8?q?alert=20while=20extension=20data=20is=20loading?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 12 +++++++++++- packages/scratch-vm/src/engine/runtime.js | 8 ++++++++ .../src/extensions/scratch3_face_sensing/index.js | 7 +++++++ packages/scratch-vm/src/virtual-machine.js | 3 +++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index b6207a5b7d..6abaa81d94 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -10,7 +10,7 @@ import {updateBlockDrag} from '../reducers/block-drag'; import {updateMonitors} from '../reducers/monitors'; import {setProjectChanged, setProjectUnchanged} from '../reducers/project-changed'; import {setRunningState, setTurboState, setStartedState} from '../reducers/vm-status'; -import {showExtensionAlert} from '../reducers/alerts'; +import {showExtensionAlert, showStandardAlert, closeAlertWithId} from '../reducers/alerts'; import {updateMicIndicator} from '../reducers/mic-indicator'; /* @@ -46,6 +46,7 @@ const vmListenerHOC = function (WrappedComponent) { this.props.vm.on('PROJECT_START', this.props.onGreenFlag); this.props.vm.on('PERIPHERAL_CONNECTION_LOST_ERROR', this.props.onShowExtensionAlert); this.props.vm.on('MIC_LISTENING', this.props.onMicListeningUpdate); + this.props.vm.on('EXTENSION_DATA_LOADING', this.props.onExtensionDataLoading); } componentDidMount () { @@ -125,6 +126,7 @@ const vmListenerHOC = function (WrappedComponent) { onKeyDown, onKeyUp, onMicListeningUpdate, + onExtensionDataLoading, onMonitorsUpdate, onTargetsUpdate, onProjectChanged, @@ -144,6 +146,7 @@ const vmListenerHOC = function (WrappedComponent) { VMListener.propTypes = { attachKeyboardEvents: PropTypes.bool, onBlockDragUpdate: PropTypes.func.isRequired, + onExtensionDataLoading: PropTypes.func.isRequired, onGreenFlag: PropTypes.func, onKeyDown: PropTypes.func, onKeyUp: PropTypes.func, @@ -202,6 +205,13 @@ const vmListenerHOC = function (WrappedComponent) { }, onMicListeningUpdate: listening => { dispatch(updateMicIndicator(listening)); + }, + onExtensionDataLoading: loading => { + if (loading) { + dispatch(showStandardAlert('importingAsset')); + } else { + dispatch(closeAlertWithId('importingAsset')); + } } }); return connect( diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 92cd2f6b4e..4f9d124d73 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -655,6 +655,10 @@ class Runtime extends EventEmitter { return 'MIC_LISTENING'; } + static get EXTENSION_DATA_LOADING () { + return 'EXTENSION_DATA_LOADING'; + } + /** * Event name for reporting that blocksInfo was updated. * @const {string} @@ -1511,6 +1515,10 @@ class Runtime extends EventEmitter { this.emit(Runtime.MIC_LISTENING, listening); } + emitExtensionLoading (loading) { + this.emit(Runtime.EXTENSION_DATA_LOADING, loading); + } + /** * Retrieve the function associated with the given opcode. * @param {!string} opcode The opcode to look up. diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index 723e485e71..40eaeee0c2 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -36,6 +36,8 @@ class Scratch3FaceSensingBlocks { */ this.runtime = runtime; + this.runtime.emit('EXTENSION_DATA_LOADING', true); + Blazeface.load().then(model => { this.blazeface = model; if (this.runtime.ioDevices) { @@ -105,6 +107,11 @@ class Scratch3FaceSensingBlocks { if (frame) { this.blazeface.estimateFaces(frame, false).then(faces => { if (faces) { + if (!this.firstTime) { + console.log('first time'); + this.firstTime = true; + this.runtime.emit('EXTENSION_DATA_LOADING', false); + } this.currentFace = faces[0]; } this._lastUpdate = time; diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 4fbd202815..876ae3083b 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -145,6 +145,9 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.MIC_LISTENING, listening => { this.emit(Runtime.MIC_LISTENING, listening); }); + this.runtime.on(Runtime.EXTENSION_DATA_LOADING, loading => { + this.emit(Runtime.EXTENSION_DATA_LOADING, loading); + }); this.runtime.on(Runtime.RUNTIME_STARTED, () => { this.emit(Runtime.RUNTIME_STARTED); }); From 3406995a0d2d91e60d6d876d535dd9f0dd3ae974 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 23 Nov 2020 17:05:06 -0500 Subject: [PATCH 1924/1971] =?UTF-8?q?Show=20=E2=80=9Cloading=20extension?= =?UTF-8?q?=E2=80=A6=E2=80=9D=20in=20the=20alert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/scratch-gui/src/lib/alerts/index.jsx | 14 ++++++++++++++ packages/scratch-gui/src/lib/vm-listener-hoc.jsx | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/src/lib/alerts/index.jsx b/packages/scratch-gui/src/lib/alerts/index.jsx index dbd4f2ce7d..25deec2410 100644 --- a/packages/scratch-gui/src/lib/alerts/index.jsx +++ b/packages/scratch-gui/src/lib/alerts/index.jsx @@ -212,6 +212,20 @@ const alerts = [ ), iconSpinner: true, level: AlertLevels.SUCCESS + }, + { + alertId: 'loadingExtensionData', + alertType: AlertTypes.STANDARD, + clearList: [], + content: ( + + ), + iconSpinner: true, + level: AlertLevels.SUCCESS } ]; diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 6abaa81d94..8e9ff5efa9 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -208,9 +208,9 @@ const vmListenerHOC = function (WrappedComponent) { }, onExtensionDataLoading: loading => { if (loading) { - dispatch(showStandardAlert('importingAsset')); + dispatch(showStandardAlert('loadingExtensionData')); } else { - dispatch(closeAlertWithId('importingAsset')); + dispatch(closeAlertWithId('loadingExtensionData')); } } }); From 02c0eda573497924e71d960916be53c1ab0ef4de Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Nov 2020 10:23:47 -0500 Subject: [PATCH 1925/1971] Add ext name and feedback button to editor menubar --- .../src/components/menu-bar/menu-bar.jsx | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index fec3d576e1..2ba4c1cb3f 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -29,6 +29,7 @@ import SB3Downloader from '../../containers/sb3-downloader.jsx'; import DeletionRestorer from '../../containers/deletion-restorer.jsx'; import TurboMode from '../../containers/turbo-mode.jsx'; import MenuBarHOC from '../../containers/menu-bar-hoc.jsx'; +import questionIcon from '../../lib/assets/icon--help.svg'; import {openTipsLibrary} from '../../reducers/modals'; import {setPlayer} from '../../reducers/mode'; @@ -338,8 +339,8 @@ class MenuBar extends React.Component {
Scratch
+ +
+ Text Blocks + Help +
+ {(this.props.canChangeLanguage) && (
@@ -490,7 +505,7 @@ class MenuBar extends React.Component {
-
- + */} {this.props.canEditTitle ? (
- ) : null)} + ) : null)} +
+ +
{this.props.canShare ? ( (this.props.isShowingProject || this.props.isUpdating) && ( From 8ce45d021cc9635d5f0d3316e66cb72c29ef925f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Nov 2020 10:30:54 -0500 Subject: [PATCH 1926/1971] Add last updated time to editor --- .../scratch-gui/src/components/menu-bar/menu-bar.jsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 2ba4c1cb3f..08a8989e4b 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -89,6 +89,12 @@ const ariaMessages = defineMessages({ } }); +const LastUpdated = () => ( + + Last updated Nov 24, 2020 + +); + const MenuBarItemTooltip = ({ children, className, @@ -604,11 +610,15 @@ class MenuBar extends React.Component { {/* show the proper UI in the account menu, given whether the user is logged in, and whether a session is available to log in with */}
+
{this.props.canSave && ( )}
+
+ +
{this.props.sessionExists ? ( this.props.username ? ( // ************ user is logged in ************ @@ -730,7 +740,6 @@ class MenuBar extends React.Component { )}
- {aboutButton} ); From 8f2acc62ba0398303d59a7a0c22822840d9f8bcd Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Nov 2020 11:18:50 -0500 Subject: [PATCH 1927/1971] WIP add pre-download confirmation modal --- .../src/components/menu-bar/menu-bar.jsx | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 08a8989e4b..d3fcd3e25d 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -30,6 +30,7 @@ import DeletionRestorer from '../../containers/deletion-restorer.jsx'; import TurboMode from '../../containers/turbo-mode.jsx'; import MenuBarHOC from '../../containers/menu-bar-hoc.jsx'; import questionIcon from '../../lib/assets/icon--help.svg'; +import DownloadConfirmation from './download-confirmation.jsx'; import {openTipsLibrary} from '../../reducers/modals'; import {setPlayer} from '../../reducers/mode'; @@ -176,8 +177,14 @@ class MenuBar extends React.Component { 'handleLanguageMouseUp', 'handleRestoreOption', 'getSaveToComputerHandler', - 'restoreOptionMessage' + 'restoreOptionMessage', + 'handleConfirmDownload', + 'handleRejectDownload' ]); + + this.state = { + downloadProjectCallback: null + }; } componentDidMount () { document.addEventListener('keydown', this.handleKeyPress); @@ -249,13 +256,23 @@ class MenuBar extends React.Component { getSaveToComputerHandler (downloadProjectCallback) { return () => { this.props.onRequestCloseFile(); - downloadProjectCallback(); - if (this.props.onProjectTelemetryEvent) { - const metadata = collectMetadata(this.props.vm, this.props.projectTitle, this.props.locale); - this.props.onProjectTelemetryEvent('projectDidSave', metadata); - } + this.setState({downloadProjectCallback}); + // downloadProjectCallback(); + // if (this.props.onProjectTelemetryEvent) { + // const metadata = collectMetadata(this.props.vm, this.props.projectTitle, this.props.locale); + // this.props.onProjectTelemetryEvent('projectDidSave', metadata); + // } }; } + handleConfirmDownload () { + if (this.state.downloadProjectCallback) { + this.state.downloadProjectCallback(); + } + this.setState({downloadProjectCallback: null}); + } + handleRejectDownload () { + this.setState({downloadProjectCallback: null}); + } handleLanguageMouseUp (e) { if (!this.props.languageMenuOpen) { this.props.onClickLanguage(e); @@ -741,6 +758,12 @@ class MenuBar extends React.Component { )}
{aboutButton} + {this.state.downloadProjectCallback && ( + + )} ); } From db222673f7add48e8c8cd98350d77ed032e58aac Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Nov 2020 15:09:32 -0500 Subject: [PATCH 1928/1971] Add link for feedback button --- .../src/components/menu-bar/menu-bar.jsx | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index d3fcd3e25d..01e870187a 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -29,7 +29,6 @@ import SB3Downloader from '../../containers/sb3-downloader.jsx'; import DeletionRestorer from '../../containers/deletion-restorer.jsx'; import TurboMode from '../../containers/turbo-mode.jsx'; import MenuBarHOC from '../../containers/menu-bar-hoc.jsx'; -import questionIcon from '../../lib/assets/icon--help.svg'; import DownloadConfirmation from './download-confirmation.jsx'; import {openTipsLibrary} from '../../reducers/modals'; @@ -72,6 +71,8 @@ import remixIcon from './icon--remix.svg'; import dropdownCaret from './dropdown-caret.svg'; import languageIcon from '../language-selector/language-icon.svg'; import aboutIcon from './icon--about.svg'; +import feedbackIcon from './icon--feedback.svg'; +import questionIcon from '../../lib/assets/icon--help.svg'; import scratchLogo from './scratch-logo.svg'; @@ -561,14 +562,19 @@ class MenuBar extends React.Component { /> ) : null)}
- + +
{this.props.canShare ? ( From 6116a542a2dc8f8e1fd956b29fdfcbf6882a2f0c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 1 Dec 2020 09:35:50 -0500 Subject: [PATCH 1929/1971] Revert "Add last updated time to editor" This reverts commit 8ce45d021cc9635d5f0d3316e66cb72c29ef925f. --- .../scratch-gui/src/components/menu-bar/menu-bar.jsx | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 01e870187a..be9ff88d54 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -91,12 +91,6 @@ const ariaMessages = defineMessages({ } }); -const LastUpdated = () => ( - - Last updated Nov 24, 2020 - -); - const MenuBarItemTooltip = ({ children, className, @@ -633,15 +627,11 @@ class MenuBar extends React.Component { {/* show the proper UI in the account menu, given whether the user is logged in, and whether a session is available to log in with */}
-
{this.props.canSave && ( )}
-
- -
{this.props.sessionExists ? ( this.props.username ? ( // ************ user is logged in ************ @@ -763,6 +753,7 @@ class MenuBar extends React.Component { )}
+ {aboutButton} {this.state.downloadProjectCallback && ( Date: Tue, 1 Dec 2020 12:32:08 -0500 Subject: [PATCH 1930/1971] reduce framerate to 15fps --- .../scratch-vm/src/extensions/scratch3_face_sensing/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index 40eaeee0c2..f41b973329 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -65,7 +65,7 @@ class Scratch3FaceSensingBlocks { * @type {number} */ static get INTERVAL () { - return 33; + return 1000 / 15; } /** From 39e70d14c9898e1620f0135daffa4b1af95e4f77 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 1 Dec 2020 12:32:16 -0500 Subject: [PATCH 1931/1971] remove log --- .../scratch-vm/src/extensions/scratch3_face_sensing/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index f41b973329..8801035d8a 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -108,7 +108,6 @@ class Scratch3FaceSensingBlocks { this.blazeface.estimateFaces(frame, false).then(faces => { if (faces) { if (!this.firstTime) { - console.log('first time'); this.firstTime = true; this.runtime.emit('EXTENSION_DATA_LOADING', false); } From 80cdbfbbee441f2f8456aa0d5171429198cb9e97 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 1 Dec 2020 12:33:36 -0500 Subject: [PATCH 1932/1971] Remove offset logic that was causing frequent skipped frames --- .../extensions/scratch3_face_sensing/index.js | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index 8801035d8a..c1eca45cb1 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -94,28 +94,20 @@ class Scratch3FaceSensingBlocks { _loop () { setTimeout(this._loop.bind(this), Math.max(this.runtime.currentStepTime, Scratch3FaceSensingBlocks.INTERVAL)); - const time = Date.now(); - if (this._lastUpdate === null) { - this._lastUpdate = time; - } - const offset = time - this._lastUpdate; - if (offset > Scratch3FaceSensingBlocks.INTERVAL) { - const frame = this.runtime.ioDevices.video.getFrame({ - format: Video.FORMAT_IMAGE_DATA, - dimensions: Scratch3FaceSensingBlocks.DIMENSIONS - }); - if (frame) { - this.blazeface.estimateFaces(frame, false).then(faces => { - if (faces) { - if (!this.firstTime) { - this.firstTime = true; - this.runtime.emit('EXTENSION_DATA_LOADING', false); - } - this.currentFace = faces[0]; + const frame = this.runtime.ioDevices.video.getFrame({ + format: Video.FORMAT_IMAGE_DATA, + dimensions: Scratch3FaceSensingBlocks.DIMENSIONS + }); + if (frame) { + this.blazeface.estimateFaces(frame, false).then(faces => { + if (faces) { + if (!this.firstTime) { + this.firstTime = true; + this.runtime.emit('EXTENSION_DATA_LOADING', false); } - this._lastUpdate = time; - }); - } + this.currentFace = faces[0]; + } + }); } } From b0c59162bb5b1ec27fbdc1dcf6b3255a17114342 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 1 Dec 2020 12:36:48 -0500 Subject: [PATCH 1933/1971] =?UTF-8?q?change=20to=20=E2=80=9Cwhen=20this=20?= =?UTF-8?q?sprite=20touches=20a=20mouth=E2=80=9D=20and=20=E2=80=9Ca=20face?= =?UTF-8?q?=20is=20detected=3F=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/extensions/scratch3_face_sensing/index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index c1eca45cb1..2f39b6080b 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -166,10 +166,10 @@ class Scratch3FaceSensingBlocks { blockType: BlockType.HAT }, { - opcode: 'whenSpriteTouchesNose', + opcode: 'whenSpriteTouchesMouth', text: formatMessage({ - id: 'faceSensing.whenSpriteTouchesNose', - default: 'when this sprite touches a nose', + id: 'faceSensing.whenSpriteTouchesMouth', + default: 'when this sprite touches a mouth', description: '' }), blockType: BlockType.HAT @@ -230,7 +230,7 @@ class Scratch3FaceSensingBlocks { opcode: 'faceIsDetected', text: formatMessage({ id: 'faceSensing.faceDetected', - default: 'face is detected?', + default: 'a face is detected?', description: '' }), blockType: BlockType.BOOLEAN @@ -376,10 +376,10 @@ class Scratch3FaceSensingBlocks { return Math.sqrt((dx * dx) + (dy * dy)); } - whenSpriteTouchesNose (args, util) { + whenSpriteTouchesMouth (args, util) { if (!this.currentFace) return false; if (!this.currentFace.landmarks) return false; - const nosePos = this.getPartPosition(2); + const nosePos = this.getPartPosition(3); return util.target.isTouchingScratchPoint(nosePos.x, nosePos.y); } From dccad9a4bccf382930159b19b485501353c27c55 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 1 Dec 2020 16:56:31 -0500 Subject: [PATCH 1934/1971] =?UTF-8?q?command=20blocks=20do=20nothing=20if?= =?UTF-8?q?=20there=E2=80=99s=20no=20face?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scratch-vm/src/extensions/scratch3_face_sensing/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index 2f39b6080b..11273c9183 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -453,15 +453,18 @@ class Scratch3FaceSensingBlocks { } goToPart (args, util) { + if (!this.currentFace) return; const pos = this.getPartPosition(args.PART); util.target.setXY(pos.x, pos.y); } pointInFaceTiltDirection (args, util) { + if (!this.currentFace) return; util.target.setDirection(this.faceTilt()); } setSizeToFaceSize (args, util) { + if (!this.currentFace) return; util.target.setSize(this.faceSize()); } From 7f19d5777f49b2f73a2cba8baa129803da0b2f92 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 1 Dec 2020 17:06:34 -0500 Subject: [PATCH 1935/1971] =?UTF-8?q?cache=20size=20and=20tilt=20to=20repo?= =?UTF-8?q?rt=20if=20there=E2=80=99s=20no=20face?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extensions/scratch3_face_sensing/index.js | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index 11273c9183..44c607a057 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -46,14 +46,8 @@ class Scratch3FaceSensingBlocks { } }); - this.currentPose = null; - - /** - * The last millisecond epoch timestamp that the video stream was - * analyzed. - * @type {number} - */ - this._lastUpdate = null; + this.cachedSize = 100; + this.cachedTilt = 90; this._clearAttachments = this._clearAttachments.bind(this); this.runtime.on('PROJECT_STOP_ALL', this._clearAttachments); @@ -403,10 +397,10 @@ class Scratch3FaceSensingBlocks { } faceSize () { - if (this.currentFace) { - return Math.round(this.currentFace.bottomRight[0] - this.currentFace.topLeft[0]); - } - return 100; + if (!this.currentFace) return this.cachedSize; + const size = Math.round(this.currentFace.bottomRight[0] - this.currentFace.topLeft[0]); + this.cachedSize = size; + return size; } getPartPosition (part) { @@ -517,12 +511,15 @@ class Scratch3FaceSensingBlocks { } faceTilt () { + if (!this.currentFace) return this.cachedTilt; const leftEyePos = this.getPartPosition(0); const rightEyePos = this.getPartPosition(1); const dx = rightEyePos.x - leftEyePos.x; const dy = rightEyePos.y - leftEyePos.y; const direction = 90 - MathUtil.radToDeg(Math.atan2(dy, dx)); - return Math.round(direction); + const tilt = Math.round(direction); + this.cachedTilt = tilt; + return tilt; } } From f833ea0e50e87735338866e883867ac10752d31b Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 9 Dec 2020 16:49:17 -0500 Subject: [PATCH 1936/1971] =?UTF-8?q?re-order=20blocks=20and=20use=20a=20m?= =?UTF-8?q?enu=20for=20=E2=80=9Cwhen=20this=20sprite=20touches=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extensions/scratch3_face_sensing/index.js | 72 +++++++++++-------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index 44c607a057..0a8c8de0be 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -4,6 +4,7 @@ const Clone = require('../../util/clone'); const MathUtil = require('../../util/math-util'); const formatMessage = require('format-message'); const Video = require('../../io/video'); +const TargetType = require('../../extension-support/target-type'); // const Posenet = require('@tensorflow-models/posenet'); const Blazeface = require('@tensorflow-models/blazeface'); @@ -151,23 +152,43 @@ class Scratch3FaceSensingBlocks { menuIconURI: menuIconURI, blocks: [ { - opcode: 'whenFaceDetected', + opcode: 'goToPart', text: formatMessage({ - id: 'faceSensing.whenFaceDetected', - default: 'when a face is detected', + id: 'faceSensing.goToPart', + default: 'go to [PART]', description: '' }), - blockType: BlockType.HAT + blockType: BlockType.COMMAND, + arguments: { + PART: { + type: ArgumentType.STRING, + menu: 'PART', + defaultValue: '2' + } + }, + filter: [TargetType.SPRITE] }, { - opcode: 'whenSpriteTouchesMouth', + opcode: 'pointInFaceTiltDirection', text: formatMessage({ - id: 'faceSensing.whenSpriteTouchesMouth', - default: 'when this sprite touches a mouth', + id: 'faceSensing.pointInFaceTiltDirection', + default: 'point in direction of face tilt', description: '' }), - blockType: BlockType.HAT + blockType: BlockType.COMMAND, + filter: [TargetType.SPRITE] + }, + { + opcode: 'setSizeToFaceSize', + text: formatMessage({ + id: 'faceSensing.setSizeToFaceSize', + default: 'set size to face size', + description: '' + }), + blockType: BlockType.COMMAND, + filter: [TargetType.SPRITE] }, + '---', { opcode: 'whenTilted', text: formatMessage({ @@ -184,40 +205,31 @@ class Scratch3FaceSensingBlocks { } } }, - '---', { - opcode: 'goToPart', + opcode: 'whenSpriteTouchesPart', text: formatMessage({ - id: 'faceSensing.goToPart', - default: 'go to [PART]', + id: 'faceSensing.whenSpriteTouchesPart', + default: 'when this sprite touches a[PART]', description: '' }), - blockType: BlockType.COMMAND, arguments: { PART: { type: ArgumentType.STRING, menu: 'PART', defaultValue: '2' } - } - }, - { - opcode: 'pointInFaceTiltDirection', - text: formatMessage({ - id: 'faceSensing.pointInFaceTiltDirection', - default: 'point in direction of face tilt', - description: '' - }), - blockType: BlockType.COMMAND + }, + blockType: BlockType.HAT, + filter: [TargetType.SPRITE] }, { - opcode: 'setSizeToFaceSize', + opcode: 'whenFaceDetected', text: formatMessage({ - id: 'faceSensing.setSizeToFaceSize', - default: 'set size to face size', + id: 'faceSensing.whenFaceDetected', + default: 'when a face is detected', description: '' }), - blockType: BlockType.COMMAND + blockType: BlockType.HAT }, '---', { @@ -370,11 +382,11 @@ class Scratch3FaceSensingBlocks { return Math.sqrt((dx * dx) + (dy * dy)); } - whenSpriteTouchesMouth (args, util) { + whenSpriteTouchesPart (args, util) { if (!this.currentFace) return false; if (!this.currentFace.landmarks) return false; - const nosePos = this.getPartPosition(3); - return util.target.isTouchingScratchPoint(nosePos.x, nosePos.y); + const pos = this.getPartPosition(args.PART); + return util.target.isTouchingScratchPoint(pos.x, pos.y); } whenFaceDetected () { From 09c4358cda39edac4995ccf4cd8c7d7321ed8b81 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 11 Dec 2020 09:39:55 -0500 Subject: [PATCH 1937/1971] Add Initial scratch lab site --- packages/scratch-gui/package.json | 1 + .../src/components/menu-bar/menu-bar.jsx | 76 ++++++++++++++++--- 2 files changed, 66 insertions(+), 11 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 2d5df27450..48f7f117a4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "description": "GraphicaL User Interface for creating and running Scratch 3.0 projects", "main": "./dist/scratch-gui.js", + "browser": "./src/index.js", "scripts": { "build": "npm run clean && webpack --colors --bail", "clean": "rimraf ./build && mkdirp build && rimraf ./dist && mkdirp dist", diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index fec3d576e1..be9ff88d54 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -29,6 +29,7 @@ import SB3Downloader from '../../containers/sb3-downloader.jsx'; import DeletionRestorer from '../../containers/deletion-restorer.jsx'; import TurboMode from '../../containers/turbo-mode.jsx'; import MenuBarHOC from '../../containers/menu-bar-hoc.jsx'; +import DownloadConfirmation from './download-confirmation.jsx'; import {openTipsLibrary} from '../../reducers/modals'; import {setPlayer} from '../../reducers/mode'; @@ -70,6 +71,8 @@ import remixIcon from './icon--remix.svg'; import dropdownCaret from './dropdown-caret.svg'; import languageIcon from '../language-selector/language-icon.svg'; import aboutIcon from './icon--about.svg'; +import feedbackIcon from './icon--feedback.svg'; +import questionIcon from '../../lib/assets/icon--help.svg'; import scratchLogo from './scratch-logo.svg'; @@ -169,8 +172,14 @@ class MenuBar extends React.Component { 'handleLanguageMouseUp', 'handleRestoreOption', 'getSaveToComputerHandler', - 'restoreOptionMessage' + 'restoreOptionMessage', + 'handleConfirmDownload', + 'handleRejectDownload' ]); + + this.state = { + downloadProjectCallback: null + }; } componentDidMount () { document.addEventListener('keydown', this.handleKeyPress); @@ -242,13 +251,23 @@ class MenuBar extends React.Component { getSaveToComputerHandler (downloadProjectCallback) { return () => { this.props.onRequestCloseFile(); - downloadProjectCallback(); - if (this.props.onProjectTelemetryEvent) { - const metadata = collectMetadata(this.props.vm, this.props.projectTitle, this.props.locale); - this.props.onProjectTelemetryEvent('projectDidSave', metadata); - } + this.setState({downloadProjectCallback}); + // downloadProjectCallback(); + // if (this.props.onProjectTelemetryEvent) { + // const metadata = collectMetadata(this.props.vm, this.props.projectTitle, this.props.locale); + // this.props.onProjectTelemetryEvent('projectDidSave', metadata); + // } }; } + handleConfirmDownload () { + if (this.state.downloadProjectCallback) { + this.state.downloadProjectCallback(); + } + this.setState({downloadProjectCallback: null}); + } + handleRejectDownload () { + this.setState({downloadProjectCallback: null}); + } handleLanguageMouseUp (e) { if (!this.props.languageMenuOpen) { this.props.onClickLanguage(e); @@ -338,8 +357,8 @@ class MenuBar extends React.Component {
Scratch
+ +
+ Text Blocks + Help +
+ {(this.props.canChangeLanguage) && (
@@ -490,7 +523,7 @@ class MenuBar extends React.Component {
-
- + */} {this.props.canEditTitle ? (
- ) : null)} + ) : null)} +
{this.props.canShare ? ( (this.props.isShowingProject || this.props.isUpdating) && ( @@ -707,6 +755,12 @@ class MenuBar extends React.Component {
{aboutButton} + {this.state.downloadProjectCallback && ( + + )} ); } From 588b8a92216c11e3cb17bbecd4555cdf84ef672e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 11 Dec 2020 11:42:41 -0500 Subject: [PATCH 1938/1971] Only load the extension when entering the modal --- packages/scratch-gui/src/containers/blocks.jsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index a3e55e93ad..df44a85343 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -135,11 +135,14 @@ class Blocks extends React.Component { if (this.props.isVisible) { this.setLocale(); } - setTimeout(() => { + + let extensionLoaded = false; + window.addEventListener('load-extension', () => { + extensionLoaded = true; this.props.vm.extensionManager.loadExtensionURL('faceSensing').then(() => { this.handleCategorySelected('faceSensing'); }) - }, 500) + }); } shouldComponentUpdate (nextProps, nextState) { return ( From 742bed0d74972ece89406c5215d3e87eba18c3f7 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 14 Dec 2020 14:38:24 -0500 Subject: [PATCH 1939/1971] update block and menu icons --- .../scratch-vm/src/extensions/scratch3_face_sensing/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index 0a8c8de0be..d124dd3b1c 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -15,14 +15,14 @@ const Blazeface = require('@tensorflow-models/blazeface'); * @type {string} */ // eslint-disable-next-line max-len -const menuIconURI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjBweCIgaGVpZ2h0PSIyMHB4IiB2aWV3Qm94PSIwIDAgMjAgMjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjIgKDY3MTQ1KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5FeHRlbnNpb25zL1NvZnR3YXJlL1ZpZGVvLVNlbnNpbmctTWVudTwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxnIGlkPSJFeHRlbnNpb25zL1NvZnR3YXJlL1ZpZGVvLVNlbnNpbmctTWVudSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9InZpZGVvLW1vdGlvbiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDUuMDAwMDAwKSIgZmlsbC1ydWxlPSJub256ZXJvIj4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjMEVCRDhDIiBvcGFjaXR5PSIwLjI1IiBjeD0iMTYiIGN5PSI4IiByPSIyIj48L2NpcmNsZT4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjMEVCRDhDIiBvcGFjaXR5PSIwLjUiIGN4PSIxNiIgY3k9IjYiIHI9IjIiPjwvY2lyY2xlPgogICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsLUNvcHkiIGZpbGw9IiMwRUJEOEMiIG9wYWNpdHk9IjAuNzUiIGN4PSIxNiIgY3k9IjQiIHI9IjIiPjwvY2lyY2xlPgogICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsIiBmaWxsPSIjMEVCRDhDIiBjeD0iMTYiIGN5PSIyIiByPSIyIj48L2NpcmNsZT4KICAgICAgICAgICAgPHBhdGggZD0iTTExLjMzNTk3MzksMi4yMDk3ODgyNSBMOC4yNSw0LjIwOTk1NjQ5IEw4LjI1LDMuMDUgQzguMjUsMi4wNDQ4ODIyNyA3LjQ2ODU5MDMxLDEuMjUgNi41LDEuMjUgTDIuMDUsMS4yNSBDMS4wMzgwNzExOSwxLjI1IDAuMjUsMi4wMzgwNzExOSAwLjI1LDMuMDUgTDAuMjUsNyBDMC4yNSw3Ljk2MzY5OTM3IDEuMDQyMjQ5MTksOC43NTU5NDg1NiAyLjA1LDguOCBMNi41LDguOCBDNy40NTA4MzAwOSw4LjggOC4yNSw3Ljk3MzI3MjUgOC4yNSw3IEw4LjI1LDUuODU4NDUyNDEgTDguNjI4NjIzOTQsNi4wODU2MjY3NyBMMTEuNDI2Nzc2Nyw3Ljc3MzIyMzMgQzExLjQzNjg5NDMsNy43ODMzNDA5MSAxMS40NzU3NjU1LDcuOCAxMS41LDcuOCBDMTEuNjMzNDkzMiw3LjggMTEuNzUsNy42OTEyNjAzNCAxMS43NSw3LjU1IEwxMS43NSwyLjQgQzExLjc1LDIuNDE4MzgyNjkgMTEuNzIxOTAyOSwyLjM1MjgyMjgyIDExLjY4NTYyNjgsMi4yNzg2MjM5NCBDMTEuNjEyOTUyOCwyLjE1NzUwMDY5IDExLjQ3MDc5NjgsMi4xMjkwNjk1IDExLjMzNTk3MzksMi4yMDk3ODgyNSBaIiBpZD0idmlkZW9fMzdfIiBzdHJva2Utb3BhY2l0eT0iMC4xNSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjAuNSIgZmlsbD0iIzRENEQ0RCI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+'; +const menuIconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48Y2lyY2xlIGZpbGw9IiM0Qzk3RkYiIGN4PSIxNS41IiBjeT0iMTcuNSIgcj0iMS41Ii8+PGNpcmNsZSBmaWxsPSIjNEM5N0ZGIiBjeD0iMjQuNSIgY3k9IjE3LjUiIHI9IjEuNSIvPjxwYXRoIGQ9Ik0yMCA5QzEzLjkyNSA5IDkgMTMuOTI1IDkgMjBzNC45MjUgMTEgMTEgMTEgMTEtNC45MjUgMTEtMTFTMjYuMDc1IDkgMjAgOXptMCAyYTkgOSAwIDExMCAxOCA5IDkgMCAwMTAtMTh6IiBmaWxsPSIjNEM5N0ZGIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48cGF0aCBkPSJNMzUgNGExIDEgMCAwMS45OTMuODgzTDM2IDV2NmExIDEgMCAwMS0xLjk5My4xMTdMMzQgMTFWNmgtNWExIDEgMCAwMS0uOTkzLS44ODNMMjggNWExIDEgMCAwMS44ODMtLjk5M0wyOSA0aDZ6TTUgMzZhMSAxIDAgMDEtLjk5My0uODgzTDQgMzV2LTZhMSAxIDAgMDExLjk5My0uMTE3TDYgMjl2NWg1YTEgMSAwIDAxLjk5My44ODNMMTIgMzVhMSAxIDAgMDEtLjg4My45OTNMMTEgMzZINXoiIGZpbGwtb3BhY2l0eT0iLjUiIGZpbGw9IiM0RDk3RkYiIGZpbGwtcnVsZT0ibm9uemVybyIvPjxwYXRoIGQ9Ik0yMi4xNjggMjEuOTQ1YTEgMSAwIDExMS42NjQgMS4xMUMyMi45NzQgMjQuMzQyIDIxLjY1OCAyNSAyMCAyNXMtMi45NzQtLjY1OC0zLjgzMi0xLjk0NWExIDEgMCAxMTEuNjY0LTEuMTFDMTguMzA3IDIyLjY1OCAxOC45OTIgMjMgMjAgMjNjMS4wMDkgMCAxLjY5My0uMzQyIDIuMTY4LTEuMDU1eiIgZmlsbD0iIzRDOTdGRiIgZmlsbC1ydWxlPSJub256ZXJvIi8+PHBhdGggZD0iTTI5LjcyIDI0LjAyOGEyLjU1NyAyLjU1NyAwIDAwMS44MDgtMS44MDhsLjU0NC0yLjAwOWMuMjUyLS45NDggMS42LS45NDggMS44NTYgMGwuNTQgMi4wMDlhMi41NjMgMi41NjMgMCAwMDEuODEzIDEuODA4bDIuMDA4LjU0NGMuOTQ4LjI1Mi45NDggMS42IDAgMS44NTdsLTIuMDA4LjU0YTIuNTYzIDIuNTYzIDAgMDAtMS44MTMgMS44MDhsLS41NCAyLjAwOWMtLjI1Ni45NTItMS42MDQuOTUyLTEuODU2IDBsLS41NDQtMi4wMDlhMi41NTcgMi41NTcgMCAwMC0xLjgwOS0xLjgwOGwtMi4wMDgtLjU0Yy0uOTQ4LS4yNTYtLjk0OC0xLjYwNSAwLTEuODU3bDIuMDA4LS41NDR6TTUuMDQgNi4zOTZBMS45MTggMS45MTggMCAwMDYuMzk2IDUuMDRsLjQwOC0xLjUwN2MuMTg5LS43MSAxLjItLjcxIDEuMzkyIDBsLjQwNSAxLjUwN2ExLjkyMiAxLjkyMiAwIDAwMS4zNiAxLjM1NmwxLjUwNi40MDhjLjcxLjE5LjcxIDEuMiAwIDEuMzkzbC0xLjUwNy40MDVhMS45MjIgMS45MjIgMCAwMC0xLjM1OSAxLjM1NmwtLjQwNSAxLjUwNmMtLjE5Mi43MTUtMS4yMDMuNzE1LTEuMzkyIDBsLS40MDgtMS41MDZBMS45MTggMS45MTggMCAwMDUuMDQgOC42MDJsLTEuNTA3LS40MDVjLS43MS0uMTkyLS43MS0xLjIwNCAwLTEuMzkzbDEuNTA3LS40MDh6IiBmaWxsPSIjRkZCRjAwIi8+PHBhdGggZD0iTTMxLjU4OSAyMC4wODNsLS41NDQgMi4wMDZhMi4wNTggMi4wNTggMCAwMS0xLjQ1NyAxLjQ1N2wtMi4wMDguNTQ0Yy0xLjQ0LjM4My0xLjQ0IDIuNDMyIDAgMi44MjFsMi4wMS41NGMuNzEuMTkgMS4yNjQuNzQ2IDEuNDU1IDEuNDU2bC41NDQgMi4wMWMuMzgzIDEuNDQ1IDIuNDMzIDEuNDQ1IDIuODIyLS4wMDFsLjU0LTIuMDA5YTIuMDYzIDIuMDYzIDAgMDExLjQ1OS0xLjQ1NWwyLjAwOS0uNTRjMS40NDItLjM5IDEuNDQyLTIuNDQtLjAwMi0yLjgyM2wtMi4wMDYtLjU0M2EyLjA2MiAyLjA2MiAwIDAxLTEuNDYtMS40NTVsLS41NC0yLjAxYy0uMzktMS40NDItMi40MzktMS40NDItMi44MjIuMDAyem0xLjg1Ni4yNTlsLjU0IDIuMDA4YTMuMDYyIDMuMDYyIDAgMDAyLjE2NSAyLjE2bDIuMDA4LjU0NWMuNDU2LjEyLjQ1Ni43NjggMCAuODkxbC0yLjAwNy41NGEzLjA2MiAzLjA2MiAwIDAwLTIuMTY2IDIuMTYybC0uNTQgMi4wMDhjLS4xMjMuNDU4LS43NjkuNDU4LS44OS4wMDJsLS41NDUtMi4wMTFhMy4wNTcgMy4wNTcgMCAwMC0yLjE2Mi0yLjE2MWwtMi4wMDctLjU0Yy0uNDU1LS4xMjMtLjQ1NS0uNzctLjAwMS0uODlsMi4wMS0uNTQ1YTMuMDU3IDMuMDU3IDAgMDAyLjE2LTIuMTYybC41NDQtMi4wMDdjLjEyMi0uNDU2Ljc2OS0uNDU2Ljg5MSAweiIgZmlsbC1vcGFjaXR5PSIuNSIgZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJub256ZXJvIi8+PHBhdGggZD0iTTYuMzIgMy40MDVsLS40MDcgMS41MDRjLS4xMy40OS0uNTExLjg3LTEuMDA0IDEuMDA1bC0xLjUwNi40MDhjLTEuMjA0LjMyLTEuMjA0IDIuMDMyIDAgMi4zNTdsMS41MDcuNDA1Yy40OS4xMzEuODcyLjUxNCAxLjAwMyAxLjAwM2wuNDA4IDEuNTA4Yy4zMiAxLjIwNyAyLjAzMyAxLjIwNyAyLjM1OCAwbC40MDUtMS41MDdhMS40MjIgMS40MjIgMCAwMTEuMDA1LTEuMDAzbDEuNTA4LS40MDZjMS4yMDQtLjMyNSAxLjIwNC0yLjAzNy0uMDAyLTIuMzU4bC0xLjUwNC0uNDA4YTEuNDIyIDEuNDIyIDAgMDEtMS4wMDctMS4wMDJMOC42OCAzLjQwM2MtLjMyNS0xLjIwNC0yLjAzOC0xLjIwNC0yLjM1OC4wMDJ6bTEuMzkzLjI1OWwuNDA1IDEuNTA2QTIuNDIxIDIuNDIxIDAgMDA5LjgzIDYuODc5bDEuNTA3LjQwOGMuMjE4LjA1OC4yMTguMzY4IDAgLjQyN2wtMS41MDUuNDA1YTIuNDIyIDIuNDIyIDAgMDAtMS43MTMgMS43MWwtLjQwNSAxLjUwNmMtLjA1OS4yMi0uMzY4LjIyLS40MjYuMDAxbC0uNDA5LTEuNTA5YTIuNDE3IDIuNDE3IDAgMDAtMS43MS0xLjcwOGwtMS41MDUtLjQwNWMtLjIxNy0uMDU5LS4yMTctLjM3LS4wMDEtLjQyN0w1LjE3IDYuODhhMi40MTggMi40MTggMCAwMDEuNzA5LTEuNzFsLjQwNy0xLjUwNWMuMDU5LS4yMTguMzY5LS4yMTguNDI3IDB6IiBmaWxsLW9wYWNpdHk9Ii40IiBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48L2c+PC9zdmc+'; /** * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI. * @type {string} */ // eslint-disable-next-line max-len -const blockIconURI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSIwIDAgNDAgNDAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjIgKDY3MTQ1KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5FeHRlbnNpb25zL1NvZnR3YXJlL1ZpZGVvLVNlbnNpbmctQmxvY2s8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZyBpZD0iRXh0ZW5zaW9ucy9Tb2Z0d2FyZS9WaWRlby1TZW5zaW5nLUJsb2NrIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2Utb3BhY2l0eT0iMC4xNSI+CiAgICAgICAgPGcgaWQ9InZpZGVvLW1vdGlvbiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDEwLjAwMDAwMCkiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSIjMDAwMDAwIj4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjRkZGRkZGIiBvcGFjaXR5PSIwLjI1IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGN4PSIzMiIgY3k9IjE2IiByPSI0LjUiPjwvY2lyY2xlPgogICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsLUNvcHkiIGZpbGw9IiNGRkZGRkYiIG9wYWNpdHk9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjeD0iMzIiIGN5PSIxMiIgcj0iNC41Ij48L2NpcmNsZT4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjRkZGRkZGIiBvcGFjaXR5PSIwLjc1IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGN4PSIzMiIgY3k9IjgiIHI9IjQuNSI+PC9jaXJjbGU+CiAgICAgICAgICAgIDxjaXJjbGUgaWQ9Ik92YWwiIGZpbGw9IiNGRkZGRkYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgY3g9IjMyIiBjeT0iNCIgcj0iNC41Ij48L2NpcmNsZT4KICAgICAgICAgICAgPHBhdGggZD0iTTIyLjY3MTk0NzcsNC40MTk1NzY0OSBMMTYuNSw4LjQxOTkxMjk4IEwxNi41LDYuMSBDMTYuNSw0LjA4OTc2NDU0IDE0LjkzNzE4MDYsMi41IDEzLDIuNSBMNC4xLDIuNSBDMi4wNzYxNDIzNywyLjUgMC41LDQuMDc2MTQyMzcgMC41LDYuMSBMMC41LDE0IEMwLjUsMTUuOTI3Mzk4NyAyLjA4NDQ5ODM5LDE3LjUxMTg5NzEgNC4xLDE3LjYgTDEzLDE3LjYgQzE0LjkwMTY2MDIsMTcuNiAxNi41LDE1Ljk0NjU0NSAxNi41LDE0IEwxNi41LDExLjcxNjkwNDggTDIyLjc1NzI0NzksMTUuNDcxMjUzNSBMMjIuODUzNTUzNCwxNS41NDY0NDY2IEMyMi44NzM3ODg2LDE1LjU2NjY4MTggMjIuOTUxNTMxLDE1LjYgMjMsMTUuNiBDMjMuMjY2OTg2NSwxNS42IDIzLjUsMTUuMzgyNTIwNyAyMy41LDE1LjEgTDIzLjUsNC44IEMyMy41LDQuODM2NzY1MzggMjMuNDQzODA1OCw0LjcwNTY0NTYzIDIzLjM3MTI1MzUsNC41NTcyNDc4OCBDMjMuMjI1OTA1Niw0LjMxNTAwMTM5IDIyLjk0MTU5MzcsNC4yNTgxMzg5OSAyMi42NzE5NDc3LDQuNDE5NTc2NDkgWiIgaWQ9InZpZGVvXzM3XyIgZmlsbD0iIzRENEQ0RCI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+'; +const blockIconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3R5bGU9ImJhY2tncm91bmQ6IzBmYmQ4YyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBmaWxsPSIjMEZCRDhDIiBkPSJNMCAwaDQwdjQwSDB6Ii8+PGNpcmNsZSBmaWxsPSIjRkZGIiBjeD0iMTUuNSIgY3k9IjE3LjUiIHI9IjEuNSIvPjxjaXJjbGUgZmlsbD0iI0ZGRiIgY3g9IjI0LjUiIGN5PSIxNy41IiByPSIxLjUiLz48cGF0aCBkPSJNMjAgOUMxMy45MjUgOSA5IDEzLjkyNSA5IDIwczQuOTI1IDExIDExIDExIDExLTQuOTI1IDExLTExUzI2LjA3NSA5IDIwIDl6bTAgMmE5IDkgMCAxMTAgMTggOSA5IDAgMDEwLTE4eiIgZmlsbD0iI0ZGRiIgZmlsbC1ydWxlPSJub256ZXJvIi8+PHBhdGggZD0iTTM1IDRhMSAxIDAgMDEuOTkzLjg4M0wzNiA1djZhMSAxIDAgMDEtMS45OTMuMTE3TDM0IDExVjZoLTVhMSAxIDAgMDEtLjk5My0uODgzTDI4IDVhMSAxIDAgMDEuODgzLS45OTNMMjkgNGg2ek01IDM2YTEgMSAwIDAxLS45OTMtLjg4M0w0IDM1di02YTEgMSAwIDAxMS45OTMtLjExN0w2IDI5djVoNWExIDEgMCAwMS45OTMuODgzTDEyIDM1YTEgMSAwIDAxLS44ODMuOTkzTDExIDM2SDV6IiBmaWxsLW9wYWNpdHk9Ii4yNSIgZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJub256ZXJvIi8+PHBhdGggZD0iTTI5LjcyIDI0LjAyOGEyLjU1NyAyLjU1NyAwIDAwMS44MDgtMS44MDhsLjU0NC0yLjAwOWMuMjUyLS45NDggMS42LS45NDggMS44NTYgMGwuNTQgMi4wMDlhMi41NjMgMi41NjMgMCAwMDEuODEzIDEuODA4bDIuMDA4LjU0NGMuOTQ4LjI1Mi45NDggMS42IDAgMS44NTdsLTIuMDA4LjU0YTIuNTYzIDIuNTYzIDAgMDAtMS44MTMgMS44MDhsLS41NCAyLjAwOWMtLjI1Ni45NTItMS42MDQuOTUyLTEuODU2IDBsLS41NDQtMi4wMDlhMi41NTcgMi41NTcgMCAwMC0xLjgwOS0xLjgwOGwtMi4wMDgtLjU0Yy0uOTQ4LS4yNTYtLjk0OC0xLjYwNSAwLTEuODU3bDIuMDA4LS41NDR6TTUuMDQgNi4zOTZBMS45MTggMS45MTggMCAwMDYuMzk2IDUuMDRsLjQwOC0xLjUwN2MuMTg5LS43MSAxLjItLjcxIDEuMzkyIDBsLjQwNSAxLjUwN2ExLjkyMiAxLjkyMiAwIDAwMS4zNiAxLjM1NmwxLjUwNi40MDhjLjcxLjE5LjcxIDEuMiAwIDEuMzkzbC0xLjUwNy40MDVhMS45MjIgMS45MjIgMCAwMC0xLjM1OSAxLjM1NmwtLjQwNSAxLjUwNmMtLjE5Mi43MTUtMS4yMDMuNzE1LTEuMzkyIDBsLS40MDgtMS41MDZBMS45MTggMS45MTggMCAwMDUuMDQgOC42MDJsLTEuNTA3LS40MDVjLS43MS0uMTkyLS43MS0xLjIwNCAwLTEuMzkzbDEuNTA3LS40MDh6IiBmaWxsPSIjRkZCRjAwIi8+PHBhdGggZD0iTTIyLjE2OCAyMS45NDVhMSAxIDAgMTExLjY2NCAxLjExQzIyLjk3NCAyNC4zNDIgMjEuNjU4IDI1IDIwIDI1cy0yLjk3NC0uNjU4LTMuODMyLTEuOTQ1YTEgMSAwIDExMS42NjQtMS4xMUMxOC4zMDcgMjIuNjU4IDE4Ljk5MiAyMyAyMCAyM2MxLjAwOSAwIDEuNjkzLS4zNDIgMi4xNjgtMS4wNTV6IiBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48cGF0aCBkPSJNMzEuNTg5IDIwLjA4M2wtLjU0NCAyLjAwNmEyLjA1OCAyLjA1OCAwIDAxLTEuNDU3IDEuNDU3bC0yLjAwOC41NDRjLTEuNDQuMzgzLTEuNDQgMi40MzIgMCAyLjgyMWwyLjAxLjU0Yy43MS4xOSAxLjI2NC43NDYgMS40NTUgMS40NTZsLjU0NCAyLjAxYy4zODMgMS40NDUgMi40MzMgMS40NDUgMi44MjItLjAwMWwuNTQtMi4wMDlhMi4wNjMgMi4wNjMgMCAwMTEuNDU5LTEuNDU1bDIuMDA5LS41NGMxLjQ0Mi0uMzkgMS40NDItMi40NC0uMDAyLTIuODIzbC0yLjAwNi0uNTQzYTIuMDYyIDIuMDYyIDAgMDEtMS40Ni0xLjQ1NWwtLjU0LTIuMDFjLS4zOS0xLjQ0Mi0yLjQzOS0xLjQ0Mi0yLjgyMi4wMDJ6bTEuODU2LjI1OWwuNTQgMi4wMDhhMy4wNjIgMy4wNjIgMCAwMDIuMTY1IDIuMTZsMi4wMDguNTQ1Yy40NTYuMTIuNDU2Ljc2OCAwIC44OTFsLTIuMDA3LjU0YTMuMDYyIDMuMDYyIDAgMDAtMi4xNjYgMi4xNjJsLS41NCAyLjAwOGMtLjEyMy40NTgtLjc2OS40NTgtLjg5LjAwMmwtLjU0NS0yLjAxMWEzLjA1NyAzLjA1NyAwIDAwLTIuMTYyLTIuMTYxbC0yLjAwNy0uNTRjLS40NTUtLjEyMy0uNDU1LS43Ny0uMDAxLS44OWwyLjAxLS41NDVhMy4wNTcgMy4wNTcgMCAwMDIuMTYtMi4xNjJsLjU0NC0yLjAwN2MuMTIyLS40NTYuNzY5LS40NTYuODkxIDB6IiBmaWxsLW9wYWNpdHk9Ii41IiBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48cGF0aCBkPSJNNi4zMiAzLjQwNWwtLjQwNyAxLjUwNGMtLjEzLjQ5LS41MTEuODctMS4wMDQgMS4wMDVsLTEuNTA2LjQwOGMtMS4yMDQuMzItMS4yMDQgMi4wMzIgMCAyLjM1N2wxLjUwNy40MDVjLjQ5LjEzMS44NzIuNTE0IDEuMDAzIDEuMDAzbC40MDggMS41MDhjLjMyIDEuMjA3IDIuMDMzIDEuMjA3IDIuMzU4IDBsLjQwNS0xLjUwN2ExLjQyMiAxLjQyMiAwIDAxMS4wMDUtMS4wMDNsMS41MDgtLjQwNmMxLjIwNC0uMzI1IDEuMjA0LTIuMDM3LS4wMDItMi4zNThsLTEuNTA0LS40MDhhMS40MjIgMS40MjIgMCAwMS0xLjAwNy0xLjAwMkw4LjY4IDMuNDAzYy0uMzI1LTEuMjA0LTIuMDM4LTEuMjA0LTIuMzU4LjAwMnptMS4zOTMuMjU5bC40MDUgMS41MDZBMi40MjEgMi40MjEgMCAwMDkuODMgNi44NzlsMS41MDcuNDA4Yy4yMTguMDU4LjIxOC4zNjggMCAuNDI3bC0xLjUwNS40MDVhMi40MjIgMi40MjIgMCAwMC0xLjcxMyAxLjcxbC0uNDA1IDEuNTA2Yy0uMDU5LjIyLS4zNjguMjItLjQyNi4wMDFsLS40MDktMS41MDlhMi40MTcgMi40MTcgMCAwMC0xLjcxLTEuNzA4bC0xLjUwNS0uNDA1Yy0uMjE3LS4wNTktLjIxNy0uMzctLjAwMS0uNDI3TDUuMTcgNi44OGEyLjQxOCAyLjQxOCAwIDAwMS43MDktMS43MWwuNDA3LTEuNTA1Yy4wNTktLjIxOC4zNjktLjIxOC40MjcgMHoiIGZpbGwtb3BhY2l0eT0iLjQiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvZz48L3N2Zz4='; /** * Class for the motion-related blocks in Scratch 3.0 From 07ec2bcb5cf593801104a6b8d35471000bedd724 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 16 Dec 2020 11:05:06 -0500 Subject: [PATCH 1940/1971] Update to gui develop as of 12-16-2020 --- packages/scratch-gui/package.json | 20 +++++++++---------- .../src/components/menu-bar/menu-bar.jsx | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 9358bebe9a..916a3aff48 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -46,12 +46,12 @@ "keymirror": "0.1.1", "lodash.bindall": "4.4.0", "lodash.debounce": "4.0.8", - "lodash.defaultsdeep": "4.6.0", + "lodash.defaultsdeep": "4.6.1", "lodash.omit": "4.5.0", "lodash.throttle": "4.0.1", "minilog": "3.1.0", "omggif": "1.0.9", - "papaparse": "5.1.1", + "papaparse": "5.3.0", "postcss-import": "^12.0.0", "postcss-loader": "^3.0.0", "postcss-simple-vars": "^5.0.1", @@ -60,7 +60,7 @@ "raw-loader": "^0.5.1", "react": "16.2.0", "react-contextmenu": "2.9.4", - "react-dom": "16.2.0", + "react-dom": "16.2.1", "react-draggable": "3.0.5", "react-ga": "2.5.3", "react-intl": "2.9.0", @@ -75,13 +75,13 @@ "redux": "3.7.2", "redux-throttle": "0.1.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200908141031", - "scratch-l10n": "3.10.20200909030847", - "scratch-paint": "0.2.0-prerelease.20200831213104", - "scratch-render": "0.1.0-prerelease.20200827214414", + "scratch-blocks": "0.1.0-prerelease.20201216042451", + "scratch-l10n": "3.10.20201216031625", "scratch-storage": "1.3.3", - "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", - "scratch-vm": "0.2.0-prerelease.20200903205543", + "scratch-vm": "0.2.0-prerelease.20201125065300", + "scratch-paint": "0.2.0-prerelease.20201020103914", + "scratch-render": "0.1.0-prerelease.20201113223804", + "scratch-svg-renderer": "0.2.0-prerelease.20201019174008", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", "text-encoding": "0.7.0", @@ -104,7 +104,7 @@ "babel-core": "7.0.0-bridge.0", "babel-eslint": "^10.0.1", "babel-loader": "^8.0.4", - "chromedriver": "84.0.1", + "chromedriver": "86.0.0", "enzyme": "^3.5.0", "enzyme-adapter-react-16": "1.3.0", "eslint": "^5.0.1", diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index be9ff88d54..93b3b78161 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -556,7 +556,7 @@ class MenuBar extends React.Component { /> ) : null)}
- From f0b67e00f8f95490f88724c20898aa98522f6780 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 16 Dec 2020 11:25:20 -0500 Subject: [PATCH 1941/1971] Update to vm develop 12-16-2020 --- packages/scratch-vm/src/engine/runtime.js | 9 +++++++++ packages/scratch-vm/src/virtual-machine.js | 3 +++ 2 files changed, 12 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 92cd2f6b4e..61f67feea3 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -601,6 +601,15 @@ class Runtime extends EventEmitter { static get PERIPHERAL_LIST_UPDATE () { return 'PERIPHERAL_LIST_UPDATE'; } + + /** + * Event name for when the user picks a bluetooth device to connect to + * via Companion Device Manager (CDM) + * @const {string} + */ + static get USER_PICKED_PERIPHERAL () { + return 'USER_PICKED_PERIPHERAL'; + } /** * Event name for reporting that a peripheral has connected. diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 4fbd202815..5cec44c10c 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -127,6 +127,9 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.PERIPHERAL_LIST_UPDATE, info => { this.emit(Runtime.PERIPHERAL_LIST_UPDATE, info); }); + this.runtime.on(Runtime.USER_PICKED_PERIPHERAL, info => { + this.emit(Runtime.USER_PICKED_PERIPHERAL, info); + }); this.runtime.on(Runtime.PERIPHERAL_CONNECTED, () => this.emit(Runtime.PERIPHERAL_CONNECTED) ); From cdd55f8dba0a61a8cd2c4daf68375fd43b148ae1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 16 Dec 2020 11:46:36 -0500 Subject: [PATCH 1942/1971] Update to render develop 12-16-2020 --- packages/scratch-render/src/RenderWebGL.js | 28 +++++++++++--------- packages/scratch-render/src/SVGSkin.js | 30 ++++++++++++++++++++++ 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 2f665d31dc..e2caba4784 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -490,7 +490,7 @@ class RenderWebGL extends EventEmitter { * @returns {int} The ID of the new Drawable. */ createDrawable (group) { - if (!group || !this._layerGroups.hasOwnProperty(group)) { + if (!group || !Object.prototype.hasOwnProperty.call(this._layerGroups, group)) { log.warn('Cannot create a drawable without a known layer group'); return; } @@ -560,7 +560,7 @@ class RenderWebGL extends EventEmitter { * @param {string} group Group name that the drawable belongs to */ destroyDrawable (drawableID, group) { - if (!group || !this._layerGroups.hasOwnProperty(group)) { + if (!group || !Object.prototype.hasOwnProperty.call(this._layerGroups, group)) { log.warn('Cannot destroy drawable without known layer group.'); return; } @@ -614,7 +614,7 @@ class RenderWebGL extends EventEmitter { * @return {?number} New order if changed, or null. */ setDrawableOrder (drawableID, order, group, optIsRelative, optMin) { - if (!group || !this._layerGroups.hasOwnProperty(group)) { + if (!group || !Object.prototype.hasOwnProperty.call(this._layerGroups, group)) { log.warn('Cannot set the order of a drawable without a known layer group.'); return; } @@ -668,7 +668,7 @@ class RenderWebGL extends EventEmitter { twgl.bindFramebufferInfo(gl, null); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); - gl.clearColor.apply(gl, this._backgroundColor4f); + gl.clearColor(...this._backgroundColor4f); gl.clear(gl.COLOR_BUFFER_BIT); this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, this._projection); @@ -829,6 +829,8 @@ class RenderWebGL extends EventEmitter { const color = __touchingColor; const hasMask = Boolean(mask3b); + drawable.updateCPURenderAttributes(); + // Masked drawable ignores ghost effect const effectMask = ~ShaderManager.EFFECT_INFO.ghost.mask; @@ -998,6 +1000,8 @@ class RenderWebGL extends EventEmitter { const drawable = this._allDrawables[drawableID]; const point = __isTouchingDrawablesPoint; + drawable.updateCPURenderAttributes(); + // This is an EXTREMELY brute force collision detector, but it is // still faster than asking the GPU to give us the pixels. for (let x = bounds.left; x <= bounds.right; x++) { @@ -1149,7 +1153,7 @@ class RenderWebGL extends EventEmitter { let hit = RenderConstants.ID_NONE; for (const hitID in hits) { - if (hits.hasOwnProperty(hitID) && (hits[hitID] > hits[hit])) { + if (Object.prototype.hasOwnProperty.call(hits, hitID) && (hits[hitID] > hits[hit])) { hit = hitID; } } @@ -1404,7 +1408,7 @@ class RenderWebGL extends EventEmitter { gl.viewport(0, 0, bounds.width, bounds.height); const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); - gl.clearColor.apply(gl, this._backgroundColor4f); + gl.clearColor(...this._backgroundColor4f); gl.clear(gl.COLOR_BUFFER_BIT); this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, projection); @@ -1452,8 +1456,6 @@ class RenderWebGL extends EventEmitter { /** @todo remove this once URL-based skin setting is removed. */ if (!drawable.skin || !drawable.skin.getTexture([100, 100])) return null; - - drawable.updateCPURenderAttributes(); const bounds = drawable.getFastBounds(); // Limit queries to the stage size. @@ -1900,7 +1902,7 @@ class RenderWebGL extends EventEmitter { const uniforms = {}; let effectBits = drawable.enabledEffects; - effectBits &= opts.hasOwnProperty('effectMask') ? opts.effectMask : effectBits; + effectBits &= Object.prototype.hasOwnProperty.call(opts, 'effectMask') ? opts.effectMask : effectBits; const newShader = this._shaderManager.getShader(drawMode, effectBits); // Manually perform region check. Do not create functions inside a @@ -1928,7 +1930,9 @@ class RenderWebGL extends EventEmitter { if (uniforms.u_skin) { twgl.setTextureParameters( - gl, uniforms.u_skin, {minMag: drawable.useNearest(drawableScale) ? gl.NEAREST : gl.LINEAR} + gl, uniforms.u_skin, { + minMag: drawable.skin.useNearest(drawableScale, drawable) ? gl.NEAREST : gl.LINEAR + } ); } @@ -1948,14 +1952,14 @@ class RenderWebGL extends EventEmitter { _getConvexHullPointsForDrawable (drawableID) { const drawable = this._allDrawables[drawableID]; - drawable.updateCPURenderAttributes(); - const [width, height] = drawable.skin.size; // No points in the hull if invisible or size is 0. if (!drawable.getVisible() || width === 0 || height === 0) { return []; } + drawable.updateCPURenderAttributes(); + /** * Return the determinant of two vectors, the vector from A to B and the vector from A to C. * diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 0b58fdcd3a..bf1aeca888 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -2,6 +2,7 @@ const twgl = require('twgl.js'); const Skin = require('./Skin'); const SvgRenderer = require('scratch-svg-renderer').SVGRenderer; +const ShaderManager = require('./ShaderManager'); const MAX_TEXTURE_DIMENSION = 2048; @@ -58,6 +59,35 @@ class SVGSkin extends Skin { return this._svgRenderer.size; } + useNearest (scale, drawable) { + // If the effect bits for mosaic, pixelate, whirl, or fisheye are set, use linear + if ((drawable.enabledEffects & ( + ShaderManager.EFFECT_INFO.fisheye.mask | + ShaderManager.EFFECT_INFO.whirl.mask | + ShaderManager.EFFECT_INFO.pixelate.mask | + ShaderManager.EFFECT_INFO.mosaic.mask + )) !== 0) { + return false; + } + + // We can't use nearest neighbor unless we are a multiple of 90 rotation + if (drawable._direction % 90 !== 0) { + return false; + } + + // Because SVG skins' bounding boxes are currently not pixel-aligned, the idea here is to hide blurriness + // by using nearest-neighbor scaling if one screen-space pixel is "close enough" to one texture pixel. + // If the scale of the skin is very close to 100 (0.99999 variance is okay I guess) + // TODO: Make this check more precise. We should use nearest if there's less than one pixel's difference + // between the screen-space and texture-space sizes of the skin. Mipmaps make this harder because there are + // multiple textures (and hence multiple texture spaces) and we need to know which one to choose. + if (Math.abs(scale[0]) > 99 && Math.abs(scale[0]) < 101 && + Math.abs(scale[1]) > 99 && Math.abs(scale[1]) < 101) { + return true; + } + return false; + } + /** * Create a MIP for a given scale. * @param {number} scale - The relative size of the MIP From fd47cd20cdb3252b767124c37922f3ab71028d02 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 18 Dec 2020 11:23:27 -0500 Subject: [PATCH 1943/1971] Reset to llk/develop for scratch-vm --- .../scratch-vm/src/extension-support/extension-manager.js | 3 +-- packages/scratch-vm/src/sprites/rendered-target.js | 8 +------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 22e824eef7..7cb556c5d0 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -23,8 +23,7 @@ const builtinExtensions = { ev3: () => require('../extensions/scratch3_ev3'), makeymakey: () => require('../extensions/scratch3_makeymakey'), boost: () => require('../extensions/scratch3_boost'), - gdxfor: () => require('../extensions/scratch3_gdx_for'), - text: () => require('../extensions/scratch3_text') + gdxfor: () => require('../extensions/scratch3_gdx_for') }; /** diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js index 70dc2dbeb0..b0c1ebfed9 100644 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -374,16 +374,10 @@ class RenderedTarget extends Target { const origW = costumeSize[0]; const origH = costumeSize[1]; const minScale = Math.min(1, Math.max(5 / origW, 5 / origH)); - let maxScale = Math.min( + const maxScale = Math.min( (1.5 * this.runtime.constructor.STAGE_WIDTH) / origW, (1.5 * this.runtime.constructor.STAGE_HEIGHT) / origH ); - // Allow special skins to override max scale, like the textCostumeSkin which - // reflows text on its own so clamping is not needed. - let overrideScale = this.renderer.getCurrentSkinMaxScale(this.drawableID); - if (overrideScale !== null) { - maxScale = overrideScale; - } this.size = MathUtil.clamp(size / 100, minScale, maxScale) * 100; const {direction, scale} = this._getRenderedDirectionAndScale(); this.renderer.updateDrawableDirectionScale(this.drawableID, direction, scale); From 50ae486919d2afc0e4b43879dd5a948793160699 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 18 Dec 2020 11:23:55 -0500 Subject: [PATCH 1944/1971] Reset to llk/develop for scratch-render --- packages/scratch-render/src/RenderWebGL.js | 29 ---------------------- 1 file changed, 29 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index e2caba4784..b3f6a0aaff 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -11,7 +11,6 @@ const RenderConstants = require('./RenderConstants'); const ShaderManager = require('./ShaderManager'); const SVGSkin = require('./SVGSkin'); const TextBubbleSkin = require('./TextBubbleSkin'); -const TextCostumeSkin = require('./TextCostumeSkin'); const EffectTransform = require('./EffectTransform'); const log = require('./util/log'); @@ -425,23 +424,6 @@ class RenderWebGL extends EventEmitter { this._reskin(skinId, newSkin); } - updateTextCostumeSkin (textState) { - // update existing skin - if (textState.skinId && (this._allSkins[textState.skinId] instanceof TextCostumeSkin)) { - this._allSkins[textState.skinId].setTextAndStyle(textState); - return textState.skinId; - } - - // create and update a new skin - const skinId = this._nextSkinId++; - const newSkin = new TextCostumeSkin(skinId, this); - this._allSkins[skinId] = newSkin; - newSkin.setTextAndStyle(textState); - // this._reskin(skinId, newSkin); // this is erroring- might be necessary? - - return skinId; - } - _reskin (skinId, newSkin) { const oldSkin = this._allSkins[skinId]; this._allSkins[skinId] = newSkin; @@ -754,17 +736,6 @@ class RenderWebGL extends EventEmitter { return this.getSkinSize(drawable.skin.id); } - /** - * Get the max scale the skin prefers. Only relevant for - * skins that have do not scale up normally, like reflowing text. - * @param {int} drawableID The ID of the Drawable to measure. - * @return {number} Max scale preferred by the skin, or null. - */ - getCurrentSkinMaxScale(drawableID) { - const drawable = this._allDrawables[drawableID]; - return this._allSkins[drawable.skin.id].maxScale; - } - /** * Get the size of a skin by ID. * @param {int} skinID The ID of the Skin to measure. From 3bdeec3247af69721e9b24a3a527a7ef4b95a9e3 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 18 Dec 2020 11:40:03 -0500 Subject: [PATCH 1945/1971] Bring up-to-date with gui develop and minimal template changes --- .../scratch-gui/src/containers/blocks.jsx | 5 ---- packages/scratch-gui/src/containers/stage.jsx | 21 +++++++++++++---- .../src/lib/libraries/extensions/index.jsx | 23 ------------------- 3 files changed, 17 insertions(+), 32 deletions(-) diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index fd05f35bc9..f5d52388ff 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -135,11 +135,6 @@ class Blocks extends React.Component { if (this.props.isVisible) { this.setLocale(); } - setTimeout(() => { - this.props.vm.extensionManager.loadExtensionURL('text').then(() => { - this.handleCategorySelected('text'); - }); - }, 3000); } shouldComponentUpdate (nextProps, nextState) { return ( diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 8e1ac92483..79a41db7ea 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -342,16 +342,29 @@ class Stage extends React.Component { } onStartDrag (x, y) { if (this.state.dragId) return; - const drawableId = this.renderer.pick(x, y); + + // Targets with no attached drawable cannot be dragged. + let draggableTargets = this.props.vm.runtime.targets.filter( + target => Number.isFinite(target.drawableID) + ); + + // Because pick queries can be expensive, only perform them for drawables that are currently draggable. + // If we're in the editor, we can drag all targets. Otherwise, filter. + if (!this.props.useEditorDragStyle) { + draggableTargets = draggableTargets.filter( + target => target.draggable + ); + } + if (draggableTargets.length === 0) return; + + const draggableIDs = draggableTargets.map(target => target.drawableID); + const drawableId = this.renderer.pick(x, y, 1, 1, draggableIDs); if (drawableId === null) return; const targetId = this.props.vm.getTargetIdForDrawableId(drawableId); if (targetId === null) return; const target = this.props.vm.runtime.getTargetById(targetId); - // Do not start drag unless in editor drag mode or target is draggable - if (!(this.props.useEditorDragStyle || target.draggable)) return; - // Dragging always brings the target to the front target.goToFront(); diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index 35ac7dddda..ba18b916b5 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -7,9 +7,6 @@ import musicInsetIconURL from './music/music-small.svg'; import penIconURL from './pen/pen.png'; import penInsetIconURL from './pen/pen-small.svg'; -import textIconURL from './text/text.png'; -import textInsetIconURL from './text/text-small.svg'; - import videoSensingIconURL from './videoSensing/video-sensing.png'; import videoSensingInsetIconURL from './videoSensing/video-sensing-small.svg'; @@ -90,26 +87,6 @@ export default [ ), featured: true }, - { - name: ( - - ), - extensionId: 'text', - iconURL: textIconURL, - insetIconURL: textInsetIconURL, - description: ( - - ), - featured: true - }, { name: ( Date: Fri, 18 Dec 2020 12:16:31 -0500 Subject: [PATCH 1946/1971] Update README --- packages/scratch-gui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 916a3aff48..17ecd9dc57 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -75,8 +75,8 @@ "redux": "3.7.2", "redux-throttle": "0.1.1", "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20201216042451", - "scratch-l10n": "3.10.20201216031625", + "scratch-blocks": "0.1.0-prerelease.20201218040309", + "scratch-l10n": "3.10.20201218031454", "scratch-storage": "1.3.3", "scratch-vm": "0.2.0-prerelease.20201125065300", "scratch-paint": "0.2.0-prerelease.20201020103914", From 1e5650c46d66d1d7bcb487fe8bacab501d161938 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 18 Dec 2020 12:18:49 -0500 Subject: [PATCH 1947/1971] Remove packages so they are never out of date --- packages/scratch-gui/package.json | 146 - .../src/components/menu-bar/menu-bar.jsx | 884 ------ .../scratch-gui/src/containers/blocks.jsx | 700 ----- packages/scratch-gui/src/containers/stage.jsx | 476 --- packages/scratch-gui/src/lib/alerts/index.jsx | 222 -- .../src/lib/libraries/extensions/index.jsx | 321 -- .../scratch-gui/src/lib/vm-listener-hoc.jsx | 213 -- packages/scratch-render/src/RenderWebGL.js | 2123 ------------- packages/scratch-render/src/SVGSkin.js | 201 -- packages/scratch-vm/src/engine/runtime.js | 2618 ----------------- .../extension-support/extension-manager.js | 440 --- .../scratch-vm/src/sprites/rendered-target.js | 1115 ------- packages/scratch-vm/src/virtual-machine.js | 1548 ---------- 13 files changed, 11007 deletions(-) delete mode 100644 packages/scratch-gui/package.json delete mode 100644 packages/scratch-gui/src/components/menu-bar/menu-bar.jsx delete mode 100644 packages/scratch-gui/src/containers/blocks.jsx delete mode 100644 packages/scratch-gui/src/containers/stage.jsx delete mode 100644 packages/scratch-gui/src/lib/alerts/index.jsx delete mode 100644 packages/scratch-gui/src/lib/libraries/extensions/index.jsx delete mode 100644 packages/scratch-gui/src/lib/vm-listener-hoc.jsx delete mode 100644 packages/scratch-render/src/RenderWebGL.js delete mode 100644 packages/scratch-render/src/SVGSkin.js delete mode 100644 packages/scratch-vm/src/engine/runtime.js delete mode 100644 packages/scratch-vm/src/extension-support/extension-manager.js delete mode 100644 packages/scratch-vm/src/sprites/rendered-target.js delete mode 100644 packages/scratch-vm/src/virtual-machine.js diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json deleted file mode 100644 index 17ecd9dc57..0000000000 --- a/packages/scratch-gui/package.json +++ /dev/null @@ -1,146 +0,0 @@ -{ - "name": "scratch-gui", - "version": "0.1.0", - "description": "GraphicaL User Interface for creating and running Scratch 3.0 projects", - "main": "./dist/scratch-gui.js", - "browser": "./src/index.js", - "scripts": { - "build": "npm run clean && webpack --colors --bail", - "clean": "rimraf ./build && mkdirp build && rimraf ./dist && mkdirp dist", - "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1) [skip ci]\"", - "prune": "./prune-gh-pages.sh", - "i18n:push": "tx-push-src scratch-editor interface translations/en.json", - "i18n:src": "rimraf ./translations/messages/src && babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/src ./translations/ && npm run i18n:push", - "start": "webpack-dev-server", - "test": "npm run test:lint && npm run test:unit && npm run build && npm run test:integration", - "test:integration": "jest --runInBand test[\\\\/]integration", - "test:lint": "eslint . --ext .js,.jsx", - "test:unit": "jest test[\\\\/]unit", - "test:smoke": "jest --runInBand test[\\\\/]smoke", - "watch": "webpack --colors --watch" - }, - "author": "Massachusetts Institute of Technology", - "license": "BSD-3-Clause", - "homepage": "https://github.com/LLK/scratch-gui#readme", - "repository": { - "type": "git", - "url": "git+ssh://git@github.com/LLK/scratch-gui.git" - }, - "dependencies": { - "arraybuffer-loader": "^1.0.6", - "autoprefixer": "^9.0.1", - "base64-loader": "1.0.0", - "bowser": "1.9.4", - "classnames": "2.2.6", - "computed-style-to-inline-style": "3.0.0", - "copy-webpack-plugin": "^4.5.1", - "core-js": "2.5.7", - "css-loader": "^1.0.0", - "es6-object-assign": "1.1.0", - "file-loader": "2.0.0", - "get-float-time-domain-data": "0.1.0", - "get-user-media-promise": "1.1.4", - "immutable": "3.8.2", - "intl": "1.2.5", - "js-base64": "2.4.9", - "keymirror": "0.1.1", - "lodash.bindall": "4.4.0", - "lodash.debounce": "4.0.8", - "lodash.defaultsdeep": "4.6.1", - "lodash.omit": "4.5.0", - "lodash.throttle": "4.0.1", - "minilog": "3.1.0", - "omggif": "1.0.9", - "papaparse": "5.3.0", - "postcss-import": "^12.0.0", - "postcss-loader": "^3.0.0", - "postcss-simple-vars": "^5.0.1", - "prop-types": "^15.5.10", - "query-string": "^5.1.1", - "raw-loader": "^0.5.1", - "react": "16.2.0", - "react-contextmenu": "2.9.4", - "react-dom": "16.2.1", - "react-draggable": "3.0.5", - "react-ga": "2.5.3", - "react-intl": "2.9.0", - "react-modal": "3.9.1", - "react-popover": "0.5.10", - "react-redux": "5.0.7", - "react-responsive": "5.0.0", - "react-style-proptype": "3.2.2", - "react-tabs": "2.3.0", - "react-tooltip": "3.8.0", - "react-virtualized": "9.20.1", - "redux": "3.7.2", - "redux-throttle": "0.1.1", - "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20201218040309", - "scratch-l10n": "3.10.20201218031454", - "scratch-storage": "1.3.3", - "scratch-vm": "0.2.0-prerelease.20201125065300", - "scratch-paint": "0.2.0-prerelease.20201020103914", - "scratch-render": "0.1.0-prerelease.20201113223804", - "scratch-svg-renderer": "0.2.0-prerelease.20201019174008", - "startaudiocontext": "1.2.1", - "style-loader": "^0.23.0", - "text-encoding": "0.7.0", - "to-style": "1.3.3", - "wav-encoder": "1.3.0", - "xhr": "2.5.0" - }, - "peerDependencies": { - "react": "^16.0.0", - "react-dom": "^16.0.0" - }, - "devDependencies": { - "@babel/cli": "^7.1.2", - "@babel/core": "^7.1.2", - "@babel/plugin-proposal-object-rest-spread": "^7.0.0", - "@babel/plugin-syntax-dynamic-import": "^7.0.0", - "@babel/plugin-transform-async-to-generator": "^7.1.0", - "@babel/preset-env": "^7.1.0", - "@babel/preset-react": "^7.0.0", - "babel-core": "7.0.0-bridge.0", - "babel-eslint": "^10.0.1", - "babel-loader": "^8.0.4", - "chromedriver": "86.0.0", - "enzyme": "^3.5.0", - "enzyme-adapter-react-16": "1.3.0", - "eslint": "^5.0.1", - "eslint-config-scratch": "^5.0.0", - "eslint-import-resolver-webpack": "^0.11.1", - "eslint-plugin-import": "^2.18.2", - "eslint-plugin-jest": "^22.14.1", - "eslint-plugin-react": "^7.12.4", - "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", - "html-webpack-plugin": "^3.2.0", - "jest": "^21.0.0", - "jest-junit": "^7.0.0", - "mkdirp": "^1.0.3", - "raf": "^3.4.0", - "react-test-renderer": "16.2.0", - "redux-mock-store": "^1.2.3", - "rimraf": "^2.6.1", - "selenium-webdriver": "3.6.0", - "uglifyjs-webpack-plugin": "^1.2.5", - "web-audio-test-api": "^0.5.2", - "webpack": "^4.6.0", - "webpack-cli": "^3.1.0", - "webpack-dev-server": "^3.1.3" - }, - "jest": { - "setupFiles": [ - "raf/polyfill", - "/test/helpers/enzyme-setup.js" - ], - "testPathIgnorePatterns": [ - "src/test.js" - ], - "moduleNameMapper": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/test/__mocks__/fileMock.js", - "\\.(css|less)$": "/test/__mocks__/styleMock.js", - "editor-msgs(\\.js)?$": "/test/__mocks__/editor-msgs-mock.js" - } - } -} diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx deleted file mode 100644 index 93b3b78161..0000000000 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ /dev/null @@ -1,884 +0,0 @@ -import classNames from 'classnames'; -import {connect} from 'react-redux'; -import {compose} from 'redux'; -import {defineMessages, FormattedMessage, injectIntl, intlShape} from 'react-intl'; -import PropTypes from 'prop-types'; -import bindAll from 'lodash.bindall'; -import bowser from 'bowser'; -import React from 'react'; - -import VM from 'scratch-vm'; - -import Box from '../box/box.jsx'; -import Button from '../button/button.jsx'; -import CommunityButton from './community-button.jsx'; -import ShareButton from './share-button.jsx'; -import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx'; -import Divider from '../divider/divider.jsx'; -import LanguageSelector from '../../containers/language-selector.jsx'; -import SaveStatus from './save-status.jsx'; -import SBFileUploader from '../../containers/sb-file-uploader.jsx'; -import ProjectWatcher from '../../containers/project-watcher.jsx'; -import MenuBarMenu from './menu-bar-menu.jsx'; -import {MenuItem, MenuSection} from '../menu/menu.jsx'; -import ProjectTitleInput from './project-title-input.jsx'; -import AuthorInfo from './author-info.jsx'; -import AccountNav from '../../containers/account-nav.jsx'; -import LoginDropdown from './login-dropdown.jsx'; -import SB3Downloader from '../../containers/sb3-downloader.jsx'; -import DeletionRestorer from '../../containers/deletion-restorer.jsx'; -import TurboMode from '../../containers/turbo-mode.jsx'; -import MenuBarHOC from '../../containers/menu-bar-hoc.jsx'; -import DownloadConfirmation from './download-confirmation.jsx'; - -import {openTipsLibrary} from '../../reducers/modals'; -import {setPlayer} from '../../reducers/mode'; -import { - autoUpdateProject, - getIsUpdating, - getIsShowingProject, - manualUpdateProject, - requestNewProject, - remixProject, - saveProjectAsCopy -} from '../../reducers/project-state'; -import { - openAccountMenu, - closeAccountMenu, - accountMenuOpen, - openFileMenu, - closeFileMenu, - fileMenuOpen, - openEditMenu, - closeEditMenu, - editMenuOpen, - openLanguageMenu, - closeLanguageMenu, - languageMenuOpen, - openLoginMenu, - closeLoginMenu, - loginMenuOpen -} from '../../reducers/menus'; - -import collectMetadata from '../../lib/collect-metadata'; - -import styles from './menu-bar.css'; - -import helpIcon from '../../lib/assets/icon--tutorials.svg'; -import mystuffIcon from './icon--mystuff.png'; -import profileIcon from './icon--profile.png'; -import remixIcon from './icon--remix.svg'; -import dropdownCaret from './dropdown-caret.svg'; -import languageIcon from '../language-selector/language-icon.svg'; -import aboutIcon from './icon--about.svg'; -import feedbackIcon from './icon--feedback.svg'; -import questionIcon from '../../lib/assets/icon--help.svg'; - -import scratchLogo from './scratch-logo.svg'; - -import sharedMessages from '../../lib/shared-messages'; - -const ariaMessages = defineMessages({ - language: { - id: 'gui.menuBar.LanguageSelector', - defaultMessage: 'language selector', - description: 'accessibility text for the language selection menu' - }, - tutorials: { - id: 'gui.menuBar.tutorialsLibrary', - defaultMessage: 'Tutorials', - description: 'accessibility text for the tutorials button' - } -}); - -const MenuBarItemTooltip = ({ - children, - className, - enable, - id, - place = 'bottom' -}) => { - if (enable) { - return ( - - {children} - - ); - } - return ( - - {children} - - ); -}; - - -MenuBarItemTooltip.propTypes = { - children: PropTypes.node, - className: PropTypes.string, - enable: PropTypes.bool, - id: PropTypes.string, - place: PropTypes.oneOf(['top', 'bottom', 'left', 'right']) -}; - -const MenuItemTooltip = ({id, isRtl, children, className}) => ( - - {children} - -); - -MenuItemTooltip.propTypes = { - children: PropTypes.node, - className: PropTypes.string, - id: PropTypes.string, - isRtl: PropTypes.bool -}; - -const AboutButton = props => ( - - ); - // Show the About button only if we have a handler for it (like in the desktop app) - const aboutButton = this.props.onClickAbout ? : null; - return ( - -
-
-
- Scratch Lab -
- -
- Text Blocks - Help -
- - {(this.props.canChangeLanguage) && (
-
- - -
- -
)} - {(this.props.canManageFiles) && ( -
- - - - - {newProjectMessage} - - - {(this.props.canSave || this.props.canCreateCopy || this.props.canRemix) && ( - - {this.props.canSave && ( - - {saveNowMessage} - - )} - {this.props.canCreateCopy && ( - - {createCopyMessage} - - )} - {this.props.canRemix && ( - - {remixMessage} - - )} - - )} - - - {(className, renderFileInput, handleLoadProject) => ( - - {/* eslint-disable max-len */} - {this.props.intl.formatMessage(sharedMessages.loadFromComputerTitle)} - {/* eslint-enable max-len */} - {renderFileInput()} - - )} - - {(className, downloadProjectCallback) => ( - - - - )} - - -
- )} -
-
- -
- - {(handleRestore, {restorable, deletedItem}) => ( - - {this.restoreOptionMessage(deletedItem)} - - )} - - {(toggleTurboMode, {turboMode}) => ( - - {turboMode ? ( - - ) : ( - - )} - - )} - - -
-
- - {/*
- - -
- */} - {this.props.canEditTitle ? ( -
- - - -
- ) : ((this.props.authorUsername && this.props.authorUsername !== this.props.username) ? ( - - ) : null)} -
-
- {this.props.canShare ? ( - (this.props.isShowingProject || this.props.isUpdating) && ( - - { - waitForUpdate => ( - { - this.handleClickShare(waitForUpdate); - }} - /* eslint-enable react/jsx-no-bind */ - /> - ) - } - - ) - ) : ( - this.props.showComingSoon ? ( - - - - ) : [] - )} - {this.props.canRemix ? remixButton : []} -
-
- {this.props.enableCommunity ? ( - (this.props.isShowingProject || this.props.isUpdating) && ( - - { - waitForUpdate => ( - { - this.handleClickSeeCommunity(waitForUpdate); - }} - /* eslint-enable react/jsx-no-bind */ - /> - ) - } - - ) - ) : (this.props.showComingSoon ? ( - - - - ) : [])} -
-
- - {/* show the proper UI in the account menu, given whether the user is - logged in, and whether a session is available to log in with */} -
-
- {this.props.canSave && ( - - )} -
- {this.props.sessionExists ? ( - this.props.username ? ( - // ************ user is logged in ************ - - -
- -
-
- -
- ) : ( - // ********* user not logged in, but a session exists - // ********* so they can choose to log in - -
- -
-
- - -
-
- ) - ) : ( - // ******** no login session is available, so don't show login stuff - - {this.props.showComingSoon ? ( - - -
- -
-
- -
- - - {'scratch-cat'} - - -
-
-
- ) : []} -
- )} -
- - {aboutButton} - {this.state.downloadProjectCallback && ( - - )} - - ); - } -} - -MenuBar.propTypes = { - accountMenuOpen: PropTypes.bool, - authorId: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), - authorThumbnailUrl: PropTypes.string, - authorUsername: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), - autoUpdateProject: PropTypes.func, - canChangeLanguage: PropTypes.bool, - canCreateCopy: PropTypes.bool, - canCreateNew: PropTypes.bool, - canEditTitle: PropTypes.bool, - canManageFiles: PropTypes.bool, - canRemix: PropTypes.bool, - canSave: PropTypes.bool, - canShare: PropTypes.bool, - className: PropTypes.string, - confirmReadyToReplaceProject: PropTypes.func, - editMenuOpen: PropTypes.bool, - enableCommunity: PropTypes.bool, - fileMenuOpen: PropTypes.bool, - intl: intlShape, - isRtl: PropTypes.bool, - isShared: PropTypes.bool, - isShowingProject: PropTypes.bool, - isUpdating: PropTypes.bool, - languageMenuOpen: PropTypes.bool, - locale: PropTypes.string.isRequired, - loginMenuOpen: PropTypes.bool, - logo: PropTypes.string, - onClickAbout: PropTypes.func, - onClickAccount: PropTypes.func, - onClickEdit: PropTypes.func, - onClickFile: PropTypes.func, - onClickLanguage: PropTypes.func, - onClickLogin: PropTypes.func, - onClickLogo: PropTypes.func, - onClickNew: PropTypes.func, - onClickRemix: PropTypes.func, - onClickSave: PropTypes.func, - onClickSaveAsCopy: PropTypes.func, - onLogOut: PropTypes.func, - onOpenRegistration: PropTypes.func, - onOpenTipLibrary: PropTypes.func, - onProjectTelemetryEvent: PropTypes.func, - onRequestCloseAccount: PropTypes.func, - onRequestCloseEdit: PropTypes.func, - onRequestCloseFile: PropTypes.func, - onRequestCloseLanguage: PropTypes.func, - onRequestCloseLogin: PropTypes.func, - onSeeCommunity: PropTypes.func, - onShare: PropTypes.func, - onToggleLoginOpen: PropTypes.func, - projectTitle: PropTypes.string, - renderLogin: PropTypes.func, - sessionExists: PropTypes.bool, - shouldSaveBeforeTransition: PropTypes.func, - showComingSoon: PropTypes.bool, - userOwnsProject: PropTypes.bool, - username: PropTypes.string, - vm: PropTypes.instanceOf(VM).isRequired -}; - -MenuBar.defaultProps = { - logo: scratchLogo, - onShare: () => {} -}; - -const mapStateToProps = (state, ownProps) => { - const loadingState = state.scratchGui.projectState.loadingState; - const user = state.session && state.session.session && state.session.session.user; - return { - accountMenuOpen: accountMenuOpen(state), - fileMenuOpen: fileMenuOpen(state), - editMenuOpen: editMenuOpen(state), - isRtl: state.locales.isRtl, - isUpdating: getIsUpdating(loadingState), - isShowingProject: getIsShowingProject(loadingState), - languageMenuOpen: languageMenuOpen(state), - locale: state.locales.locale, - loginMenuOpen: loginMenuOpen(state), - projectTitle: state.scratchGui.projectTitle, - sessionExists: state.session && typeof state.session.session !== 'undefined', - username: user ? user.username : null, - userOwnsProject: ownProps.authorUsername && user && - (ownProps.authorUsername === user.username), - vm: state.scratchGui.vm - }; -}; - -const mapDispatchToProps = dispatch => ({ - autoUpdateProject: () => dispatch(autoUpdateProject()), - onOpenTipLibrary: () => dispatch(openTipsLibrary()), - onClickAccount: () => dispatch(openAccountMenu()), - onRequestCloseAccount: () => dispatch(closeAccountMenu()), - onClickFile: () => dispatch(openFileMenu()), - onRequestCloseFile: () => dispatch(closeFileMenu()), - onClickEdit: () => dispatch(openEditMenu()), - onRequestCloseEdit: () => dispatch(closeEditMenu()), - onClickLanguage: () => dispatch(openLanguageMenu()), - onRequestCloseLanguage: () => dispatch(closeLanguageMenu()), - onClickLogin: () => dispatch(openLoginMenu()), - onRequestCloseLogin: () => dispatch(closeLoginMenu()), - onClickNew: needSave => dispatch(requestNewProject(needSave)), - onClickRemix: () => dispatch(remixProject()), - onClickSave: () => dispatch(manualUpdateProject()), - onClickSaveAsCopy: () => dispatch(saveProjectAsCopy()), - onSeeCommunity: () => dispatch(setPlayer(true)) -}); - -export default compose( - injectIntl, - MenuBarHOC, - connect( - mapStateToProps, - mapDispatchToProps - ) -)(MenuBar); diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx deleted file mode 100644 index f5d52388ff..0000000000 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ /dev/null @@ -1,700 +0,0 @@ -import bindAll from 'lodash.bindall'; -import debounce from 'lodash.debounce'; -import defaultsDeep from 'lodash.defaultsdeep'; -import makeToolboxXML from '../lib/make-toolbox-xml'; -import PropTypes from 'prop-types'; -import React from 'react'; -import VMScratchBlocks from '../lib/blocks'; -import VM from 'scratch-vm'; - -import log from '../lib/log.js'; -import Prompt from './prompt.jsx'; -import BlocksComponent from '../components/blocks/blocks.jsx'; -import ExtensionLibrary from './extension-library.jsx'; -import extensionData from '../lib/libraries/extensions/index.jsx'; -import CustomProcedures from './custom-procedures.jsx'; -import errorBoundaryHOC from '../lib/error-boundary-hoc.jsx'; -import {BLOCKS_DEFAULT_SCALE, STAGE_DISPLAY_SIZES} from '../lib/layout-constants'; -import DropAreaHOC from '../lib/drop-area-hoc.jsx'; -import DragConstants from '../lib/drag-constants'; -import defineDynamicBlock from '../lib/define-dynamic-block'; - -import {connect} from 'react-redux'; -import {updateToolbox} from '../reducers/toolbox'; -import {activateColorPicker} from '../reducers/color-picker'; -import {closeExtensionLibrary, openSoundRecorder, openConnectionModal} from '../reducers/modals'; -import {activateCustomProcedures, deactivateCustomProcedures} from '../reducers/custom-procedures'; -import {setConnectionModalExtensionId} from '../reducers/connection-modal'; -import {updateMetrics} from '../reducers/workspace-metrics'; - -import { - activateTab, - SOUNDS_TAB_INDEX -} from '../reducers/editor-tab'; - -const addFunctionListener = (object, property, callback) => { - const oldFn = object[property]; - object[property] = function () { - const result = oldFn.apply(this, arguments); - callback.apply(this, result); - return result; - }; -}; - -const DroppableBlocks = DropAreaHOC([ - DragConstants.BACKPACK_CODE -])(BlocksComponent); - -class Blocks extends React.Component { - constructor (props) { - super(props); - this.ScratchBlocks = VMScratchBlocks(props.vm); - bindAll(this, [ - 'attachVM', - 'detachVM', - 'getToolboxXML', - 'handleCategorySelected', - 'handleConnectionModalStart', - 'handleDrop', - 'handleStatusButtonUpdate', - 'handleOpenSoundRecorder', - 'handlePromptStart', - 'handlePromptCallback', - 'handlePromptClose', - 'handleCustomProceduresClose', - 'onScriptGlowOn', - 'onScriptGlowOff', - 'onBlockGlowOn', - 'onBlockGlowOff', - 'handleExtensionAdded', - 'handleBlocksInfoUpdate', - 'onTargetsUpdate', - 'onVisualReport', - 'onWorkspaceUpdate', - 'onWorkspaceMetricsChange', - 'setBlocks', - 'setLocale' - ]); - this.ScratchBlocks.prompt = this.handlePromptStart; - this.ScratchBlocks.statusButtonCallback = this.handleConnectionModalStart; - this.ScratchBlocks.recordSoundCallback = this.handleOpenSoundRecorder; - - this.state = { - prompt: null - }; - this.onTargetsUpdate = debounce(this.onTargetsUpdate, 100); - this.toolboxUpdateQueue = []; - } - componentDidMount () { - this.ScratchBlocks.FieldColourSlider.activateEyedropper_ = this.props.onActivateColorPicker; - this.ScratchBlocks.Procedures.externalProcedureDefCallback = this.props.onActivateCustomProcedures; - this.ScratchBlocks.ScratchMsgs.setLocale(this.props.locale); - - const workspaceConfig = defaultsDeep({}, - Blocks.defaultOptions, - this.props.options, - {rtl: this.props.isRtl, toolbox: this.props.toolboxXML} - ); - this.workspace = this.ScratchBlocks.inject(this.blocks, workspaceConfig); - - // Register buttons under new callback keys for creating variables, - // lists, and procedures from extensions. - - const toolboxWorkspace = this.workspace.getFlyout().getWorkspace(); - - const varListButtonCallback = type => - (() => this.ScratchBlocks.Variables.createVariable(this.workspace, null, type)); - const procButtonCallback = () => { - this.ScratchBlocks.Procedures.createProcedureDefCallback_(this.workspace); - }; - - toolboxWorkspace.registerButtonCallback('MAKE_A_VARIABLE', varListButtonCallback('')); - toolboxWorkspace.registerButtonCallback('MAKE_A_LIST', varListButtonCallback('list')); - toolboxWorkspace.registerButtonCallback('MAKE_A_PROCEDURE', procButtonCallback); - - // Store the xml of the toolbox that is actually rendered. - // This is used in componentDidUpdate instead of prevProps, because - // the xml can change while e.g. on the costumes tab. - this._renderedToolboxXML = this.props.toolboxXML; - - // we actually never want the workspace to enable "refresh toolbox" - this basically re-renders the - // entire toolbox every time we reset the workspace. We call updateToolbox as a part of - // componentDidUpdate so the toolbox will still correctly be updated - this.setToolboxRefreshEnabled = this.workspace.setToolboxRefreshEnabled.bind(this.workspace); - this.workspace.setToolboxRefreshEnabled = () => { - this.setToolboxRefreshEnabled(false); - }; - - // @todo change this when blockly supports UI events - addFunctionListener(this.workspace, 'translate', this.onWorkspaceMetricsChange); - addFunctionListener(this.workspace, 'zoom', this.onWorkspaceMetricsChange); - - this.attachVM(); - // Only update blocks/vm locale when visible to avoid sizing issues - // If locale changes while not visible it will get handled in didUpdate - if (this.props.isVisible) { - this.setLocale(); - } - } - shouldComponentUpdate (nextProps, nextState) { - return ( - this.state.prompt !== nextState.prompt || - this.props.isVisible !== nextProps.isVisible || - this._renderedToolboxXML !== nextProps.toolboxXML || - this.props.extensionLibraryVisible !== nextProps.extensionLibraryVisible || - this.props.customProceduresVisible !== nextProps.customProceduresVisible || - this.props.locale !== nextProps.locale || - this.props.anyModalVisible !== nextProps.anyModalVisible || - this.props.stageSize !== nextProps.stageSize - ); - } - componentDidUpdate (prevProps) { - // If any modals are open, call hideChaff to close z-indexed field editors - if (this.props.anyModalVisible && !prevProps.anyModalVisible) { - this.ScratchBlocks.hideChaff(); - } - - // Only rerender the toolbox when the blocks are visible and the xml is - // different from the previously rendered toolbox xml. - // Do not check against prevProps.toolboxXML because that may not have been rendered. - if (this.props.isVisible && this.props.toolboxXML !== this._renderedToolboxXML) { - this.requestToolboxUpdate(); - } - - if (this.props.isVisible === prevProps.isVisible) { - if (this.props.stageSize !== prevProps.stageSize) { - // force workspace to redraw for the new stage size - window.dispatchEvent(new Event('resize')); - } - return; - } - // @todo hack to resize blockly manually in case resize happened while hidden - // @todo hack to reload the workspace due to gui bug #413 - if (this.props.isVisible) { // Scripts tab - this.workspace.setVisible(true); - if (prevProps.locale !== this.props.locale || this.props.locale !== this.props.vm.getLocale()) { - // call setLocale if the locale has changed, or changed while the blocks were hidden. - // vm.getLocale() will be out of sync if locale was changed while not visible - this.setLocale(); - } else { - this.props.vm.refreshWorkspace(); - this.requestToolboxUpdate(); - } - - window.dispatchEvent(new Event('resize')); - } else { - this.workspace.setVisible(false); - } - } - componentWillUnmount () { - this.detachVM(); - this.workspace.dispose(); - clearTimeout(this.toolboxUpdateTimeout); - } - requestToolboxUpdate () { - clearTimeout(this.toolboxUpdateTimeout); - this.toolboxUpdateTimeout = setTimeout(() => { - this.updateToolbox(); - }, 0); - } - setLocale () { - this.ScratchBlocks.ScratchMsgs.setLocale(this.props.locale); - this.props.vm.setLocale(this.props.locale, this.props.messages) - .then(() => { - this.workspace.getFlyout().setRecyclingEnabled(false); - this.props.vm.refreshWorkspace(); - this.requestToolboxUpdate(); - this.withToolboxUpdates(() => { - this.workspace.getFlyout().setRecyclingEnabled(true); - }); - }); - } - - updateToolbox () { - this.toolboxUpdateTimeout = false; - - const categoryId = this.workspace.toolbox_.getSelectedCategoryId(); - const offset = this.workspace.toolbox_.getCategoryScrollOffset(); - this.workspace.updateToolbox(this.props.toolboxXML); - this._renderedToolboxXML = this.props.toolboxXML; - - // In order to catch any changes that mutate the toolbox during "normal runtime" - // (variable changes/etc), re-enable toolbox refresh. - // Using the setter function will rerender the entire toolbox which we just rendered. - this.workspace.toolboxRefreshEnabled_ = true; - - const currentCategoryPos = this.workspace.toolbox_.getCategoryPositionById(categoryId); - const currentCategoryLen = this.workspace.toolbox_.getCategoryLengthById(categoryId); - if (offset < currentCategoryLen) { - this.workspace.toolbox_.setFlyoutScrollPos(currentCategoryPos + offset); - } else { - this.workspace.toolbox_.setFlyoutScrollPos(currentCategoryPos); - } - - const queue = this.toolboxUpdateQueue; - this.toolboxUpdateQueue = []; - queue.forEach(fn => fn()); - } - - withToolboxUpdates (fn) { - // if there is a queued toolbox update, we need to wait - if (this.toolboxUpdateTimeout) { - this.toolboxUpdateQueue.push(fn); - } else { - fn(); - } - } - - attachVM () { - this.workspace.addChangeListener(this.props.vm.blockListener); - this.flyoutWorkspace = this.workspace - .getFlyout() - .getWorkspace(); - this.flyoutWorkspace.addChangeListener(this.props.vm.flyoutBlockListener); - this.flyoutWorkspace.addChangeListener(this.props.vm.monitorBlockListener); - this.props.vm.addListener('SCRIPT_GLOW_ON', this.onScriptGlowOn); - this.props.vm.addListener('SCRIPT_GLOW_OFF', this.onScriptGlowOff); - this.props.vm.addListener('BLOCK_GLOW_ON', this.onBlockGlowOn); - this.props.vm.addListener('BLOCK_GLOW_OFF', this.onBlockGlowOff); - this.props.vm.addListener('VISUAL_REPORT', this.onVisualReport); - this.props.vm.addListener('workspaceUpdate', this.onWorkspaceUpdate); - this.props.vm.addListener('targetsUpdate', this.onTargetsUpdate); - this.props.vm.addListener('EXTENSION_ADDED', this.handleExtensionAdded); - this.props.vm.addListener('BLOCKSINFO_UPDATE', this.handleBlocksInfoUpdate); - this.props.vm.addListener('PERIPHERAL_CONNECTED', this.handleStatusButtonUpdate); - this.props.vm.addListener('PERIPHERAL_DISCONNECTED', this.handleStatusButtonUpdate); - } - detachVM () { - this.props.vm.removeListener('SCRIPT_GLOW_ON', this.onScriptGlowOn); - this.props.vm.removeListener('SCRIPT_GLOW_OFF', this.onScriptGlowOff); - this.props.vm.removeListener('BLOCK_GLOW_ON', this.onBlockGlowOn); - this.props.vm.removeListener('BLOCK_GLOW_OFF', this.onBlockGlowOff); - this.props.vm.removeListener('VISUAL_REPORT', this.onVisualReport); - this.props.vm.removeListener('workspaceUpdate', this.onWorkspaceUpdate); - this.props.vm.removeListener('targetsUpdate', this.onTargetsUpdate); - this.props.vm.removeListener('EXTENSION_ADDED', this.handleExtensionAdded); - this.props.vm.removeListener('BLOCKSINFO_UPDATE', this.handleBlocksInfoUpdate); - this.props.vm.removeListener('PERIPHERAL_CONNECTED', this.handleStatusButtonUpdate); - this.props.vm.removeListener('PERIPHERAL_DISCONNECTED', this.handleStatusButtonUpdate); - } - - updateToolboxBlockValue (id, value) { - this.withToolboxUpdates(() => { - const block = this.workspace - .getFlyout() - .getWorkspace() - .getBlockById(id); - if (block) { - block.inputList[0].fieldRow[0].setValue(value); - } - }); - } - - onTargetsUpdate () { - if (this.props.vm.editingTarget && this.workspace.getFlyout()) { - ['glide', 'move', 'set'].forEach(prefix => { - this.updateToolboxBlockValue(`${prefix}x`, Math.round(this.props.vm.editingTarget.x).toString()); - this.updateToolboxBlockValue(`${prefix}y`, Math.round(this.props.vm.editingTarget.y).toString()); - }); - } - } - onWorkspaceMetricsChange () { - const target = this.props.vm.editingTarget; - if (target && target.id) { - // Dispatch updateMetrics later, since onWorkspaceMetricsChange may be (very indirectly) - // called from a reducer, i.e. when you create a custom procedure. - // TODO: Is this a vehement hack? - setTimeout(() => { - this.props.updateMetrics({ - targetID: target.id, - scrollX: this.workspace.scrollX, - scrollY: this.workspace.scrollY, - scale: this.workspace.scale - }); - }, 0); - } - } - onScriptGlowOn (data) { - this.workspace.glowStack(data.id, true); - } - onScriptGlowOff (data) { - this.workspace.glowStack(data.id, false); - } - onBlockGlowOn (data) { - this.workspace.glowBlock(data.id, true); - } - onBlockGlowOff (data) { - this.workspace.glowBlock(data.id, false); - } - onVisualReport (data) { - this.workspace.reportValue(data.id, data.value); - } - getToolboxXML () { - // Use try/catch because this requires digging pretty deep into the VM - // Code inside intentionally ignores several error situations (no stage, etc.) - // Because they would get caught by this try/catch - try { - let {editingTarget: target, runtime} = this.props.vm; - const stage = runtime.getTargetForStage(); - if (!target) target = stage; // If no editingTarget, use the stage - - const stageCostumes = stage.getCostumes(); - const targetCostumes = target.getCostumes(); - const targetSounds = target.getSounds(); - const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(target); - return makeToolboxXML(false, target.isStage, target.id, dynamicBlocksXML, - targetCostumes[targetCostumes.length - 1].name, - stageCostumes[stageCostumes.length - 1].name, - targetSounds.length > 0 ? targetSounds[targetSounds.length - 1].name : '' - ); - } catch { - return null; - } - } - onWorkspaceUpdate (data) { - // When we change sprites, update the toolbox to have the new sprite's blocks - const toolboxXML = this.getToolboxXML(); - if (toolboxXML) { - this.props.updateToolboxState(toolboxXML); - } - - if (this.props.vm.editingTarget && !this.props.workspaceMetrics.targets[this.props.vm.editingTarget.id]) { - this.onWorkspaceMetricsChange(); - } - - // Remove and reattach the workspace listener (but allow flyout events) - this.workspace.removeChangeListener(this.props.vm.blockListener); - const dom = this.ScratchBlocks.Xml.textToDom(data.xml); - try { - this.ScratchBlocks.Xml.clearWorkspaceAndLoadFromXml(dom, this.workspace); - } catch (error) { - // The workspace is likely incomplete. What did update should be - // functional. - // - // Instead of throwing the error, by logging it and continuing as - // normal lets the other workspace update processes complete in the - // gui and vm, which lets the vm run even if the workspace is - // incomplete. Throwing the error would keep things like setting the - // correct editing target from happening which can interfere with - // some blocks and processes in the vm. - if (error.message) { - error.message = `Workspace Update Error: ${error.message}`; - } - log.error(error); - } - this.workspace.addChangeListener(this.props.vm.blockListener); - - if (this.props.vm.editingTarget && this.props.workspaceMetrics.targets[this.props.vm.editingTarget.id]) { - const {scrollX, scrollY, scale} = this.props.workspaceMetrics.targets[this.props.vm.editingTarget.id]; - this.workspace.scrollX = scrollX; - this.workspace.scrollY = scrollY; - this.workspace.scale = scale; - this.workspace.resize(); - } - - // Clear the undo state of the workspace since this is a - // fresh workspace and we don't want any changes made to another sprites - // workspace to be 'undone' here. - this.workspace.clearUndo(); - } - handleExtensionAdded (categoryInfo) { - const defineBlocks = blockInfoArray => { - if (blockInfoArray && blockInfoArray.length > 0) { - const staticBlocksJson = []; - const dynamicBlocksInfo = []; - blockInfoArray.forEach(blockInfo => { - if (blockInfo.info && blockInfo.info.isDynamic) { - dynamicBlocksInfo.push(blockInfo); - } else if (blockInfo.json) { - staticBlocksJson.push(blockInfo.json); - } - // otherwise it's a non-block entry such as '---' - }); - - this.ScratchBlocks.defineBlocksWithJsonArray(staticBlocksJson); - dynamicBlocksInfo.forEach(blockInfo => { - // This is creating the block factory / constructor -- NOT a specific instance of the block. - // The factory should only know static info about the block: the category info and the opcode. - // Anything else will be picked up from the XML attached to the block instance. - const extendedOpcode = `${categoryInfo.id}_${blockInfo.info.opcode}`; - const blockDefinition = - defineDynamicBlock(this.ScratchBlocks, categoryInfo, blockInfo, extendedOpcode); - this.ScratchBlocks.Blocks[extendedOpcode] = blockDefinition; - }); - } - }; - - // scratch-blocks implements a menu or custom field as a special kind of block ("shadow" block) - // these actually define blocks and MUST run regardless of the UI state - defineBlocks( - Object.getOwnPropertyNames(categoryInfo.customFieldTypes) - .map(fieldTypeName => categoryInfo.customFieldTypes[fieldTypeName].scratchBlocksDefinition)); - defineBlocks(categoryInfo.menus); - defineBlocks(categoryInfo.blocks); - - // Update the toolbox with new blocks if possible - const toolboxXML = this.getToolboxXML(); - if (toolboxXML) { - this.props.updateToolboxState(toolboxXML); - } - } - handleBlocksInfoUpdate (categoryInfo) { - // @todo Later we should replace this to avoid all the warnings from redefining blocks. - this.handleExtensionAdded(categoryInfo); - } - handleCategorySelected (categoryId) { - const extension = extensionData.find(ext => ext.extensionId === categoryId); - if (extension && extension.launchPeripheralConnectionFlow) { - this.handleConnectionModalStart(categoryId); - } - - this.withToolboxUpdates(() => { - this.workspace.toolbox_.setSelectedCategoryById(categoryId); - }); - } - setBlocks (blocks) { - this.blocks = blocks; - } - handlePromptStart (message, defaultValue, callback, optTitle, optVarType) { - const p = {prompt: {callback, message, defaultValue}}; - p.prompt.title = optTitle ? optTitle : - this.ScratchBlocks.Msg.VARIABLE_MODAL_TITLE; - p.prompt.varType = typeof optVarType === 'string' ? - optVarType : this.ScratchBlocks.SCALAR_VARIABLE_TYPE; - p.prompt.showVariableOptions = // This flag means that we should show variable/list options about scope - optVarType !== this.ScratchBlocks.BROADCAST_MESSAGE_VARIABLE_TYPE && - p.prompt.title !== this.ScratchBlocks.Msg.RENAME_VARIABLE_MODAL_TITLE && - p.prompt.title !== this.ScratchBlocks.Msg.RENAME_LIST_MODAL_TITLE; - p.prompt.showCloudOption = (optVarType === this.ScratchBlocks.SCALAR_VARIABLE_TYPE) && this.props.canUseCloud; - this.setState(p); - } - handleConnectionModalStart (extensionId) { - this.props.onOpenConnectionModal(extensionId); - } - handleStatusButtonUpdate () { - this.ScratchBlocks.refreshStatusButtons(this.workspace); - } - handleOpenSoundRecorder () { - this.props.onOpenSoundRecorder(); - } - - /* - * Pass along information about proposed name and variable options (scope and isCloud) - * and additional potentially conflicting variable names from the VM - * to the variable validation prompt callback used in scratch-blocks. - */ - handlePromptCallback (input, variableOptions) { - this.state.prompt.callback( - input, - this.props.vm.runtime.getAllVarNamesOfType(this.state.prompt.varType), - variableOptions); - this.handlePromptClose(); - } - handlePromptClose () { - this.setState({prompt: null}); - } - handleCustomProceduresClose (data) { - this.props.onRequestCloseCustomProcedures(data); - const ws = this.workspace; - ws.refreshToolboxSelection_(); - ws.toolbox_.scrollToCategoryById('myBlocks'); - } - handleDrop (dragInfo) { - fetch(dragInfo.payload.bodyUrl) - .then(response => response.json()) - .then(blocks => this.props.vm.shareBlocksToTarget(blocks, this.props.vm.editingTarget.id)) - .then(() => { - this.props.vm.refreshWorkspace(); - this.updateToolbox(); // To show new variables/custom blocks - }); - } - render () { - /* eslint-disable no-unused-vars */ - const { - anyModalVisible, - canUseCloud, - customProceduresVisible, - extensionLibraryVisible, - options, - stageSize, - vm, - isRtl, - isVisible, - onActivateColorPicker, - onOpenConnectionModal, - onOpenSoundRecorder, - updateToolboxState, - onActivateCustomProcedures, - onRequestCloseExtensionLibrary, - onRequestCloseCustomProcedures, - toolboxXML, - updateMetrics: updateMetricsProp, - workspaceMetrics, - ...props - } = this.props; - /* eslint-enable no-unused-vars */ - return ( - - - {this.state.prompt ? ( - - ) : null} - {extensionLibraryVisible ? ( - - ) : null} - {customProceduresVisible ? ( - - ) : null} - - ); - } -} - -Blocks.propTypes = { - anyModalVisible: PropTypes.bool, - canUseCloud: PropTypes.bool, - customProceduresVisible: PropTypes.bool, - extensionLibraryVisible: PropTypes.bool, - isRtl: PropTypes.bool, - isVisible: PropTypes.bool, - locale: PropTypes.string.isRequired, - messages: PropTypes.objectOf(PropTypes.string), - onActivateColorPicker: PropTypes.func, - onActivateCustomProcedures: PropTypes.func, - onOpenConnectionModal: PropTypes.func, - onOpenSoundRecorder: PropTypes.func, - onRequestCloseCustomProcedures: PropTypes.func, - onRequestCloseExtensionLibrary: PropTypes.func, - options: PropTypes.shape({ - media: PropTypes.string, - zoom: PropTypes.shape({ - controls: PropTypes.bool, - wheel: PropTypes.bool, - startScale: PropTypes.number - }), - colours: PropTypes.shape({ - workspace: PropTypes.string, - flyout: PropTypes.string, - toolbox: PropTypes.string, - toolboxSelected: PropTypes.string, - scrollbar: PropTypes.string, - scrollbarHover: PropTypes.string, - insertionMarker: PropTypes.string, - insertionMarkerOpacity: PropTypes.number, - fieldShadow: PropTypes.string, - dragShadowOpacity: PropTypes.number - }), - comments: PropTypes.bool, - collapse: PropTypes.bool - }), - stageSize: PropTypes.oneOf(Object.keys(STAGE_DISPLAY_SIZES)).isRequired, - toolboxXML: PropTypes.string, - updateMetrics: PropTypes.func, - updateToolboxState: PropTypes.func, - vm: PropTypes.instanceOf(VM).isRequired, - workspaceMetrics: PropTypes.shape({ - targets: PropTypes.objectOf(PropTypes.object) - }) -}; - -Blocks.defaultOptions = { - zoom: { - controls: true, - wheel: true, - startScale: BLOCKS_DEFAULT_SCALE - }, - grid: { - spacing: 40, - length: 2, - colour: '#ddd' - }, - colours: { - workspace: '#F9F9F9', - flyout: '#F9F9F9', - toolbox: '#FFFFFF', - toolboxSelected: '#E9EEF2', - scrollbar: '#CECDCE', - scrollbarHover: '#CECDCE', - insertionMarker: '#000000', - insertionMarkerOpacity: 0.2, - fieldShadow: 'rgba(255, 255, 255, 0.3)', - dragShadowOpacity: 0.6 - }, - comments: true, - collapse: false, - sounds: false -}; - -Blocks.defaultProps = { - isVisible: true, - options: Blocks.defaultOptions -}; - -const mapStateToProps = state => ({ - anyModalVisible: ( - Object.keys(state.scratchGui.modals).some(key => state.scratchGui.modals[key]) || - state.scratchGui.mode.isFullScreen - ), - extensionLibraryVisible: state.scratchGui.modals.extensionLibrary, - isRtl: state.locales.isRtl, - locale: state.locales.locale, - messages: state.locales.messages, - toolboxXML: state.scratchGui.toolbox.toolboxXML, - customProceduresVisible: state.scratchGui.customProcedures.active, - workspaceMetrics: state.scratchGui.workspaceMetrics -}); - -const mapDispatchToProps = dispatch => ({ - onActivateColorPicker: callback => dispatch(activateColorPicker(callback)), - onActivateCustomProcedures: (data, callback) => dispatch(activateCustomProcedures(data, callback)), - onOpenConnectionModal: id => { - dispatch(setConnectionModalExtensionId(id)); - dispatch(openConnectionModal()); - }, - onOpenSoundRecorder: () => { - dispatch(activateTab(SOUNDS_TAB_INDEX)); - dispatch(openSoundRecorder()); - }, - onRequestCloseExtensionLibrary: () => { - dispatch(closeExtensionLibrary()); - }, - onRequestCloseCustomProcedures: data => { - dispatch(deactivateCustomProcedures(data)); - }, - updateToolboxState: toolboxXML => { - dispatch(updateToolbox(toolboxXML)); - }, - updateMetrics: metrics => { - dispatch(updateMetrics(metrics)); - } -}); - -export default errorBoundaryHOC('Blocks')( - connect( - mapStateToProps, - mapDispatchToProps - )(Blocks) -); diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx deleted file mode 100644 index 79a41db7ea..0000000000 --- a/packages/scratch-gui/src/containers/stage.jsx +++ /dev/null @@ -1,476 +0,0 @@ -import bindAll from 'lodash.bindall'; -import PropTypes from 'prop-types'; -import React from 'react'; -import Renderer from 'scratch-render'; -import VM from 'scratch-vm'; -import {connect} from 'react-redux'; - -import {STAGE_DISPLAY_SIZES} from '../lib/layout-constants'; -import {getEventXY} from '../lib/touch-utils'; -import VideoProvider from '../lib/video/video-provider'; -import {SVGRenderer as V2SVGAdapter} from 'scratch-svg-renderer'; -import {BitmapAdapter as V2BitmapAdapter} from 'scratch-svg-renderer'; - -import StageComponent from '../components/stage/stage.jsx'; - -import { - activateColorPicker, - deactivateColorPicker -} from '../reducers/color-picker'; - -const colorPickerRadius = 20; -const dragThreshold = 3; // Same as the block drag threshold - -class Stage extends React.Component { - constructor (props) { - super(props); - bindAll(this, [ - 'attachMouseEvents', - 'cancelMouseDownTimeout', - 'detachMouseEvents', - 'handleDoubleClick', - 'handleQuestionAnswered', - 'onMouseUp', - 'onMouseMove', - 'onMouseDown', - 'onStartDrag', - 'onStopDrag', - 'onWheel', - 'updateRect', - 'questionListener', - 'setDragCanvas', - 'clearDragCanvas', - 'drawDragCanvas', - 'positionDragCanvas' - ]); - this.state = { - mouseDownTimeoutId: null, - mouseDownPosition: null, - isDragging: false, - dragOffset: null, - dragId: null, - colorInfo: null, - question: null - }; - if (this.props.vm.renderer) { - this.renderer = this.props.vm.renderer; - this.canvas = this.renderer.canvas; - } else { - this.canvas = document.createElement('canvas'); - this.renderer = new Renderer(this.canvas); - this.props.vm.attachRenderer(this.renderer); - - // Only attach a video provider once because it is stateful - this.props.vm.setVideoProvider(new VideoProvider()); - - // Calling draw a single time before any project is loaded just makes - // the canvas white instead of solid black–needed because it is not - // possible to use CSS to style the canvas to have a different - // default color - this.props.vm.renderer.draw(); - } - this.props.vm.attachV2SVGAdapter(new V2SVGAdapter()); - this.props.vm.attachV2BitmapAdapter(new V2BitmapAdapter()); - } - componentDidMount () { - this.attachRectEvents(); - this.attachMouseEvents(this.canvas); - this.updateRect(); - this.props.vm.runtime.addListener('QUESTION', this.questionListener); - } - shouldComponentUpdate (nextProps, nextState) { - return this.props.stageSize !== nextProps.stageSize || - this.props.isColorPicking !== nextProps.isColorPicking || - this.state.colorInfo !== nextState.colorInfo || - this.props.isFullScreen !== nextProps.isFullScreen || - this.state.question !== nextState.question || - this.props.micIndicator !== nextProps.micIndicator || - this.props.isStarted !== nextProps.isStarted; - } - componentDidUpdate (prevProps) { - if (this.props.isColorPicking && !prevProps.isColorPicking) { - this.startColorPickingLoop(); - } else if (!this.props.isColorPicking && prevProps.isColorPicking) { - this.stopColorPickingLoop(); - } - this.updateRect(); - this.renderer.resize(this.rect.width, this.rect.height); - } - componentWillUnmount () { - this.detachMouseEvents(this.canvas); - this.detachRectEvents(); - this.stopColorPickingLoop(); - this.props.vm.runtime.removeListener('QUESTION', this.questionListener); - } - questionListener (question) { - this.setState({question: question}); - } - handleQuestionAnswered (answer) { - this.setState({question: null}, () => { - this.props.vm.runtime.emit('ANSWER', answer); - }); - } - startColorPickingLoop () { - this.intervalId = setInterval(() => { - if (typeof this.pickX === 'number') { - this.setState({colorInfo: this.getColorInfo(this.pickX, this.pickY)}); - } - }, 30); - } - stopColorPickingLoop () { - clearInterval(this.intervalId); - } - attachMouseEvents (canvas) { - document.addEventListener('mousemove', this.onMouseMove); - document.addEventListener('mouseup', this.onMouseUp); - document.addEventListener('touchmove', this.onMouseMove); - document.addEventListener('touchend', this.onMouseUp); - canvas.addEventListener('mousedown', this.onMouseDown); - canvas.addEventListener('touchstart', this.onMouseDown); - canvas.addEventListener('wheel', this.onWheel); - } - detachMouseEvents (canvas) { - document.removeEventListener('mousemove', this.onMouseMove); - document.removeEventListener('mouseup', this.onMouseUp); - document.removeEventListener('touchmove', this.onMouseMove); - document.removeEventListener('touchend', this.onMouseUp); - canvas.removeEventListener('mousedown', this.onMouseDown); - canvas.removeEventListener('touchstart', this.onMouseDown); - canvas.removeEventListener('wheel', this.onWheel); - } - attachRectEvents () { - window.addEventListener('resize', this.updateRect); - window.addEventListener('scroll', this.updateRect); - } - detachRectEvents () { - window.removeEventListener('resize', this.updateRect); - window.removeEventListener('scroll', this.updateRect); - } - updateRect () { - this.rect = this.canvas.getBoundingClientRect(); - } - getScratchCoords (x, y) { - const nativeSize = this.renderer.getNativeSize(); - return [ - (nativeSize[0] / this.rect.width) * (x - (this.rect.width / 2)), - (nativeSize[1] / this.rect.height) * (y - (this.rect.height / 2)) - ]; - } - getColorInfo (x, y) { - return { - x: x, - y: y, - ...this.renderer.extractColor(x, y, colorPickerRadius) - }; - } - handleDoubleClick (e) { - const {x, y} = getEventXY(e); - // Set editing target from cursor position, if clicking on a sprite. - const mousePosition = [x - this.rect.left, y - this.rect.top]; - const drawableId = this.renderer.pick(mousePosition[0], mousePosition[1]); - if (drawableId === null) return; - const targetId = this.props.vm.getTargetIdForDrawableId(drawableId); - if (targetId === null) return; - this.props.vm.setEditingTarget(targetId); - } - onMouseMove (e) { - const {x, y} = getEventXY(e); - const mousePosition = [x - this.rect.left, y - this.rect.top]; - - if (this.props.isColorPicking) { - // Set the pickX/Y for the color picker loop to pick up - this.pickX = mousePosition[0]; - this.pickY = mousePosition[1]; - } - - if (this.state.mouseDown && !this.state.isDragging) { - const distanceFromMouseDown = Math.sqrt( - Math.pow(mousePosition[0] - this.state.mouseDownPosition[0], 2) + - Math.pow(mousePosition[1] - this.state.mouseDownPosition[1], 2) - ); - if (distanceFromMouseDown > dragThreshold) { - this.cancelMouseDownTimeout(); - this.onStartDrag(...this.state.mouseDownPosition); - } - } - if (this.state.mouseDown && this.state.isDragging) { - // Editor drag style only updates the drag canvas, does full update at the end of drag - // Non-editor drag style just updates the sprite continuously. - if (this.props.useEditorDragStyle) { - this.positionDragCanvas(mousePosition[0], mousePosition[1]); - } else { - const spritePosition = this.getScratchCoords(mousePosition[0], mousePosition[1]); - this.props.vm.postSpriteInfo({ - x: spritePosition[0] + this.state.dragOffset[0], - y: -(spritePosition[1] + this.state.dragOffset[1]), - force: true - }); - } - } - const coordinates = { - x: mousePosition[0], - y: mousePosition[1], - canvasWidth: this.rect.width, - canvasHeight: this.rect.height - }; - this.props.vm.postIOData('mouse', coordinates); - } - onMouseUp (e) { - const {x, y} = getEventXY(e); - const mousePosition = [x - this.rect.left, y - this.rect.top]; - this.cancelMouseDownTimeout(); - this.setState({ - mouseDown: false, - mouseDownPosition: null - }); - const data = { - isDown: false, - x: x - this.rect.left, - y: y - this.rect.top, - canvasWidth: this.rect.width, - canvasHeight: this.rect.height, - wasDragged: this.state.isDragging - }; - if (this.state.isDragging) { - this.onStopDrag(mousePosition[0], mousePosition[1]); - } - this.props.vm.postIOData('mouse', data); - - if (this.props.isColorPicking && - mousePosition[0] > 0 && mousePosition[0] < this.rect.width && - mousePosition[1] > 0 && mousePosition[1] < this.rect.height - ) { - const {r, g, b} = this.state.colorInfo.color; - const componentToString = c => { - const hex = c.toString(16); - return hex.length === 1 ? `0${hex}` : hex; - }; - const colorString = `#${componentToString(r)}${componentToString(g)}${componentToString(b)}`; - this.props.onDeactivateColorPicker(colorString); - this.setState({colorInfo: null}); - this.pickX = null; - this.pickY = null; - } - } - onMouseDown (e) { - this.updateRect(); - const {x, y} = getEventXY(e); - const mousePosition = [x - this.rect.left, y - this.rect.top]; - if (this.props.isColorPicking) { - // Set the pickX/Y for the color picker loop to pick up - this.pickX = mousePosition[0]; - this.pickY = mousePosition[1]; - // Immediately update the color picker info - this.setState({colorInfo: this.getColorInfo(this.pickX, this.pickY)}); - } else { - if (e.button === 0 || (window.TouchEvent && e instanceof TouchEvent)) { - this.setState({ - mouseDown: true, - mouseDownPosition: mousePosition, - mouseDownTimeoutId: setTimeout( - this.onStartDrag.bind(this, mousePosition[0], mousePosition[1]), - 400 - ) - }); - } - const data = { - isDown: true, - x: mousePosition[0], - y: mousePosition[1], - canvasWidth: this.rect.width, - canvasHeight: this.rect.height - }; - this.props.vm.postIOData('mouse', data); - if (e.preventDefault) { - // Prevent default to prevent touch from dragging page - e.preventDefault(); - // But we do want any active input to be blurred - if (document.activeElement && document.activeElement.blur) { - document.activeElement.blur(); - } - } - } - } - onWheel (e) { - const data = { - deltaX: e.deltaX, - deltaY: e.deltaY - }; - this.props.vm.postIOData('mouseWheel', data); - } - cancelMouseDownTimeout () { - if (this.state.mouseDownTimeoutId !== null) { - clearTimeout(this.state.mouseDownTimeoutId); - } - this.setState({mouseDownTimeoutId: null}); - } - /** - * Initialize the position of the "dragged sprite" canvas - * @param {DrawableExtraction} drawableData The data returned from renderer.extractDrawableScreenSpace - * @param {number} x The x position of the initial drag event - * @param {number} y The y position of the initial drag event - */ - drawDragCanvas (drawableData, x, y) { - const { - imageData, - x: boundsX, - y: boundsY, - width: boundsWidth, - height: boundsHeight - } = drawableData; - this.dragCanvas.width = imageData.width; - this.dragCanvas.height = imageData.height; - // On high-DPI devices, the canvas size in layout-pixels is not equal to the size of the extracted data. - this.dragCanvas.style.width = `${boundsWidth}px`; - this.dragCanvas.style.height = `${boundsHeight}px`; - - this.dragCanvas.getContext('2d').putImageData(imageData, 0, 0); - // Position so that pick location is at (0, 0) so that positionDragCanvas() - // can use translation to move to mouse position smoothly. - this.dragCanvas.style.left = `${boundsX - x}px`; - this.dragCanvas.style.top = `${boundsY - y}px`; - this.dragCanvas.style.display = 'block'; - } - clearDragCanvas () { - this.dragCanvas.width = this.dragCanvas.height = 0; - this.dragCanvas.style.display = 'none'; - } - positionDragCanvas (mouseX, mouseY) { - // mouseX/Y are relative to stage top/left, and dragCanvas is already - // positioned so that the pick location is at (0,0). - this.dragCanvas.style.transform = `translate(${mouseX}px, ${mouseY}px)`; - } - onStartDrag (x, y) { - if (this.state.dragId) return; - - // Targets with no attached drawable cannot be dragged. - let draggableTargets = this.props.vm.runtime.targets.filter( - target => Number.isFinite(target.drawableID) - ); - - // Because pick queries can be expensive, only perform them for drawables that are currently draggable. - // If we're in the editor, we can drag all targets. Otherwise, filter. - if (!this.props.useEditorDragStyle) { - draggableTargets = draggableTargets.filter( - target => target.draggable - ); - } - if (draggableTargets.length === 0) return; - - const draggableIDs = draggableTargets.map(target => target.drawableID); - const drawableId = this.renderer.pick(x, y, 1, 1, draggableIDs); - if (drawableId === null) return; - const targetId = this.props.vm.getTargetIdForDrawableId(drawableId); - if (targetId === null) return; - - const target = this.props.vm.runtime.getTargetById(targetId); - - // Dragging always brings the target to the front - target.goToFront(); - - const [scratchMouseX, scratchMouseY] = this.getScratchCoords(x, y); - const offsetX = target.x - scratchMouseX; - const offsetY = -(target.y + scratchMouseY); - - this.props.vm.startDrag(targetId); - this.setState({ - isDragging: true, - dragId: targetId, - dragOffset: [offsetX, offsetY] - }); - if (this.props.useEditorDragStyle) { - // Extract the drawable art - const drawableData = this.renderer.extractDrawableScreenSpace(drawableId); - this.drawDragCanvas(drawableData, x, y); - this.positionDragCanvas(x, y); - this.props.vm.postSpriteInfo({visible: false}); - this.props.vm.renderer.draw(); - } - } - onStopDrag (mouseX, mouseY) { - const dragId = this.state.dragId; - const commonStopDragActions = () => { - this.props.vm.stopDrag(dragId); - this.setState({ - isDragging: false, - dragOffset: null, - dragId: null - }); - }; - if (this.props.useEditorDragStyle) { - // Need to sequence these actions to prevent flickering. - const spriteInfo = {visible: true}; - // First update the sprite position if dropped in the stage. - if (mouseX > 0 && mouseX < this.rect.width && - mouseY > 0 && mouseY < this.rect.height) { - const spritePosition = this.getScratchCoords(mouseX, mouseY); - spriteInfo.x = spritePosition[0] + this.state.dragOffset[0]; - spriteInfo.y = -(spritePosition[1] + this.state.dragOffset[1]); - spriteInfo.force = true; - } - this.props.vm.postSpriteInfo(spriteInfo); - // Then clear the dragging canvas and stop drag (potentially slow if selecting sprite) - this.clearDragCanvas(); - commonStopDragActions(); - this.props.vm.renderer.draw(); - } else { - commonStopDragActions(); - } - } - setDragCanvas (canvas) { - this.dragCanvas = canvas; - } - render () { - const { - vm, // eslint-disable-line no-unused-vars - onActivateColorPicker, // eslint-disable-line no-unused-vars - ...props - } = this.props; - return ( - - ); - } -} - -Stage.propTypes = { - isColorPicking: PropTypes.bool, - isFullScreen: PropTypes.bool.isRequired, - isStarted: PropTypes.bool, - micIndicator: PropTypes.bool, - onActivateColorPicker: PropTypes.func, - onDeactivateColorPicker: PropTypes.func, - stageSize: PropTypes.oneOf(Object.keys(STAGE_DISPLAY_SIZES)).isRequired, - useEditorDragStyle: PropTypes.bool, - vm: PropTypes.instanceOf(VM).isRequired -}; - -Stage.defaultProps = { - useEditorDragStyle: true -}; - -const mapStateToProps = state => ({ - isColorPicking: state.scratchGui.colorPicker.active, - isFullScreen: state.scratchGui.mode.isFullScreen, - isStarted: state.scratchGui.vmStatus.started, - micIndicator: state.scratchGui.micIndicator, - // Do not use editor drag style in fullscreen or player mode. - useEditorDragStyle: !(state.scratchGui.mode.isFullScreen || state.scratchGui.mode.isPlayerOnly) -}); - -const mapDispatchToProps = dispatch => ({ - onActivateColorPicker: () => dispatch(activateColorPicker()), - onDeactivateColorPicker: color => dispatch(deactivateColorPicker(color)) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(Stage); diff --git a/packages/scratch-gui/src/lib/alerts/index.jsx b/packages/scratch-gui/src/lib/alerts/index.jsx deleted file mode 100644 index dbd4f2ce7d..0000000000 --- a/packages/scratch-gui/src/lib/alerts/index.jsx +++ /dev/null @@ -1,222 +0,0 @@ -import React from 'react'; -import {FormattedMessage} from 'react-intl'; -import keyMirror from 'keymirror'; - -import successImage from '../assets/icon--success.svg'; - -const AlertTypes = keyMirror({ - STANDARD: null, - EXTENSION: null, - INLINE: null -}); - -const AlertLevels = { - SUCCESS: 'success', - INFO: 'info', - WARN: 'warn' -}; - -const alerts = [ - { - alertId: 'createSuccess', - alertType: AlertTypes.STANDARD, - clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', - 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], - content: ( - - ), - iconURL: successImage, - level: AlertLevels.SUCCESS, - maxDisplaySecs: 5 - }, - { - alertId: 'createCopySuccess', - alertType: AlertTypes.STANDARD, - clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', - 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], - content: ( - - ), - iconURL: successImage, - level: AlertLevels.SUCCESS, - maxDisplaySecs: 5 - }, - { - alertId: 'createRemixSuccess', - alertType: AlertTypes.STANDARD, - clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', - 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], - content: ( - - ), - iconURL: successImage, - level: AlertLevels.SUCCESS, - maxDisplaySecs: 5 - }, - { - alertId: 'creating', - alertType: AlertTypes.STANDARD, - clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', - 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], - content: ( - - ), - iconSpinner: true, - level: AlertLevels.SUCCESS - }, - { - alertId: 'creatingCopy', - alertType: AlertTypes.STANDARD, - clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', - 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], - content: ( - - ), - iconSpinner: true, - level: AlertLevels.SUCCESS - }, - { - alertId: 'creatingRemix', - alertType: AlertTypes.STANDARD, - clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', - 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], - content: ( - - ), - iconSpinner: true, - level: AlertLevels.SUCCESS - }, - { - alertId: 'creatingError', - clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', - 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], - closeButton: true, - content: ( - - ), - level: AlertLevels.WARN - }, - { - alertId: 'savingError', - clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', - 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], - showDownload: true, - showSaveNow: true, - closeButton: false, - content: ( - - ), - level: AlertLevels.WARN - }, - { - alertId: 'saveSuccess', - alertType: AlertTypes.INLINE, - clearList: ['saveSuccess', 'saving', 'savingError'], - content: ( - - ), - iconURL: successImage, - level: AlertLevels.SUCCESS, - maxDisplaySecs: 3 - }, - { - alertId: 'saving', - alertType: AlertTypes.INLINE, - clearList: ['saveSuccess', 'saving', 'savingError'], - content: ( - - ), - iconSpinner: true, - level: AlertLevels.INFO - }, - { - alertId: 'cloudInfo', - alertType: AlertTypes.STANDARD, - clearList: ['cloudInfo'], - content: ( - - - - ) - }} - /> - ), - closeButton: true, - level: AlertLevels.SUCCESS, - maxDisplaySecs: 15 - }, - { - alertId: 'importingAsset', - alertType: AlertTypes.STANDARD, - clearList: [], - content: ( - - ), - iconSpinner: true, - level: AlertLevels.SUCCESS - } -]; - -export { - alerts as default, - AlertLevels, - AlertTypes -}; diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx deleted file mode 100644 index ba18b916b5..0000000000 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ /dev/null @@ -1,321 +0,0 @@ -import React from 'react'; -import {FormattedMessage} from 'react-intl'; - -import musicIconURL from './music/music.png'; -import musicInsetIconURL from './music/music-small.svg'; - -import penIconURL from './pen/pen.png'; -import penInsetIconURL from './pen/pen-small.svg'; - -import videoSensingIconURL from './videoSensing/video-sensing.png'; -import videoSensingInsetIconURL from './videoSensing/video-sensing-small.svg'; - -import text2speechIconURL from './text2speech/text2speech.png'; -import text2speechInsetIconURL from './text2speech/text2speech-small.svg'; - -import translateIconURL from './translate/translate.png'; -import translateInsetIconURL from './translate/translate-small.png'; - -import makeymakeyIconURL from './makeymakey/makeymakey.png'; -import makeymakeyInsetIconURL from './makeymakey/makeymakey-small.svg'; - -import microbitIconURL from './microbit/microbit.png'; -import microbitInsetIconURL from './microbit/microbit-small.svg'; -import microbitConnectionIconURL from './microbit/microbit-illustration.svg'; -import microbitConnectionSmallIconURL from './microbit/microbit-small.svg'; - -import ev3IconURL from './ev3/ev3.png'; -import ev3InsetIconURL from './ev3/ev3-small.svg'; -import ev3ConnectionIconURL from './ev3/ev3-hub-illustration.svg'; -import ev3ConnectionSmallIconURL from './ev3/ev3-small.svg'; - -import wedo2IconURL from './wedo2/wedo.png'; // TODO: Rename file names to match variable/prop names? -import wedo2InsetIconURL from './wedo2/wedo-small.svg'; -import wedo2ConnectionIconURL from './wedo2/wedo-illustration.svg'; -import wedo2ConnectionSmallIconURL from './wedo2/wedo-small.svg'; -import wedo2ConnectionTipIconURL from './wedo2/wedo-button-illustration.svg'; - -import boostIconURL from './boost/boost.png'; -import boostInsetIconURL from './boost/boost-small.svg'; -import boostConnectionIconURL from './boost/boost-illustration.svg'; -import boostConnectionSmallIconURL from './boost/boost-small.svg'; -import boostConnectionTipIconURL from './boost/boost-button-illustration.svg'; - -import gdxforIconURL from './gdxfor/gdxfor.png'; -import gdxforInsetIconURL from './gdxfor/gdxfor-small.svg'; -import gdxforConnectionIconURL from './gdxfor/gdxfor-illustration.svg'; -import gdxforConnectionSmallIconURL from './gdxfor/gdxfor-small.svg'; - -export default [ - { - name: ( - - ), - extensionId: 'music', - iconURL: musicIconURL, - insetIconURL: musicInsetIconURL, - description: ( - - ), - featured: true - }, - { - name: ( - - ), - extensionId: 'pen', - iconURL: penIconURL, - insetIconURL: penInsetIconURL, - description: ( - - ), - featured: true - }, - { - name: ( - - ), - extensionId: 'videoSensing', - iconURL: videoSensingIconURL, - insetIconURL: videoSensingInsetIconURL, - description: ( - - ), - featured: true - }, - { - name: ( - - ), - extensionId: 'text2speech', - collaborator: 'Amazon Web Services', - iconURL: text2speechIconURL, - insetIconURL: text2speechInsetIconURL, - description: ( - - ), - featured: true, - internetConnectionRequired: true - }, - { - name: ( - - ), - extensionId: 'translate', - collaborator: 'Google', - iconURL: translateIconURL, - insetIconURL: translateInsetIconURL, - description: ( - - ), - featured: true, - internetConnectionRequired: true - }, - { - name: 'Makey Makey', - extensionId: 'makeymakey', - collaborator: 'JoyLabz', - iconURL: makeymakeyIconURL, - insetIconURL: makeymakeyInsetIconURL, - description: ( - - ), - featured: true - }, - { - name: 'micro:bit', - extensionId: 'microbit', - collaborator: 'micro:bit', - iconURL: microbitIconURL, - insetIconURL: microbitInsetIconURL, - description: ( - - ), - featured: true, - disabled: false, - bluetoothRequired: true, - internetConnectionRequired: true, - launchPeripheralConnectionFlow: true, - useAutoScan: false, - connectionIconURL: microbitConnectionIconURL, - connectionSmallIconURL: microbitConnectionSmallIconURL, - connectingMessage: ( - - ), - helpLink: 'https://scratch.mit.edu/microbit' - }, - { - name: 'LEGO MINDSTORMS EV3', - extensionId: 'ev3', - collaborator: 'LEGO', - iconURL: ev3IconURL, - insetIconURL: ev3InsetIconURL, - description: ( - - ), - featured: true, - disabled: false, - bluetoothRequired: true, - internetConnectionRequired: true, - launchPeripheralConnectionFlow: true, - useAutoScan: false, - connectionIconURL: ev3ConnectionIconURL, - connectionSmallIconURL: ev3ConnectionSmallIconURL, - connectingMessage: ( - - ), - helpLink: 'https://scratch.mit.edu/ev3' - }, - { - name: 'LEGO BOOST', - extensionId: 'boost', - collaborator: 'LEGO', - iconURL: boostIconURL, - insetIconURL: boostInsetIconURL, - description: ( - - ), - featured: true, - disabled: false, - bluetoothRequired: true, - internetConnectionRequired: true, - launchPeripheralConnectionFlow: true, - useAutoScan: true, - connectionIconURL: boostConnectionIconURL, - connectionSmallIconURL: boostConnectionSmallIconURL, - connectionTipIconURL: boostConnectionTipIconURL, - connectingMessage: ( - - ), - helpLink: 'https://scratch.mit.edu/boost' - }, - { - name: 'LEGO Education WeDo 2.0', - extensionId: 'wedo2', - collaborator: 'LEGO', - iconURL: wedo2IconURL, - insetIconURL: wedo2InsetIconURL, - description: ( - - ), - featured: true, - disabled: false, - bluetoothRequired: true, - internetConnectionRequired: true, - launchPeripheralConnectionFlow: true, - useAutoScan: true, - connectionIconURL: wedo2ConnectionIconURL, - connectionSmallIconURL: wedo2ConnectionSmallIconURL, - connectionTipIconURL: wedo2ConnectionTipIconURL, - connectingMessage: ( - - ), - helpLink: 'https://scratch.mit.edu/wedo' - }, - { - name: 'Go Direct Force & Acceleration', - extensionId: 'gdxfor', - collaborator: 'Vernier', - iconURL: gdxforIconURL, - insetIconURL: gdxforInsetIconURL, - description: ( - - ), - featured: true, - disabled: false, - bluetoothRequired: true, - internetConnectionRequired: true, - launchPeripheralConnectionFlow: true, - useAutoScan: false, - connectionIconURL: gdxforConnectionIconURL, - connectionSmallIconURL: gdxforConnectionSmallIconURL, - connectingMessage: ( - - ), - helpLink: 'https://scratch.mit.edu/vernier' - } -]; diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx deleted file mode 100644 index b6207a5b7d..0000000000 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ /dev/null @@ -1,213 +0,0 @@ -import bindAll from 'lodash.bindall'; -import PropTypes from 'prop-types'; -import React from 'react'; -import VM from 'scratch-vm'; - -import {connect} from 'react-redux'; - -import {updateTargets} from '../reducers/targets'; -import {updateBlockDrag} from '../reducers/block-drag'; -import {updateMonitors} from '../reducers/monitors'; -import {setProjectChanged, setProjectUnchanged} from '../reducers/project-changed'; -import {setRunningState, setTurboState, setStartedState} from '../reducers/vm-status'; -import {showExtensionAlert} from '../reducers/alerts'; -import {updateMicIndicator} from '../reducers/mic-indicator'; - -/* - * Higher Order Component to manage events emitted by the VM - * @param {React.Component} WrappedComponent component to manage VM events for - * @returns {React.Component} connected component with vm events bound to redux - */ -const vmListenerHOC = function (WrappedComponent) { - class VMListener extends React.Component { - constructor (props) { - super(props); - bindAll(this, [ - 'handleKeyDown', - 'handleKeyUp', - 'handleProjectChanged', - 'handleTargetsUpdate' - ]); - // We have to start listening to the vm here rather than in - // componentDidMount because the HOC mounts the wrapped component, - // so the HOC componentDidMount triggers after the wrapped component - // mounts. - // If the wrapped component uses the vm in componentDidMount, then - // we need to start listening before mounting the wrapped component. - this.props.vm.on('targetsUpdate', this.handleTargetsUpdate); - this.props.vm.on('MONITORS_UPDATE', this.props.onMonitorsUpdate); - this.props.vm.on('BLOCK_DRAG_UPDATE', this.props.onBlockDragUpdate); - this.props.vm.on('TURBO_MODE_ON', this.props.onTurboModeOn); - this.props.vm.on('TURBO_MODE_OFF', this.props.onTurboModeOff); - this.props.vm.on('PROJECT_RUN_START', this.props.onProjectRunStart); - this.props.vm.on('PROJECT_RUN_STOP', this.props.onProjectRunStop); - this.props.vm.on('PROJECT_CHANGED', this.handleProjectChanged); - this.props.vm.on('RUNTIME_STARTED', this.props.onRuntimeStarted); - this.props.vm.on('PROJECT_START', this.props.onGreenFlag); - this.props.vm.on('PERIPHERAL_CONNECTION_LOST_ERROR', this.props.onShowExtensionAlert); - this.props.vm.on('MIC_LISTENING', this.props.onMicListeningUpdate); - - } - componentDidMount () { - if (this.props.attachKeyboardEvents) { - document.addEventListener('keydown', this.handleKeyDown); - document.addEventListener('keyup', this.handleKeyUp); - } - this.props.vm.postIOData('userData', {username: this.props.username}); - } - componentDidUpdate (prevProps) { - if (prevProps.username !== this.props.username) { - this.props.vm.postIOData('userData', {username: this.props.username}); - } - - // Re-request a targets update when the shouldUpdateTargets state changes to true - // i.e. when the editor transitions out of fullscreen/player only modes - if (this.props.shouldUpdateTargets && !prevProps.shouldUpdateTargets) { - this.props.vm.emitTargetsUpdate(false /* Emit the event, but do not trigger project change */); - } - } - componentWillUnmount () { - this.props.vm.removeListener('PERIPHERAL_CONNECTION_LOST_ERROR', this.props.onShowExtensionAlert); - if (this.props.attachKeyboardEvents) { - document.removeEventListener('keydown', this.handleKeyDown); - document.removeEventListener('keyup', this.handleKeyUp); - } - } - handleProjectChanged () { - if (this.props.shouldUpdateProjectChanged && !this.props.projectChanged) { - this.props.onProjectChanged(); - } - } - handleTargetsUpdate (data) { - if (this.props.shouldUpdateTargets) { - this.props.onTargetsUpdate(data); - } - } - handleKeyDown (e) { - // Don't capture keys intended for Blockly inputs. - if (e.target !== document && e.target !== document.body) return; - - const key = (!e.key || e.key === 'Dead') ? e.keyCode : e.key; - this.props.vm.postIOData('keyboard', { - key: key, - isDown: true - }); - - // Prevent space/arrow key from scrolling the page. - if (e.keyCode === 32 || // 32=space - (e.keyCode >= 37 && e.keyCode <= 40)) { // 37, 38, 39, 40 are arrows - e.preventDefault(); - } - } - handleKeyUp (e) { - // Always capture up events, - // even those that have switched to other targets. - const key = (!e.key || e.key === 'Dead') ? e.keyCode : e.key; - this.props.vm.postIOData('keyboard', { - key: key, - isDown: false - }); - - // E.g., prevent scroll. - if (e.target !== document && e.target !== document.body) { - e.preventDefault(); - } - } - render () { - const { - /* eslint-disable no-unused-vars */ - attachKeyboardEvents, - projectChanged, - shouldUpdateTargets, - shouldUpdateProjectChanged, - onBlockDragUpdate, - onGreenFlag, - onKeyDown, - onKeyUp, - onMicListeningUpdate, - onMonitorsUpdate, - onTargetsUpdate, - onProjectChanged, - onProjectRunStart, - onProjectRunStop, - onProjectSaved, - onRuntimeStarted, - onTurboModeOff, - onTurboModeOn, - onShowExtensionAlert, - /* eslint-enable no-unused-vars */ - ...props - } = this.props; - return ; - } - } - VMListener.propTypes = { - attachKeyboardEvents: PropTypes.bool, - onBlockDragUpdate: PropTypes.func.isRequired, - onGreenFlag: PropTypes.func, - onKeyDown: PropTypes.func, - onKeyUp: PropTypes.func, - onMicListeningUpdate: PropTypes.func.isRequired, - onMonitorsUpdate: PropTypes.func.isRequired, - onProjectChanged: PropTypes.func.isRequired, - onProjectRunStart: PropTypes.func.isRequired, - onProjectRunStop: PropTypes.func.isRequired, - onProjectSaved: PropTypes.func.isRequired, - onRuntimeStarted: PropTypes.func.isRequired, - onShowExtensionAlert: PropTypes.func.isRequired, - onTargetsUpdate: PropTypes.func.isRequired, - onTurboModeOff: PropTypes.func.isRequired, - onTurboModeOn: PropTypes.func.isRequired, - projectChanged: PropTypes.bool, - shouldUpdateTargets: PropTypes.bool, - shouldUpdateProjectChanged: PropTypes.bool, - username: PropTypes.string, - vm: PropTypes.instanceOf(VM).isRequired - }; - VMListener.defaultProps = { - attachKeyboardEvents: true, - onGreenFlag: () => ({}) - }; - const mapStateToProps = state => ({ - projectChanged: state.scratchGui.projectChanged, - // Do not emit target or project updates in fullscreen or player only mode - // or when recording sounds (it leads to garbled recordings on low-power machines) - shouldUpdateTargets: !state.scratchGui.mode.isFullScreen && !state.scratchGui.mode.isPlayerOnly && - !state.scratchGui.modals.soundRecorder, - // Do not update the projectChanged state in fullscreen or player only mode - shouldUpdateProjectChanged: !state.scratchGui.mode.isFullScreen && !state.scratchGui.mode.isPlayerOnly, - vm: state.scratchGui.vm, - username: state.session && state.session.session && state.session.session.user ? - state.session.session.user.username : '' - }); - const mapDispatchToProps = dispatch => ({ - onTargetsUpdate: data => { - dispatch(updateTargets(data.targetList, data.editingTarget)); - }, - onMonitorsUpdate: monitorList => { - dispatch(updateMonitors(monitorList)); - }, - onBlockDragUpdate: areBlocksOverGui => { - dispatch(updateBlockDrag(areBlocksOverGui)); - }, - onProjectRunStart: () => dispatch(setRunningState(true)), - onProjectRunStop: () => dispatch(setRunningState(false)), - onProjectChanged: () => dispatch(setProjectChanged()), - onProjectSaved: () => dispatch(setProjectUnchanged()), - onRuntimeStarted: () => dispatch(setStartedState(true)), - onTurboModeOn: () => dispatch(setTurboState(true)), - onTurboModeOff: () => dispatch(setTurboState(false)), - onShowExtensionAlert: data => { - dispatch(showExtensionAlert(data)); - }, - onMicListeningUpdate: listening => { - dispatch(updateMicIndicator(listening)); - } - }); - return connect( - mapStateToProps, - mapDispatchToProps - )(VMListener); -}; - -export default vmListenerHOC; diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js deleted file mode 100644 index b3f6a0aaff..0000000000 --- a/packages/scratch-render/src/RenderWebGL.js +++ /dev/null @@ -1,2123 +0,0 @@ -const EventEmitter = require('events'); - -const hull = require('hull.js'); -const twgl = require('twgl.js'); - -const BitmapSkin = require('./BitmapSkin'); -const Drawable = require('./Drawable'); -const Rectangle = require('./Rectangle'); -const PenSkin = require('./PenSkin'); -const RenderConstants = require('./RenderConstants'); -const ShaderManager = require('./ShaderManager'); -const SVGSkin = require('./SVGSkin'); -const TextBubbleSkin = require('./TextBubbleSkin'); -const EffectTransform = require('./EffectTransform'); -const log = require('./util/log'); - -const __isTouchingDrawablesPoint = twgl.v3.create(); -const __candidatesBounds = new Rectangle(); -const __fenceBounds = new Rectangle(); -const __touchingColor = new Uint8ClampedArray(4); -const __blendColor = new Uint8ClampedArray(4); - -// More pixels than this and we give up to the GPU and take the cost of readPixels -// Width * Height * Number of drawables at location -const __cpuTouchingColorPixelCount = 4e4; - -/** - * @callback RenderWebGL#idFilterFunc - * @param {int} drawableID The ID to filter. - * @return {bool} True if the ID passes the filter, otherwise false. - */ - -/** - * Maximum touch size for a picking check. - * @todo Figure out a reasonable max size. Maybe this should be configurable? - * @type {Array} - * @memberof RenderWebGL - */ -const MAX_TOUCH_SIZE = [3, 3]; - -/** - * Passed to the uniforms for mask in touching color - */ -const MASK_TOUCHING_COLOR_TOLERANCE = 2; - -/** - * Maximum number of pixels in either dimension of "extracted drawable" data - * @type {int} - */ -const MAX_EXTRACTED_DRAWABLE_DIMENSION = 2048; - -/** - * Determines if the mask color is "close enough" (only test the 6 top bits for - * each color). These bit masks are what scratch 2 used to use, so we do the same. - * @param {Uint8Array} a A color3b or color4b value. - * @param {Uint8Array} b A color3b or color4b value. - * @returns {boolean} If the colors match within the parameters. - */ -const maskMatches = (a, b) => ( - // has some non-alpha component to test against - a[3] > 0 && - (a[0] & 0b11111100) === (b[0] & 0b11111100) && - (a[1] & 0b11111100) === (b[1] & 0b11111100) && - (a[2] & 0b11111100) === (b[2] & 0b11111100) -); - -/** - * Determines if the given color is "close enough" (only test the 5 top bits for - * red and green, 4 bits for blue). These bit masks are what scratch 2 used to use, - * so we do the same. - * @param {Uint8Array} a A color3b or color4b value. - * @param {Uint8Array} b A color3b or color4b value / or a larger array when used with offsets - * @param {number} offset An offset into the `b` array, which lets you use a larger array to test - * multiple values at the same time. - * @returns {boolean} If the colors match within the parameters. - */ -const colorMatches = (a, b, offset) => ( - (a[0] & 0b11111000) === (b[offset + 0] & 0b11111000) && - (a[1] & 0b11111000) === (b[offset + 1] & 0b11111000) && - (a[2] & 0b11110000) === (b[offset + 2] & 0b11110000) -); - -/** - * Sprite Fencing - The number of pixels a sprite is required to leave remaining - * onscreen around the edge of the staging area. - * @type {number} - */ -const FENCE_WIDTH = 15; - - -class RenderWebGL extends EventEmitter { - /** - * Check if this environment appears to support this renderer before attempting to create an instance. - * Catching an exception from the constructor is also a valid way to test for (lack of) support. - * @param {canvas} [optCanvas] - An optional canvas to use for the test. Otherwise a temporary canvas will be used. - * @returns {boolean} - True if this environment appears to support this renderer, false otherwise. - */ - static isSupported (optCanvas) { - try { - // Create the context the same way that the constructor will: attributes may make the difference. - return !!RenderWebGL._getContext(optCanvas || document.createElement('canvas')); - } catch (e) { - return false; - } - } - - /** - * Ask TWGL to create a rendering context with the attributes used by this renderer. - * @param {canvas} canvas - attach the context to this canvas. - * @returns {WebGLRenderingContext} - a TWGL rendering context (backed by either WebGL 1.0 or 2.0). - * @private - */ - static _getContext (canvas) { - return twgl.getWebGLContext(canvas, {alpha: false, stencil: true, antialias: false}); - } - - /** - * Create a renderer for drawing Scratch sprites to a canvas using WebGL. - * Coordinates will default to Scratch 2.0 values if unspecified. - * The stage's "native" size will be calculated from the these coordinates. - * For example, the defaults result in a native size of 480x360. - * Queries such as "touching color?" will always execute at the native size. - * @see RenderWebGL#setStageSize - * @see RenderWebGL#resize - * @param {canvas} canvas The canvas to draw onto. - * @param {int} [xLeft=-240] The x-coordinate of the left edge. - * @param {int} [xRight=240] The x-coordinate of the right edge. - * @param {int} [yBottom=-180] The y-coordinate of the bottom edge. - * @param {int} [yTop=180] The y-coordinate of the top edge. - * @constructor - * @listens RenderWebGL#event:NativeSizeChanged - */ - constructor (canvas, xLeft, xRight, yBottom, yTop) { - super(); - - /** @type {WebGLRenderingContext} */ - const gl = this._gl = RenderWebGL._getContext(canvas); - if (!gl) { - throw new Error('Could not get WebGL context: this browser or environment may not support WebGL.'); - } - - /** @type {RenderWebGL.UseGpuModes} */ - this._useGpuMode = RenderWebGL.UseGpuModes.Automatic; - - /** @type {Drawable[]} */ - this._allDrawables = []; - - /** @type {Skin[]} */ - this._allSkins = []; - - /** @type {Array} */ - this._drawList = []; - - // A list of layer group names in the order they should appear - // from furthest back to furthest in front. - /** @type {Array} */ - this._groupOrdering = []; - - /** - * @typedef LayerGroup - * @property {int} groupIndex The relative position of this layer group in the group ordering - * @property {int} drawListOffset The absolute position of this layer group in the draw list - * This number gets updated as drawables get added to or deleted from the draw list. - */ - - // Map of group name to layer group - /** @type {Object.} */ - this._layerGroups = {}; - - /** @type {int} */ - this._nextDrawableId = RenderConstants.ID_NONE + 1; - - /** @type {int} */ - this._nextSkinId = RenderConstants.ID_NONE + 1; - - /** @type {module:twgl/m4.Mat4} */ - this._projection = twgl.m4.identity(); - - /** @type {ShaderManager} */ - this._shaderManager = new ShaderManager(gl); - - /** @type {HTMLCanvasElement} */ - this._tempCanvas = document.createElement('canvas'); - - /** @type {any} */ - this._regionId = null; - - /** @type {function} */ - this._exitRegion = null; - - /** @type {object} */ - this._backgroundDrawRegionId = { - enter: () => this._enterDrawBackground(), - exit: () => this._exitDrawBackground() - }; - - /** @type {Array.} */ - this._snapshotCallbacks = []; - - /** @type {Array} */ - // Don't set this directly-- use setBackgroundColor so it stays in sync with _backgroundColor3b - this._backgroundColor4f = [0, 0, 0, 1]; - - /** @type {Uint8ClampedArray} */ - // Don't set this directly-- use setBackgroundColor so it stays in sync with _backgroundColor4f - this._backgroundColor3b = new Uint8ClampedArray(3); - - this._createGeometry(); - - this.on(RenderConstants.Events.NativeSizeChanged, this.onNativeSizeChanged); - - this.setBackgroundColor(1, 1, 1); - this.setStageSize(xLeft || -240, xRight || 240, yBottom || -180, yTop || 180); - this.resize(this._nativeSize[0], this._nativeSize[1]); - - gl.disable(gl.DEPTH_TEST); - /** @todo disable when no partial transparency? */ - gl.enable(gl.BLEND); - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - } - - /** - * @returns {WebGLRenderingContext} the WebGL rendering context associated with this renderer. - */ - get gl () { - return this._gl; - } - - /** - * @returns {HTMLCanvasElement} the canvas of the WebGL rendering context associated with this renderer. - */ - get canvas () { - return this._gl && this._gl.canvas; - } - - /** - * Set the physical size of the stage in device-independent pixels. - * This will be multiplied by the device's pixel ratio on high-DPI displays. - * @param {int} pixelsWide The desired width in device-independent pixels. - * @param {int} pixelsTall The desired height in device-independent pixels. - */ - resize (pixelsWide, pixelsTall) { - const {canvas} = this._gl; - const pixelRatio = window.devicePixelRatio || 1; - const newWidth = pixelsWide * pixelRatio; - const newHeight = pixelsTall * pixelRatio; - - // Certain operations, such as moving the color picker, call `resize` once per frame, even though the canvas - // size doesn't change. To avoid unnecessary canvas updates, check that we *really* need to resize the canvas. - if (canvas.width !== newWidth || canvas.height !== newHeight) { - canvas.width = newWidth; - canvas.height = newHeight; - // Resizing the canvas causes it to be cleared, so redraw it. - this.draw(); - } - - } - - /** - * Set the background color for the stage. The stage will be cleared with this - * color each frame. - * @param {number} red The red component for the background. - * @param {number} green The green component for the background. - * @param {number} blue The blue component for the background. - */ - setBackgroundColor (red, green, blue) { - this._backgroundColor4f[0] = red; - this._backgroundColor4f[1] = green; - this._backgroundColor4f[2] = blue; - - this._backgroundColor3b[0] = red * 255; - this._backgroundColor3b[1] = green * 255; - this._backgroundColor3b[2] = blue * 255; - - } - - /** - * Tell the renderer to draw various debug information to the provided canvas - * during certain operations. - * @param {canvas} canvas The canvas to use for debug output. - */ - setDebugCanvas (canvas) { - this._debugCanvas = canvas; - } - - /** - * Control the use of the GPU or CPU paths in `isTouchingColor`. - * @param {RenderWebGL.UseGpuModes} useGpuMode - automatically decide, force CPU, or force GPU. - */ - setUseGpuMode (useGpuMode) { - this._useGpuMode = useGpuMode; - } - - /** - * Set logical size of the stage in Scratch units. - * @param {int} xLeft The left edge's x-coordinate. Scratch 2 uses -240. - * @param {int} xRight The right edge's x-coordinate. Scratch 2 uses 240. - * @param {int} yBottom The bottom edge's y-coordinate. Scratch 2 uses -180. - * @param {int} yTop The top edge's y-coordinate. Scratch 2 uses 180. - */ - setStageSize (xLeft, xRight, yBottom, yTop) { - this._xLeft = xLeft; - this._xRight = xRight; - this._yBottom = yBottom; - this._yTop = yTop; - - // swap yBottom & yTop to fit Scratch convention of +y=up - this._projection = twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1); - - this._setNativeSize(Math.abs(xRight - xLeft), Math.abs(yBottom - yTop)); - } - - /** - * @return {Array} the "native" size of the stage, which is used for pen, query renders, etc. - */ - getNativeSize () { - return [this._nativeSize[0], this._nativeSize[1]]; - } - - /** - * Set the "native" size of the stage, which is used for pen, query renders, etc. - * @param {int} width - the new width to set. - * @param {int} height - the new height to set. - * @private - * @fires RenderWebGL#event:NativeSizeChanged - */ - _setNativeSize (width, height) { - this._nativeSize = [width, height]; - this.emit(RenderConstants.Events.NativeSizeChanged, {newSize: this._nativeSize}); - } - - /** - * Create a new bitmap skin from a snapshot of the provided bitmap data. - * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin. - * @param {!int} [costumeResolution=1] - The resolution to use for this bitmap. - * @param {?Array} [rotationCenter] Optional: rotation center of the skin. If not supplied, the center of - * the skin will be used. - * @returns {!int} the ID for the new skin. - */ - createBitmapSkin (bitmapData, costumeResolution, rotationCenter) { - const skinId = this._nextSkinId++; - const newSkin = new BitmapSkin(skinId, this); - newSkin.setBitmap(bitmapData, costumeResolution, rotationCenter); - this._allSkins[skinId] = newSkin; - return skinId; - } - - /** - * Create a new SVG skin. - * @param {!string} svgData - new SVG to use. - * @param {?Array} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the - * skin will be used - * @returns {!int} the ID for the new skin. - */ - createSVGSkin (svgData, rotationCenter) { - const skinId = this._nextSkinId++; - const newSkin = new SVGSkin(skinId, this); - newSkin.setSVG(svgData, rotationCenter); - this._allSkins[skinId] = newSkin; - return skinId; - } - - /** - * Create a new PenSkin - a skin which implements a Scratch pen layer. - * @returns {!int} the ID for the new skin. - */ - createPenSkin () { - const skinId = this._nextSkinId++; - const newSkin = new PenSkin(skinId, this); - this._allSkins[skinId] = newSkin; - return skinId; - } - - /** - * Create a new SVG skin using the text bubble svg creator. The rotation center - * is always placed at the top left. - * @param {!string} type - either "say" or "think". - * @param {!string} text - the text for the bubble. - * @param {!boolean} pointsLeft - which side the bubble is pointing. - * @returns {!int} the ID for the new skin. - */ - createTextSkin (type, text, pointsLeft) { - const skinId = this._nextSkinId++; - const newSkin = new TextBubbleSkin(skinId, this); - newSkin.setTextBubble(type, text, pointsLeft); - this._allSkins[skinId] = newSkin; - return skinId; - } - - /** - * Update an existing SVG skin, or create an SVG skin if the previous skin was not SVG. - * @param {!int} skinId the ID for the skin to change. - * @param {!string} svgData - new SVG to use. - * @param {?Array} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the - * skin will be used - */ - updateSVGSkin (skinId, svgData, rotationCenter) { - if (this._allSkins[skinId] instanceof SVGSkin) { - this._allSkins[skinId].setSVG(svgData, rotationCenter); - return; - } - - const newSkin = new SVGSkin(skinId, this); - newSkin.setSVG(svgData, rotationCenter); - this._reskin(skinId, newSkin); - } - - /** - * Update an existing bitmap skin, or create a bitmap skin if the previous skin was not bitmap. - * @param {!int} skinId the ID for the skin to change. - * @param {!ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} imgData - new contents for this skin. - * @param {!number} bitmapResolution - the resolution scale for a bitmap costume. - * @param {?Array} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the - * skin will be used - */ - updateBitmapSkin (skinId, imgData, bitmapResolution, rotationCenter) { - if (this._allSkins[skinId] instanceof BitmapSkin) { - this._allSkins[skinId].setBitmap(imgData, bitmapResolution, rotationCenter); - return; - } - - const newSkin = new BitmapSkin(skinId, this); - newSkin.setBitmap(imgData, bitmapResolution, rotationCenter); - this._reskin(skinId, newSkin); - } - - _reskin (skinId, newSkin) { - const oldSkin = this._allSkins[skinId]; - this._allSkins[skinId] = newSkin; - - // Tell drawables to update - for (const drawable of this._allDrawables) { - if (drawable && drawable.skin === oldSkin) { - drawable.skin = newSkin; - } - } - oldSkin.dispose(); - } - - /** - * Update a skin using the text bubble svg creator. - * @param {!int} skinId the ID for the skin to change. - * @param {!string} type - either "say" or "think". - * @param {!string} text - the text for the bubble. - * @param {!boolean} pointsLeft - which side the bubble is pointing. - */ - updateTextSkin (skinId, type, text, pointsLeft) { - if (this._allSkins[skinId] instanceof TextBubbleSkin) { - this._allSkins[skinId].setTextBubble(type, text, pointsLeft); - return; - } - - const newSkin = new TextBubbleSkin(skinId, this); - newSkin.setTextBubble(type, text, pointsLeft); - this._reskin(skinId, newSkin); - } - - - /** - * Destroy an existing skin. Do not use the skin or its ID after calling this. - * @param {!int} skinId - The ID of the skin to destroy. - */ - destroySkin (skinId) { - const oldSkin = this._allSkins[skinId]; - oldSkin.dispose(); - delete this._allSkins[skinId]; - } - - /** - * Create a new Drawable and add it to the scene. - * @param {string} group Layer group to add the drawable to - * @returns {int} The ID of the new Drawable. - */ - createDrawable (group) { - if (!group || !Object.prototype.hasOwnProperty.call(this._layerGroups, group)) { - log.warn('Cannot create a drawable without a known layer group'); - return; - } - const drawableID = this._nextDrawableId++; - const drawable = new Drawable(drawableID); - this._allDrawables[drawableID] = drawable; - this._addToDrawList(drawableID, group); - - drawable.skin = null; - - return drawableID; - } - - /** - * Set the layer group ordering for the renderer. - * @param {Array} groupOrdering The ordered array of layer group - * names - */ - setLayerGroupOrdering (groupOrdering) { - this._groupOrdering = groupOrdering; - for (let i = 0; i < this._groupOrdering.length; i++) { - this._layerGroups[this._groupOrdering[i]] = { - groupIndex: i, - drawListOffset: 0 - }; - } - } - - _addToDrawList (drawableID, group) { - const currentLayerGroup = this._layerGroups[group]; - const currentGroupOrderingIndex = currentLayerGroup.groupIndex; - - const drawListOffset = this._endIndexForKnownLayerGroup(currentLayerGroup); - this._drawList.splice(drawListOffset, 0, drawableID); - - this._updateOffsets('add', currentGroupOrderingIndex); - } - - _updateOffsets (updateType, currentGroupOrderingIndex) { - for (let i = currentGroupOrderingIndex + 1; i < this._groupOrdering.length; i++) { - const laterGroupName = this._groupOrdering[i]; - if (updateType === 'add') { - this._layerGroups[laterGroupName].drawListOffset++; - } else if (updateType === 'delete'){ - this._layerGroups[laterGroupName].drawListOffset--; - } - } - } - - get _visibleDrawList () { - return this._drawList.filter(id => this._allDrawables[id]._visible); - } - - // Given a layer group, return the index where it ends (non-inclusive), - // e.g. the returned index does not have a drawable from this layer group in it) - _endIndexForKnownLayerGroup (layerGroup) { - const groupIndex = layerGroup.groupIndex; - if (groupIndex === this._groupOrdering.length - 1) { - return this._drawList.length; - } - return this._layerGroups[this._groupOrdering[groupIndex + 1]].drawListOffset; - } - - /** - * Destroy a Drawable, removing it from the scene. - * @param {int} drawableID The ID of the Drawable to remove. - * @param {string} group Group name that the drawable belongs to - */ - destroyDrawable (drawableID, group) { - if (!group || !Object.prototype.hasOwnProperty.call(this._layerGroups, group)) { - log.warn('Cannot destroy drawable without known layer group.'); - return; - } - const drawable = this._allDrawables[drawableID]; - drawable.dispose(); - delete this._allDrawables[drawableID]; - - const currentLayerGroup = this._layerGroups[group]; - const endIndex = this._endIndexForKnownLayerGroup(currentLayerGroup); - - let index = currentLayerGroup.drawListOffset; - while (index < endIndex) { - if (this._drawList[index] === drawableID) { - break; - } - index++; - } - if (index < endIndex) { - this._drawList.splice(index, 1); - this._updateOffsets('delete', currentLayerGroup.groupIndex); - } else { - log.warn('Could not destroy drawable that could not be found in layer group.'); - return; - } - } - - /** - * Returns the position of the given drawableID in the draw list. This is - * the absolute position irrespective of layer group. - * @param {number} drawableID The drawable ID to find. - * @return {number} The postion of the given drawable ID. - */ - getDrawableOrder (drawableID) { - return this._drawList.indexOf(drawableID); - } - - /** - * Set a drawable's order in the drawable list (effectively, z/layer). - * Can be used to move drawables to absolute positions in the list, - * or relative to their current positions. - * "go back N layers": setDrawableOrder(id, -N, true, 1); (assuming stage at 0). - * "go to back": setDrawableOrder(id, 1); (assuming stage at 0). - * "go to front": setDrawableOrder(id, Infinity); - * @param {int} drawableID ID of Drawable to reorder. - * @param {number} order New absolute order or relative order adjusment. - * @param {string=} group Name of layer group drawable belongs to. - * Reordering will not take place if drawable cannot be found within the bounds - * of the layer group. - * @param {boolean=} optIsRelative If set, `order` refers to a relative change. - * @param {number=} optMin If set, order constrained to be at least `optMin`. - * @return {?number} New order if changed, or null. - */ - setDrawableOrder (drawableID, order, group, optIsRelative, optMin) { - if (!group || !Object.prototype.hasOwnProperty.call(this._layerGroups, group)) { - log.warn('Cannot set the order of a drawable without a known layer group.'); - return; - } - - const currentLayerGroup = this._layerGroups[group]; - const startIndex = currentLayerGroup.drawListOffset; - const endIndex = this._endIndexForKnownLayerGroup(currentLayerGroup); - - let oldIndex = startIndex; - while (oldIndex < endIndex) { - if (this._drawList[oldIndex] === drawableID) { - break; - } - oldIndex++; - } - - if (oldIndex < endIndex) { - // Remove drawable from the list. - if (order === 0) { - return oldIndex; - } - - const _ = this._drawList.splice(oldIndex, 1)[0]; - // Determine new index. - let newIndex = order; - if (optIsRelative) { - newIndex += oldIndex; - } - - const possibleMin = (optMin || 0) + startIndex; - const min = (possibleMin >= startIndex && possibleMin < endIndex) ? possibleMin : startIndex; - newIndex = Math.max(newIndex, min); - - newIndex = Math.min(newIndex, endIndex); - - // Insert at new index. - this._drawList.splice(newIndex, 0, drawableID); - return newIndex; - } - - return null; - } - - /** - * Draw all current drawables and present the frame on the canvas. - */ - draw () { - this._doExitDrawRegion(); - - const gl = this._gl; - - twgl.bindFramebufferInfo(gl, null); - gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); - gl.clearColor(...this._backgroundColor4f); - gl.clear(gl.COLOR_BUFFER_BIT); - - this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, this._projection); - if (this._snapshotCallbacks.length > 0) { - const snapshot = gl.canvas.toDataURL(); - this._snapshotCallbacks.forEach(cb => cb(snapshot)); - this._snapshotCallbacks = []; - } - } - - /** - * Get the precise bounds for a Drawable. - * @param {int} drawableID ID of Drawable to get bounds for. - * @return {object} Bounds for a tight box around the Drawable. - */ - getBounds (drawableID) { - const drawable = this._allDrawables[drawableID]; - // Tell the Drawable about its updated convex hull, if necessary. - if (drawable.needsConvexHullPoints()) { - const points = this._getConvexHullPointsForDrawable(drawableID); - drawable.setConvexHullPoints(points); - } - const bounds = drawable.getFastBounds(); - // In debug mode, draw the bounds. - if (this._debugCanvas) { - const gl = this._gl; - this._debugCanvas.width = gl.canvas.width; - this._debugCanvas.height = gl.canvas.height; - const context = this._debugCanvas.getContext('2d'); - context.drawImage(gl.canvas, 0, 0); - context.strokeStyle = '#FF0000'; - const pr = window.devicePixelRatio; - context.strokeRect( - pr * (bounds.left + (this._nativeSize[0] / 2)), - pr * (-bounds.top + (this._nativeSize[1] / 2)), - pr * (bounds.right - bounds.left), - pr * (-bounds.bottom + bounds.top) - ); - } - return bounds; - } - - /** - * Get the precise bounds for a Drawable around the top slice. - * Used for positioning speech bubbles more closely to the sprite. - * @param {int} drawableID ID of Drawable to get bubble bounds for. - * @return {object} Bounds for a tight box around the Drawable top slice. - */ - getBoundsForBubble (drawableID) { - const drawable = this._allDrawables[drawableID]; - // Tell the Drawable about its updated convex hull, if necessary. - if (drawable.needsConvexHullPoints()) { - const points = this._getConvexHullPointsForDrawable(drawableID); - drawable.setConvexHullPoints(points); - } - const bounds = drawable.getBoundsForBubble(); - // In debug mode, draw the bounds. - if (this._debugCanvas) { - const gl = this._gl; - this._debugCanvas.width = gl.canvas.width; - this._debugCanvas.height = gl.canvas.height; - const context = this._debugCanvas.getContext('2d'); - context.drawImage(gl.canvas, 0, 0); - context.strokeStyle = '#FF0000'; - const pr = window.devicePixelRatio; - context.strokeRect( - pr * (bounds.left + (this._nativeSize[0] / 2)), - pr * (-bounds.top + (this._nativeSize[1] / 2)), - pr * (bounds.right - bounds.left), - pr * (-bounds.bottom + bounds.top) - ); - } - return bounds; - } - - /** - * Get the current skin (costume) size of a Drawable. - * @param {int} drawableID The ID of the Drawable to measure. - * @return {Array} Skin size, width and height. - */ - getCurrentSkinSize (drawableID) { - const drawable = this._allDrawables[drawableID]; - return this.getSkinSize(drawable.skin.id); - } - - /** - * Get the size of a skin by ID. - * @param {int} skinID The ID of the Skin to measure. - * @return {Array} Skin size, width and height. - */ - getSkinSize (skinID) { - const skin = this._allSkins[skinID]; - return skin.size; - } - - /** - * Get the rotation center of a skin by ID. - * @param {int} skinID The ID of the Skin - * @return {Array} The rotationCenterX and rotationCenterY - */ - getSkinRotationCenter (skinID) { - const skin = this._allSkins[skinID]; - return skin.calculateRotationCenter(); - } - - /** - * Check if a particular Drawable is touching a particular color. - * Unlike touching drawable, if the "tester" is invisble, we will still test. - * @param {int} drawableID The ID of the Drawable to check. - * @param {Array} color3b Test if the Drawable is touching this color. - * @param {Array} [mask3b] Optionally mask the check to this part of Drawable. - * @returns {boolean} True iff the Drawable is touching the color. - */ - isTouchingColor (drawableID, color3b, mask3b) { - const candidates = this._candidatesTouching(drawableID, this._visibleDrawList); - - let bounds; - if (colorMatches(color3b, this._backgroundColor3b, 0)) { - // If the color we're checking for is the background color, don't confine the check to - // candidate drawables' bounds--since the background spans the entire stage, we must check - // everything that lies inside the drawable. - bounds = this._touchingBounds(drawableID); - // e.g. empty costume, or off the stage - if (bounds === null) return false; - } else if (candidates.length === 0) { - // If not checking for the background color, we can return early if there are no candidate drawables. - return false; - } else { - bounds = this._candidatesBounds(candidates); - } - - const maxPixelsForCPU = this._getMaxPixelsForCPU(); - - const debugCanvasContext = this._debugCanvas && this._debugCanvas.getContext('2d'); - if (debugCanvasContext) { - this._debugCanvas.width = bounds.width; - this._debugCanvas.height = bounds.height; - } - - // if there are just too many pixels to CPU render efficiently, we need to let readPixels happen - if (bounds.width * bounds.height * (candidates.length + 1) >= maxPixelsForCPU) { - this._isTouchingColorGpuStart(drawableID, candidates.map(({id}) => id).reverse(), bounds, color3b, mask3b); - } - - const drawable = this._allDrawables[drawableID]; - const point = __isTouchingDrawablesPoint; - const color = __touchingColor; - const hasMask = Boolean(mask3b); - - drawable.updateCPURenderAttributes(); - - // Masked drawable ignores ghost effect - const effectMask = ~ShaderManager.EFFECT_INFO.ghost.mask; - - // Scratch Space - +y is top - for (let y = bounds.bottom; y <= bounds.top; y++) { - if (bounds.width * (y - bounds.bottom) * (candidates.length + 1) >= maxPixelsForCPU) { - return this._isTouchingColorGpuFin(bounds, color3b, y - bounds.bottom); - } - for (let x = bounds.left; x <= bounds.right; x++) { - point[1] = y; - point[0] = x; - // if we use a mask, check our sample color... - if (hasMask ? - maskMatches(Drawable.sampleColor4b(point, drawable, color, effectMask), mask3b) : - drawable.isTouching(point)) { - RenderWebGL.sampleColor3b(point, candidates, color); - if (debugCanvasContext) { - debugCanvasContext.fillStyle = `rgb(${color[0]},${color[1]},${color[2]})`; - debugCanvasContext.fillRect(x - bounds.left, bounds.bottom - y, 1, 1); - } - // ...and the target color is drawn at this pixel - if (colorMatches(color, color3b, 0)) { - return true; - } - } - } - } - return false; - } - - _getMaxPixelsForCPU () { - switch (this._useGpuMode) { - case RenderWebGL.UseGpuModes.ForceCPU: - return Infinity; - case RenderWebGL.UseGpuModes.ForceGPU: - return 0; - case RenderWebGL.UseGpuModes.Automatic: - default: - return __cpuTouchingColorPixelCount; - } - } - - _enterDrawBackground () { - const gl = this.gl; - const currentShader = this._shaderManager.getShader(ShaderManager.DRAW_MODE.background, 0); - gl.disable(gl.BLEND); - gl.useProgram(currentShader.program); - twgl.setBuffersAndAttributes(gl, currentShader, this._bufferInfo); - } - - _exitDrawBackground () { - const gl = this.gl; - gl.enable(gl.BLEND); - } - - _isTouchingColorGpuStart (drawableID, candidateIDs, bounds, color3b, mask3b) { - this._doExitDrawRegion(); - - const gl = this._gl; - twgl.bindFramebufferInfo(gl, this._queryBufferInfo); - - // Limit size of viewport to the bounds around the target Drawable, - // and create the projection matrix for the draw. - gl.viewport(0, 0, bounds.width, bounds.height); - const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); - - // Clear the query buffer to fully transparent. This will be the color of pixels that fail the stencil test. - gl.clearColor(0, 0, 0, 0); - gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); - - let extraUniforms; - if (mask3b) { - extraUniforms = { - u_colorMask: [mask3b[0] / 255, mask3b[1] / 255, mask3b[2] / 255], - u_colorMaskTolerance: MASK_TOUCHING_COLOR_TOLERANCE / 255 - }; - } - - try { - // Using the stencil buffer, mask out the drawing to either the drawable's alpha channel - // or pixels of the drawable which match the mask color, depending on whether a mask color is given. - // Masked-out pixels will not be checked. - gl.enable(gl.STENCIL_TEST); - gl.stencilFunc(gl.ALWAYS, 1, 1); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); - gl.colorMask(false, false, false, false); - this._drawThese( - [drawableID], - mask3b ? - ShaderManager.DRAW_MODE.colorMask : - ShaderManager.DRAW_MODE.silhouette, - projection, - { - extraUniforms, - ignoreVisibility: true, // Touching color ignores sprite visibility, - effectMask: ~ShaderManager.EFFECT_INFO.ghost.mask - }); - - gl.stencilFunc(gl.EQUAL, 1, 1); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - gl.colorMask(true, true, true, true); - - // Draw the background as a quad. Drawing a background with gl.clear will not mask to the stenciled area. - this.enterDrawRegion(this._backgroundDrawRegionId); - - const uniforms = { - u_backgroundColor: this._backgroundColor4f - }; - - const currentShader = this._shaderManager.getShader(ShaderManager.DRAW_MODE.background, 0); - twgl.setUniforms(currentShader, uniforms); - twgl.drawBufferInfo(gl, this._bufferInfo, gl.TRIANGLES); - - // Draw the candidate drawables on top of the background. - this._drawThese(candidateIDs, ShaderManager.DRAW_MODE.default, projection, - {idFilterFunc: testID => testID !== drawableID} - ); - } finally { - gl.colorMask(true, true, true, true); - gl.disable(gl.STENCIL_TEST); - this._doExitDrawRegion(); - } - } - - _isTouchingColorGpuFin (bounds, color3b, stop) { - const gl = this._gl; - const pixels = new Uint8Array(Math.floor(bounds.width * (bounds.height - stop) * 4)); - gl.readPixels(0, 0, bounds.width, (bounds.height - stop), gl.RGBA, gl.UNSIGNED_BYTE, pixels); - - if (this._debugCanvas) { - this._debugCanvas.width = bounds.width; - this._debugCanvas.height = bounds.height; - const context = this._debugCanvas.getContext('2d'); - const imageData = context.getImageData(0, 0, bounds.width, bounds.height - stop); - imageData.data.set(pixels); - context.putImageData(imageData, 0, 0); - } - - for (let pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { - // Transparent pixels are masked (either by the drawable's alpha channel or color mask). - if (pixels[pixelBase + 3] !== 0 && colorMatches(color3b, pixels, pixelBase)) { - return true; - } - } - - return false; - } - - /** - * Check if a particular Drawable is touching any in a set of Drawables. - * @param {int} drawableID The ID of the Drawable to check. - * @param {?Array} candidateIDs The Drawable IDs to check, otherwise all visible drawables in the renderer - * @returns {boolean} True if the Drawable is touching one of candidateIDs. - */ - isTouchingDrawables (drawableID, candidateIDs = this._drawList) { - const candidates = this._candidatesTouching(drawableID, - // even if passed an invisible drawable, we will NEVER touch it! - candidateIDs.filter(id => this._allDrawables[id]._visible)); - // if we are invisble we don't touch anything. - if (candidates.length === 0 || !this._allDrawables[drawableID]._visible) { - return false; - } - - // Get the union of all the candidates intersections. - const bounds = this._candidatesBounds(candidates); - - const drawable = this._allDrawables[drawableID]; - const point = __isTouchingDrawablesPoint; - - drawable.updateCPURenderAttributes(); - - // This is an EXTREMELY brute force collision detector, but it is - // still faster than asking the GPU to give us the pixels. - for (let x = bounds.left; x <= bounds.right; x++) { - // Scratch Space - +y is top - point[0] = x; - for (let y = bounds.bottom; y <= bounds.top; y++) { - point[1] = y; - if (drawable.isTouching(point)) { - for (let index = 0; index < candidates.length; index++) { - if (candidates[index].drawable.isTouching(point)) { - return true; - } - } - } - } - } - - return false; - } - - /** - * Convert a client based x/y position on the canvas to a Scratch 3 world space - * Rectangle. This creates recangles with a radius to cover selecting multiple - * scratch pixels with touch / small render areas. - * - * @param {int} centerX The client x coordinate of the picking location. - * @param {int} centerY The client y coordinate of the picking location. - * @param {int} [width] The client width of the touch event (optional). - * @param {int} [height] The client width of the touch event (optional). - * @returns {Rectangle} Scratch world space rectangle, iterate bottom <= top, - * left <= right. - */ - clientSpaceToScratchBounds (centerX, centerY, width = 1, height = 1) { - const gl = this._gl; - - const clientToScratchX = this._nativeSize[0] / gl.canvas.clientWidth; - const clientToScratchY = this._nativeSize[1] / gl.canvas.clientHeight; - - width *= clientToScratchX; - height *= clientToScratchY; - - width = Math.max(1, Math.min(Math.round(width), MAX_TOUCH_SIZE[0])); - height = Math.max(1, Math.min(Math.round(height), MAX_TOUCH_SIZE[1])); - const x = (centerX * clientToScratchX) - ((width - 1) / 2); - // + because scratch y is inverted - const y = (centerY * clientToScratchY) + ((height - 1) / 2); - - const xOfs = (width % 2) ? 0 : -0.5; - // y is offset +0.5 - const yOfs = (height % 2) ? 0 : -0.5; - - const bounds = new Rectangle(); - bounds.initFromBounds(Math.floor(this._xLeft + x + xOfs), Math.floor(this._xLeft + x + xOfs + width - 1), - Math.ceil(this._yTop - y + yOfs), Math.ceil(this._yTop - y + yOfs + height - 1)); - return bounds; - } - - /** - * Determine if the drawable is touching a client based x/y. Helper method for sensing - * touching mouse-pointer. Ignores visibility. - * - * @param {int} drawableID The ID of the drawable to check. - * @param {int} centerX The client x coordinate of the picking location. - * @param {int} centerY The client y coordinate of the picking location. - * @param {int} [touchWidth] The client width of the touch event (optional). - * @param {int} [touchHeight] The client height of the touch event (optional). - * @returns {boolean} If the drawable has any pixels that would draw in the touch area - */ - drawableTouching (drawableID, centerX, centerY, touchWidth, touchHeight) { - const drawable = this._allDrawables[drawableID]; - if (!drawable) { - return false; - } - const bounds = this.clientSpaceToScratchBounds(centerX, centerY, touchWidth, touchHeight); - const worldPos = twgl.v3.create(); - - drawable.updateCPURenderAttributes(); - - for (worldPos[1] = bounds.bottom; worldPos[1] <= bounds.top; worldPos[1]++) { - for (worldPos[0] = bounds.left; worldPos[0] <= bounds.right; worldPos[0]++) { - if (drawable.isTouching(worldPos)) { - return true; - } - } - } - return false; - } - - /** - * Detect which sprite, if any, is at the given location. - * This function will pick all drawables that are visible, unless specific - * candidate drawable IDs are provided. Used for determining what is clicked - * or dragged. Will not select hidden / ghosted sprites. - * - * @param {int} centerX The client x coordinate of the picking location. - * @param {int} centerY The client y coordinate of the picking location. - * @param {int} [touchWidth] The client width of the touch event (optional). - * @param {int} [touchHeight] The client height of the touch event (optional). - * @param {Array} [candidateIDs] The Drawable IDs to pick from, otherwise all visible drawables. - * @returns {int} The ID of the topmost Drawable under the picking location, or - * RenderConstants.ID_NONE if there is no Drawable at that location. - */ - pick (centerX, centerY, touchWidth, touchHeight, candidateIDs) { - const bounds = this.clientSpaceToScratchBounds(centerX, centerY, touchWidth, touchHeight); - if (bounds.left === -Infinity || bounds.bottom === -Infinity) { - return false; - } - - candidateIDs = (candidateIDs || this._drawList).filter(id => { - const drawable = this._allDrawables[id]; - // default pick list ignores visible and ghosted sprites. - if (drawable.getVisible() && drawable.getUniforms().u_ghost !== 0) { - const drawableBounds = drawable.getFastBounds(); - const inRange = bounds.intersects(drawableBounds); - if (!inRange) return false; - - drawable.updateCPURenderAttributes(); - return true; - } - return false; - }); - if (candidateIDs.length === 0) { - return false; - } - - const hits = []; - const worldPos = twgl.v3.create(0, 0, 0); - // Iterate over the scratch pixels and check if any candidate can be - // touched at that point. - for (worldPos[1] = bounds.bottom; worldPos[1] <= bounds.top; worldPos[1]++) { - for (worldPos[0] = bounds.left; worldPos[0] <= bounds.right; worldPos[0]++) { - - // Check candidates in the reverse order they would have been - // drawn. This will determine what candiate's silhouette pixel - // would have been drawn at the point. - for (let d = candidateIDs.length - 1; d >= 0; d--) { - const id = candidateIDs[d]; - const drawable = this._allDrawables[id]; - if (drawable.isTouching(worldPos)) { - hits[id] = (hits[id] || 0) + 1; - break; - } - } - } - } - - // Bias toward selecting anything over nothing - hits[RenderConstants.ID_NONE] = 0; - - let hit = RenderConstants.ID_NONE; - for (const hitID in hits) { - if (Object.prototype.hasOwnProperty.call(hits, hitID) && (hits[hitID] > hits[hit])) { - hit = hitID; - } - } - - return Number(hit); - } - - /** - * @typedef DrawableExtractionOld - * @property {Uint8Array} data Raw pixel data for the drawable - * @property {int} width Drawable bounding box width - * @property {int} height Drawable bounding box height - * @property {Array} scratchOffset [x, y] offset in Scratch coordinates - * from the drawable position to the client x, y coordinate - * @property {int} x The x coordinate relative to drawable bounding box - * @property {int} y The y coordinate relative to drawable bounding box - */ - - /** - * Return drawable pixel data and picking coordinates relative to the drawable bounds - * @param {int} drawableID The ID of the drawable to get pixel data for - * @param {int} x The client x coordinate of the picking location. - * @param {int} y The client y coordinate of the picking location. - * @return {?DrawableExtractionOld} Data about the picked drawable - * @deprecated Use {@link extractDrawableScreenSpace} instead. - */ - extractDrawable (drawableID, x, y) { - this._doExitDrawRegion(); - - const drawable = this._allDrawables[drawableID]; - if (!drawable) return null; - - // Convert client coordinates into absolute scratch units - const scratchX = this._nativeSize[0] * ((x / this._gl.canvas.clientWidth) - 0.5); - const scratchY = this._nativeSize[1] * ((y / this._gl.canvas.clientHeight) - 0.5); - - const gl = this._gl; - - const bounds = drawable.getFastBounds(); - bounds.snapToInt(); - - // Set a reasonable max limit width and height for the bufferInfo bounds - const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); - const clampedWidth = Math.min(2048, bounds.width, maxTextureSize); - const clampedHeight = Math.min(2048, bounds.height, maxTextureSize); - - // Make a new bufferInfo since this._queryBufferInfo is limited to 480x360 - const attachments = [ - {format: gl.RGBA}, - {format: gl.DEPTH_STENCIL} - ]; - const bufferInfo = twgl.createFramebufferInfo(gl, attachments, clampedWidth, clampedHeight); - - try { - // If the new bufferInfo is invalid, fall back to using the smaller _queryBufferInfo - twgl.bindFramebufferInfo(gl, bufferInfo); - if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) { - twgl.bindFramebufferInfo(gl, this._queryBufferInfo); - } - - // Translate to scratch units relative to the drawable - const pickX = scratchX - bounds.left; - const pickY = scratchY + bounds.top; - - // Limit size of viewport to the bounds around the target Drawable, - // and create the projection matrix for the draw. - gl.viewport(0, 0, bounds.width, bounds.height); - const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); - - gl.clearColor(0, 0, 0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - try { - gl.disable(gl.BLEND); - // ImageData objects store alpha un-premultiplied, so draw with the `straightAlpha` draw mode. - this._drawThese([drawableID], ShaderManager.DRAW_MODE.straightAlpha, projection, - {effectMask: ~ShaderManager.EFFECT_INFO.ghost.mask}); - } finally { - gl.enable(gl.BLEND); - } - - const data = new Uint8Array(Math.floor(bounds.width * bounds.height * 4)); - gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, data); - - if (this._debugCanvas) { - this._debugCanvas.width = bounds.width; - this._debugCanvas.height = bounds.height; - const ctx = this._debugCanvas.getContext('2d'); - const imageData = ctx.createImageData(bounds.width, bounds.height); - imageData.data.set(data); - ctx.putImageData(imageData, 0, 0); - ctx.beginPath(); - ctx.arc(pickX, pickY, 3, 0, 2 * Math.PI, false); - ctx.fillStyle = 'white'; - ctx.fill(); - ctx.lineWidth = 1; - ctx.strokeStyle = 'black'; - ctx.stroke(); - } - - return { - data: data, - width: bounds.width, - height: bounds.height, - scratchOffset: [ - -scratchX + drawable._position[0], - -scratchY - drawable._position[1] - ], - x: pickX, - y: pickY - }; - } finally { - gl.deleteFramebuffer(bufferInfo.framebuffer); - } - } - - /** - * @typedef DrawableExtraction - * @property {ImageData} data Raw pixel data for the drawable - * @property {number} x The x coordinate of the drawable's bounding box's top-left corner, in 'CSS pixels' - * @property {number} y The y coordinate of the drawable's bounding box's top-left corner, in 'CSS pixels' - * @property {number} width The drawable's bounding box width, in 'CSS pixels' - * @property {number} height The drawable's bounding box height, in 'CSS pixels' - */ - - /** - * Return a drawable's pixel data and bounds in screen space. - * @param {int} drawableID The ID of the drawable to get pixel data for - * @return {DrawableExtraction} Data about the picked drawable - */ - extractDrawableScreenSpace (drawableID) { - const drawable = this._allDrawables[drawableID]; - if (!drawable) throw new Error(`Could not extract drawable with ID ${drawableID}; it does not exist`); - - this._doExitDrawRegion(); - - const nativeCenterX = this._nativeSize[0] * 0.5; - const nativeCenterY = this._nativeSize[1] * 0.5; - - const scratchBounds = drawable.getFastBounds(); - - const canvas = this.canvas; - // Ratio of the screen-space scale of the stage's canvas to the "native size" of the stage - const scaleFactor = canvas.width / this._nativeSize[0]; - - // Bounds of the extracted drawable, in "canvas pixel space" - // (origin is 0, 0, destination is the canvas width, height). - const canvasSpaceBounds = new Rectangle(); - canvasSpaceBounds.initFromBounds( - (scratchBounds.left + nativeCenterX) * scaleFactor, - (scratchBounds.right + nativeCenterX) * scaleFactor, - // in "canvas space", +y is down, but Rectangle methods assume bottom < top, so swap them - (nativeCenterY - scratchBounds.top) * scaleFactor, - (nativeCenterY - scratchBounds.bottom) * scaleFactor - ); - canvasSpaceBounds.snapToInt(); - - // undo the transformation to transform the bounds, snapped to "canvas-pixel space", back to "Scratch space" - // We have to transform -> snap -> invert transform so that the "Scratch-space" bounds are snapped in - // "canvas-pixel space". - scratchBounds.initFromBounds( - (canvasSpaceBounds.left / scaleFactor) - nativeCenterX, - (canvasSpaceBounds.right / scaleFactor) - nativeCenterX, - nativeCenterY - (canvasSpaceBounds.top / scaleFactor), - nativeCenterY - (canvasSpaceBounds.bottom / scaleFactor) - ); - - const gl = this._gl; - - // Set a reasonable max limit width and height for the bufferInfo bounds - const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); - const clampedWidth = Math.min(MAX_EXTRACTED_DRAWABLE_DIMENSION, canvasSpaceBounds.width, maxTextureSize); - const clampedHeight = Math.min(MAX_EXTRACTED_DRAWABLE_DIMENSION, canvasSpaceBounds.height, maxTextureSize); - - // Make a new bufferInfo since this._queryBufferInfo is limited to 480x360 - const bufferInfo = twgl.createFramebufferInfo(gl, [{format: gl.RGBA}], clampedWidth, clampedHeight); - - try { - twgl.bindFramebufferInfo(gl, bufferInfo); - - // Limit size of viewport to the bounds around the target Drawable, - // and create the projection matrix for the draw. - gl.viewport(0, 0, clampedWidth, clampedHeight); - const projection = twgl.m4.ortho( - scratchBounds.left, - scratchBounds.right, - scratchBounds.top, - scratchBounds.bottom, - -1, 1 - ); - - gl.clearColor(0, 0, 0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - // Don't apply the ghost effect. TODO: is this an intentional design decision? - this._drawThese([drawableID], ShaderManager.DRAW_MODE.straightAlpha, projection, - {effectMask: ~ShaderManager.EFFECT_INFO.ghost.mask}); - - const data = new Uint8Array(Math.floor(clampedWidth * clampedHeight * 4)); - gl.readPixels(0, 0, clampedWidth, clampedHeight, gl.RGBA, gl.UNSIGNED_BYTE, data); - // readPixels can only read into a Uint8Array, but ImageData has to take a Uint8ClampedArray. - // We can share the same underlying buffer between them to avoid having to copy any data. - const imageData = new ImageData(new Uint8ClampedArray(data.buffer), clampedWidth, clampedHeight); - - // On high-DPI devices, the canvas' width (in canvas pixels) will be larger than its width in CSS pixels. - // We want to return the CSS-space bounds, - // so take into account the ratio between the canvas' pixel dimensions and its layout dimensions. - // This is usually the same as 1 / window.devicePixelRatio, but if e.g. you zoom your browser window without - // the canvas resizing, then it'll differ. - const ratio = canvas.getBoundingClientRect().width / canvas.width; - - return { - imageData, - x: canvasSpaceBounds.left * ratio, - y: canvasSpaceBounds.bottom * ratio, - width: canvasSpaceBounds.width * ratio, - height: canvasSpaceBounds.height * ratio - }; - } finally { - gl.deleteFramebuffer(bufferInfo.framebuffer); - } - } - - /** - * @typedef ColorExtraction - * @property {Uint8Array} data Raw pixel data for the drawable - * @property {int} width Drawable bounding box width - * @property {int} height Drawable bounding box height - * @property {object} color Color object with RGBA properties at picked location - */ - - /** - * Return drawable pixel data and color at a given position - * @param {int} x The client x coordinate of the picking location. - * @param {int} y The client y coordinate of the picking location. - * @param {int} radius The client radius to extract pixels with. - * @return {?ColorExtraction} Data about the picked color - */ - extractColor (x, y, radius) { - this._doExitDrawRegion(); - - const scratchX = Math.round(this._nativeSize[0] * ((x / this._gl.canvas.clientWidth) - 0.5)); - const scratchY = Math.round(-this._nativeSize[1] * ((y / this._gl.canvas.clientHeight) - 0.5)); - - const gl = this._gl; - twgl.bindFramebufferInfo(gl, this._queryBufferInfo); - - const bounds = new Rectangle(); - bounds.initFromBounds(scratchX - radius, scratchX + radius, scratchY - radius, scratchY + radius); - - const pickX = scratchX - bounds.left; - const pickY = bounds.top - scratchY; - - gl.viewport(0, 0, bounds.width, bounds.height); - const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); - - gl.clearColor(...this._backgroundColor4f); - gl.clear(gl.COLOR_BUFFER_BIT); - this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, projection); - - const data = new Uint8Array(Math.floor(bounds.width * bounds.height * 4)); - gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, data); - - const pixelBase = Math.floor(4 * ((pickY * bounds.width) + pickX)); - const color = { - r: data[pixelBase], - g: data[pixelBase + 1], - b: data[pixelBase + 2], - a: data[pixelBase + 3] - }; - - if (this._debugCanvas) { - this._debugCanvas.width = bounds.width; - this._debugCanvas.height = bounds.height; - const ctx = this._debugCanvas.getContext('2d'); - const imageData = ctx.createImageData(bounds.width, bounds.height); - imageData.data.set(data); - ctx.putImageData(imageData, 0, 0); - ctx.strokeStyle = 'black'; - ctx.fillStyle = `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`; - ctx.rect(pickX - 4, pickY - 4, 8, 8); - ctx.fill(); - ctx.stroke(); - } - - return { - data: data, - width: bounds.width, - height: bounds.height, - color: color - }; - } - - /** - * Get the candidate bounding box for a touching query. - * @param {int} drawableID ID for drawable of query. - * @return {?Rectangle} Rectangle bounds for touching query, or null. - */ - _touchingBounds (drawableID) { - const drawable = this._allDrawables[drawableID]; - - /** @todo remove this once URL-based skin setting is removed. */ - if (!drawable.skin || !drawable.skin.getTexture([100, 100])) return null; - - const bounds = drawable.getFastBounds(); - - // Limit queries to the stage size. - bounds.clamp(this._xLeft, this._xRight, this._yBottom, this._yTop); - - // Use integer coordinates for queries - weird things happen - // when you provide float width/heights to gl.viewport and projection. - bounds.snapToInt(); - - if (bounds.width === 0 || bounds.height === 0) { - // No space to query. - return null; - } - return bounds; - } - - /** - * Filter a list of candidates for a touching query into only those that - * could possibly intersect the given bounds. - * @param {int} drawableID - ID for drawable of query. - * @param {Array} candidateIDs - Candidates for touching query. - * @return {?Array< {id, drawable, intersection} >} Filtered candidates with useful data. - */ - _candidatesTouching (drawableID, candidateIDs) { - const bounds = this._touchingBounds(drawableID); - const result = []; - if (bounds === null) { - return result; - } - // iterate through the drawables list BACKWARDS - we want the top most item to be the first we check - for (let index = candidateIDs.length - 1; index >= 0; index--) { - const id = candidateIDs[index]; - if (id !== drawableID) { - const drawable = this._allDrawables[id]; - // Text bubbles aren't considered in "touching" queries - if (drawable.skin instanceof TextBubbleSkin) continue; - if (drawable.skin && drawable._visible) { - // Update the CPU position data - drawable.updateCPURenderAttributes(); - const candidateBounds = drawable.getFastBounds(); - - // Push bounds out to integers. If a drawable extends out into half a pixel, that half-pixel still - // needs to be tested. Plus, in some areas we construct another rectangle from the union of these, - // and iterate over its pixels (width * height). Turns out that doesn't work so well when the - // width/height aren't integers. - candidateBounds.snapToInt(); - - if (bounds.intersects(candidateBounds)) { - result.push({ - id, - drawable, - intersection: Rectangle.intersect(bounds, candidateBounds) - }); - } - } - } - } - return result; - } - - /** - * Helper to get the union bounds from a set of candidates returned from the above method - * @private - * @param {Array} candidates info from _candidatesTouching - * @return {Rectangle} the outer bounding box union - */ - _candidatesBounds (candidates) { - return candidates.reduce((memo, {intersection}) => { - if (!memo) { - return intersection; - } - // store the union of the two rectangles in our static rectangle instance - return Rectangle.union(memo, intersection, __candidatesBounds); - }, null); - } - - /** - * Update a drawable's skin. - * @param {number} drawableID The drawable's id. - * @param {number} skinId The skin to update to. - */ - updateDrawableSkinId (drawableID, skinId) { - const drawable = this._allDrawables[drawableID]; - // TODO: https://github.com/LLK/scratch-vm/issues/2288 - if (!drawable) return; - drawable.skin = this._allSkins[skinId]; - } - - /** - * Update a drawable's position. - * @param {number} drawableID The drawable's id. - * @param {Array.} position The new position. - */ - updateDrawablePosition (drawableID, position) { - const drawable = this._allDrawables[drawableID]; - // TODO: https://github.com/LLK/scratch-vm/issues/2288 - if (!drawable) return; - drawable.updatePosition(position); - } - - /** - * Update a drawable's direction. - * @param {number} drawableID The drawable's id. - * @param {number} direction A new direction. - */ - updateDrawableDirection (drawableID, direction) { - const drawable = this._allDrawables[drawableID]; - // TODO: https://github.com/LLK/scratch-vm/issues/2288 - if (!drawable) return; - drawable.updateDirection(direction); - } - - /** - * Update a drawable's scale. - * @param {number} drawableID The drawable's id. - * @param {Array.} scale A new scale. - */ - updateDrawableScale (drawableID, scale) { - const drawable = this._allDrawables[drawableID]; - // TODO: https://github.com/LLK/scratch-vm/issues/2288 - if (!drawable) return; - drawable.updateScale(scale); - } - - /** - * Update a drawable's direction and scale together. - * @param {number} drawableID The drawable's id. - * @param {number} direction A new direction. - * @param {Array.} scale A new scale. - */ - updateDrawableDirectionScale (drawableID, direction, scale) { - const drawable = this._allDrawables[drawableID]; - // TODO: https://github.com/LLK/scratch-vm/issues/2288 - if (!drawable) return; - drawable.updateDirection(direction); - drawable.updateScale(scale); - } - - /** - * Update a drawable's visibility. - * @param {number} drawableID The drawable's id. - * @param {boolean} visible Will the drawable be visible? - */ - updateDrawableVisible (drawableID, visible) { - const drawable = this._allDrawables[drawableID]; - // TODO: https://github.com/LLK/scratch-vm/issues/2288 - if (!drawable) return; - drawable.updateVisible(visible); - } - - /** - * Update a drawable's visual effect. - * @param {number} drawableID The drawable's id. - * @param {string} effectName The effect to change. - * @param {number} value A new effect value. - */ - updateDrawableEffect (drawableID, effectName, value) { - const drawable = this._allDrawables[drawableID]; - // TODO: https://github.com/LLK/scratch-vm/issues/2288 - if (!drawable) return; - drawable.updateEffect(effectName, value); - } - - /** - * Update the position, direction, scale, or effect properties of this Drawable. - * @deprecated Use specific updateDrawable* methods instead. - * @param {int} drawableID The ID of the Drawable to update. - * @param {object.} properties The new property values to set. - */ - updateDrawableProperties (drawableID, properties) { - const drawable = this._allDrawables[drawableID]; - if (!drawable) { - /** - * @todo(https://github.com/LLK/scratch-vm/issues/2288) fix whatever's wrong in the VM which causes this, then add a warning or throw here. - * Right now this happens so much on some projects that a warning or exception here can hang the browser. - */ - return; - } - if ('skinId' in properties) { - this.updateDrawableSkinId(drawableID, properties.skinId); - } - drawable.updateProperties(properties); - } - - /** - * Update the position object's x & y members to keep the drawable fenced in view. - * @param {int} drawableID - The ID of the Drawable to update. - * @param {Array.} position to be fenced - An array of type [x, y] - * @return {Array.} The fenced position as an array [x, y] - */ - getFencedPositionOfDrawable (drawableID, position) { - let x = position[0]; - let y = position[1]; - - const drawable = this._allDrawables[drawableID]; - if (!drawable) { - // @todo(https://github.com/LLK/scratch-vm/issues/2288) fix whatever's wrong in the VM which causes this, then add a warning or throw here. - // Right now this happens so much on some projects that a warning or exception here can hang the browser. - return [x, y]; - } - - const dx = x - drawable._position[0]; - const dy = y - drawable._position[1]; - const aabb = drawable._skin.getFenceBounds(drawable, __fenceBounds); - const inset = Math.floor(Math.min(aabb.width, aabb.height) / 2); - - const sx = this._xRight - Math.min(FENCE_WIDTH, inset); - if (aabb.right + dx < -sx) { - x = Math.ceil(drawable._position[0] - (sx + aabb.right)); - } else if (aabb.left + dx > sx) { - x = Math.floor(drawable._position[0] + (sx - aabb.left)); - } - const sy = this._yTop - Math.min(FENCE_WIDTH, inset); - if (aabb.top + dy < -sy) { - y = Math.ceil(drawable._position[1] - (sy + aabb.top)); - } else if (aabb.bottom + dy > sy) { - y = Math.floor(drawable._position[1] + (sy - aabb.bottom)); - } - return [x, y]; - } - - /** - * Clear a pen layer. - * @param {int} penSkinID - the unique ID of a Pen Skin. - */ - penClear (penSkinID) { - const skin = /** @type {PenSkin} */ this._allSkins[penSkinID]; - skin.clear(); - } - - /** - * Draw a point on a pen layer. - * @param {int} penSkinID - the unique ID of a Pen Skin. - * @param {PenAttributes} penAttributes - how the point should be drawn. - * @param {number} x - the X coordinate of the point to draw. - * @param {number} y - the Y coordinate of the point to draw. - */ - penPoint (penSkinID, penAttributes, x, y) { - const skin = /** @type {PenSkin} */ this._allSkins[penSkinID]; - skin.drawPoint(penAttributes, x, y); - } - - /** - * Draw a line on a pen layer. - * @param {int} penSkinID - the unique ID of a Pen Skin. - * @param {PenAttributes} penAttributes - how the line should be drawn. - * @param {number} x0 - the X coordinate of the beginning of the line. - * @param {number} y0 - the Y coordinate of the beginning of the line. - * @param {number} x1 - the X coordinate of the end of the line. - * @param {number} y1 - the Y coordinate of the end of the line. - */ - penLine (penSkinID, penAttributes, x0, y0, x1, y1) { - const skin = /** @type {PenSkin} */ this._allSkins[penSkinID]; - skin.drawLine(penAttributes, x0, y0, x1, y1); - } - - /** - * Stamp a Drawable onto a pen layer. - * @param {int} penSkinID - the unique ID of a Pen Skin. - * @param {int} stampID - the unique ID of the Drawable to use as the stamp. - */ - penStamp (penSkinID, stampID) { - const stampDrawable = this._allDrawables[stampID]; - if (!stampDrawable) { - return; - } - - const bounds = this._touchingBounds(stampID); - if (!bounds) { - return; - } - - this._doExitDrawRegion(); - - const skin = /** @type {PenSkin} */ this._allSkins[penSkinID]; - - const gl = this._gl; - twgl.bindFramebufferInfo(gl, skin._framebuffer); - - // Limit size of viewport to the bounds around the stamp Drawable and create the projection matrix for the draw. - gl.viewport( - (this._nativeSize[0] * 0.5) + bounds.left, - (this._nativeSize[1] * 0.5) - bounds.top, - bounds.width, - bounds.height - ); - const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); - - // Draw the stamped sprite onto the PenSkin's framebuffer. - this._drawThese([stampID], ShaderManager.DRAW_MODE.default, projection, {ignoreVisibility: true}); - skin._silhouetteDirty = true; - } - - /* ****** - * Truly internal functions: these support the functions above. - ********/ - - /** - * Build geometry (vertex and index) buffers. - * @private - */ - _createGeometry () { - const quad = { - a_position: { - numComponents: 2, - data: [ - -0.5, -0.5, - 0.5, -0.5, - -0.5, 0.5, - -0.5, 0.5, - 0.5, -0.5, - 0.5, 0.5 - ] - }, - a_texCoord: { - numComponents: 2, - data: [ - 1, 0, - 0, 0, - 1, 1, - 1, 1, - 0, 0, - 0, 1 - ] - } - }; - this._bufferInfo = twgl.createBufferInfoFromArrays(this._gl, quad); - } - - /** - * Respond to a change in the "native" rendering size. The native size is used by buffers which are fixed in size - * regardless of the size of the main render target. This includes the buffers used for queries such as picking and - * color-touching. The fixed size allows (more) consistent behavior across devices and presentation modes. - * @param {object} event - The change event. - * @private - */ - onNativeSizeChanged (event) { - const [width, height] = event.newSize; - - const gl = this._gl; - const attachments = [ - {format: gl.RGBA}, - {format: gl.DEPTH_STENCIL} - ]; - - if (!this._pickBufferInfo) { - this._pickBufferInfo = twgl.createFramebufferInfo(gl, attachments, MAX_TOUCH_SIZE[0], MAX_TOUCH_SIZE[1]); - } - - /** @todo should we create this on demand to save memory? */ - // A 480x360 32-bpp buffer is 675 KiB. - if (this._queryBufferInfo) { - twgl.resizeFramebufferInfo(gl, this._queryBufferInfo, attachments, width, height); - } else { - this._queryBufferInfo = twgl.createFramebufferInfo(gl, attachments, width, height); - } - } - - /** - * Enter a draw region. - * - * A draw region is where multiple draw operations are performed with the - * same GL state. WebGL performs poorly when it changes state like blend - * mode. Marking a collection of state values as a "region" the renderer - * can skip superfluous extra state calls when it is already in that - * region. Since one region may be entered from within another a exit - * handle can also be registered that is called when a new region is about - * to be entered to restore a common inbetween state. - * - * @param {any} regionId - id of the region to enter - * @param {function} enter - handle to call when first entering a region - * @param {function} exit - handle to call when leaving a region - */ - enterDrawRegion (regionId, enter = regionId.enter, exit = regionId.exit) { - if (this._regionId !== regionId) { - this._doExitDrawRegion(); - this._regionId = regionId; - enter(); - this._exitRegion = exit; - } - } - - /** - * Forcefully exit the current region returning to a common inbetween GL - * state. - */ - _doExitDrawRegion () { - if (this._exitRegion !== null) { - this._exitRegion(); - } - this._exitRegion = null; - this._regionId = null; - } - - /** - * Get the screen-space scale of a drawable, as percentages of the drawable's "normal" size. - * @param {Drawable} drawable The drawable whose screen-space scale we're fetching. - * @returns {Array} The screen-space X and Y dimensions of the drawable's scale, as percentages. - */ - _getDrawableScreenSpaceScale (drawable) { - return [ - drawable.scale[0] * this._gl.canvas.width / this._nativeSize[0], - drawable.scale[1] * this._gl.canvas.height / this._nativeSize[1] - ]; - } - - /** - * Draw a set of Drawables, by drawable ID - * @param {Array} drawables The Drawable IDs to draw, possibly this._drawList. - * @param {ShaderManager.DRAW_MODE} drawMode Draw normally, silhouette, etc. - * @param {module:twgl/m4.Mat4} projection The projection matrix to use. - * @param {object} [opts] Options for drawing - * @param {idFilterFunc} opts.filter An optional filter function. - * @param {object.} opts.extraUniforms Extra uniforms for the shaders. - * @param {int} opts.effectMask Bitmask for effects to allow - * @param {boolean} opts.ignoreVisibility Draw all, despite visibility (e.g. stamping, touching color) - * @private - */ - _drawThese (drawables, drawMode, projection, opts = {}) { - - const gl = this._gl; - let currentShader = null; - - const numDrawables = drawables.length; - for (let drawableIndex = 0; drawableIndex < numDrawables; ++drawableIndex) { - const drawableID = drawables[drawableIndex]; - - // If we have a filter, check whether the ID fails - if (opts.filter && !opts.filter(drawableID)) continue; - - const drawable = this._allDrawables[drawableID]; - /** @todo check if drawable is inside the viewport before anything else */ - - // Hidden drawables (e.g., by a "hide" block) are not drawn unless - // the ignoreVisibility flag is used (e.g. for stamping or touchingColor). - if (!drawable.getVisible() && !opts.ignoreVisibility) continue; - - // Combine drawable scale with the native vs. backing pixel ratio - const drawableScale = this._getDrawableScreenSpaceScale(drawable); - - // If the skin or texture isn't ready yet, skip it. - if (!drawable.skin || !drawable.skin.getTexture(drawableScale)) continue; - - const uniforms = {}; - - let effectBits = drawable.enabledEffects; - effectBits &= Object.prototype.hasOwnProperty.call(opts, 'effectMask') ? opts.effectMask : effectBits; - const newShader = this._shaderManager.getShader(drawMode, effectBits); - - // Manually perform region check. Do not create functions inside a - // loop. - if (this._regionId !== newShader) { - this._doExitDrawRegion(); - this._regionId = newShader; - - currentShader = newShader; - gl.useProgram(currentShader.program); - twgl.setBuffersAndAttributes(gl, currentShader, this._bufferInfo); - Object.assign(uniforms, { - u_projectionMatrix: projection - }); - } - - Object.assign(uniforms, - drawable.skin.getUniforms(drawableScale), - drawable.getUniforms()); - - // Apply extra uniforms after the Drawable's, to allow overwriting. - if (opts.extraUniforms) { - Object.assign(uniforms, opts.extraUniforms); - } - - if (uniforms.u_skin) { - twgl.setTextureParameters( - gl, uniforms.u_skin, { - minMag: drawable.skin.useNearest(drawableScale, drawable) ? gl.NEAREST : gl.LINEAR - } - ); - } - - twgl.setUniforms(currentShader, uniforms); - twgl.drawBufferInfo(gl, this._bufferInfo, gl.TRIANGLES); - } - - this._regionId = null; - } - - /** - * Get the convex hull points for a particular Drawable. - * To do this, calculate it based on the drawable's Silhouette. - * @param {int} drawableID The Drawable IDs calculate convex hull for. - * @return {Array>} points Convex hull points, as [[x, y], ...] - */ - _getConvexHullPointsForDrawable (drawableID) { - const drawable = this._allDrawables[drawableID]; - - const [width, height] = drawable.skin.size; - // No points in the hull if invisible or size is 0. - if (!drawable.getVisible() || width === 0 || height === 0) { - return []; - } - - drawable.updateCPURenderAttributes(); - - /** - * Return the determinant of two vectors, the vector from A to B and the vector from A to C. - * - * The determinant is useful in this case to know if AC is counter-clockwise from AB. - * A positive value means that AC is counter-clockwise from AB. A negative value means AC is clockwise from AB. - * - * @param {Float32Array} A A 2d vector in space. - * @param {Float32Array} B A 2d vector in space. - * @param {Float32Array} C A 2d vector in space. - * @return {number} Greater than 0 if counter clockwise, less than if clockwise, 0 if all points are on a line. - */ - const determinant = function (A, B, C) { - // AB = B - A - // AC = C - A - // det (AB BC) = AB0 * AC1 - AB1 * AC0 - return (((B[0] - A[0]) * (C[1] - A[1])) - ((B[1] - A[1]) * (C[0] - A[0]))); - }; - - // This algorithm for calculating the convex hull somewhat resembles the monotone chain algorithm. - // The main difference is that instead of sorting the points by x-coordinate, and y-coordinate in case of ties, - // it goes through them by y-coordinate in the outer loop and x-coordinate in the inner loop. - // This gives us "left" and "right" hulls, whereas the monotone chain algorithm gives "top" and "bottom" hulls. - // Adapted from https://github.com/LLK/scratch-flash/blob/dcbeeb59d44c3be911545dfe54d46a32404f8e69/src/scratch/ScratchCostume.as#L369-L413 - - const leftHull = []; - const rightHull = []; - - // While convex hull algorithms usually push and pop values from the list of hull points, - // here, we keep indices for the "last" point in each array. Any points past these indices are ignored. - // This is functionally equivalent to pushing and popping from a "stack" of hull points. - let leftEndPointIndex = -1; - let rightEndPointIndex = -1; - - const _pixelPos = twgl.v3.create(); - const _effectPos = twgl.v3.create(); - - let currentPoint; - - // *Not* Scratch Space-- +y is bottom - // Loop over all rows of pixels, starting at the top - for (let y = 0; y < height; y++) { - _pixelPos[1] = y / height; - - // We start at the leftmost point, then go rightwards until we hit an opaque pixel - let x = 0; - for (; x < width; x++) { - _pixelPos[0] = x / width; - EffectTransform.transformPoint(drawable, _pixelPos, _effectPos); - if (drawable.skin.isTouchingLinear(_effectPos)) { - currentPoint = [x, y]; - break; - } - } - - // If we managed to loop all the way through, there are no opaque pixels on this row. Go to the next one - if (x >= width) { - continue; - } - - // Because leftEndPointIndex is initialized to -1, this is skipped for the first two rows. - // It runs only when there are enough points in the left hull to make at least one line. - // If appending the current point to the left hull makes a counter-clockwise turn, - // we want to append the current point. Otherwise, we decrement the index of the "last" hull point until the - // current point makes a counter-clockwise turn. - // This decrementing has the same effect as popping from the point list, but is hopefully faster. - while (leftEndPointIndex > 0) { - if (determinant(leftHull[leftEndPointIndex], leftHull[leftEndPointIndex - 1], currentPoint) > 0) { - break; - } else { - // leftHull.pop(); - --leftEndPointIndex; - } - } - - // This has the same effect as pushing to the point list. - // This "list head pointer" coding style leaves excess points dangling at the end of the list, - // but that doesn't matter; we simply won't copy them over to the final hull. - - // leftHull.push(currentPoint); - leftHull[++leftEndPointIndex] = currentPoint; - - // Now we repeat the process for the right side, looking leftwards for a pixel. - for (x = width - 1; x >= 0; x--) { - _pixelPos[0] = x / width; - EffectTransform.transformPoint(drawable, _pixelPos, _effectPos); - if (drawable.skin.isTouchingLinear(_effectPos)) { - currentPoint = [x, y]; - break; - } - } - - // Because we're coming at this from the right, it goes clockwise this time. - while (rightEndPointIndex > 0) { - if (determinant(rightHull[rightEndPointIndex], rightHull[rightEndPointIndex - 1], currentPoint) < 0) { - break; - } else { - --rightEndPointIndex; - } - } - - rightHull[++rightEndPointIndex] = currentPoint; - } - - // Start off "hullPoints" with the left hull points. - const hullPoints = leftHull; - // This is where we get rid of those dangling extra points. - hullPoints.length = leftEndPointIndex + 1; - // Add points from the right side in reverse order so all points are ordered clockwise. - for (let j = rightEndPointIndex; j >= 0; --j) { - hullPoints.push(rightHull[j]); - } - - // Simplify boundary points using hull.js. - // TODO: Remove this; this algorithm already generates convex hulls. - return hull(hullPoints, Infinity); - } - - /** - * Sample a "final" color from an array of drawables at a given scratch space. - * Will blend any alpha values with the drawables "below" it. - * @param {twgl.v3} vec Scratch Vector Space to sample - * @param {Array} drawables A list of drawables with the "top most" - * drawable at index 0 - * @param {Uint8ClampedArray} dst The color3b space to store the answer in. - * @return {Uint8ClampedArray} The dst vector with everything blended down. - */ - static sampleColor3b (vec, drawables, dst) { - dst = dst || new Uint8ClampedArray(3); - dst.fill(0); - let blendAlpha = 1; - for (let index = 0; blendAlpha !== 0 && index < drawables.length; index++) { - /* - if (left > vec[0] || right < vec[0] || - bottom > vec[1] || top < vec[0]) { - continue; - } - */ - Drawable.sampleColor4b(vec, drawables[index].drawable, __blendColor); - // Equivalent to gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - dst[0] += __blendColor[0] * blendAlpha; - dst[1] += __blendColor[1] * blendAlpha; - dst[2] += __blendColor[2] * blendAlpha; - blendAlpha *= (1 - (__blendColor[3] / 255)); - } - // Backdrop could be transparent, so we need to go to the "clear color" of the - // draw scene (white) as a fallback if everything was alpha - dst[0] += blendAlpha * 255; - dst[1] += blendAlpha * 255; - dst[2] += blendAlpha * 255; - return dst; - } - - /** - * @callback RenderWebGL#snapshotCallback - * @param {string} dataURI Data URI of the snapshot of the renderer - */ - - /** - * @param {snapshotCallback} callback Function called in the next frame with the snapshot data - */ - requestSnapshot (callback) { - this._snapshotCallbacks.push(callback); - } -} - -// :3 -RenderWebGL.prototype.canHazPixels = RenderWebGL.prototype.extractDrawable; - -/** - * Values for setUseGPU() - * @enum {string} - */ -RenderWebGL.UseGpuModes = { - /** - * Heuristically decide whether to use the GPU path, the CPU path, or a dynamic mixture of the two. - */ - Automatic: 'Automatic', - - /** - * Always use the GPU path. - */ - ForceGPU: 'ForceGPU', - - /** - * Always use the CPU path. - */ - ForceCPU: 'ForceCPU' -}; - -module.exports = RenderWebGL; diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js deleted file mode 100644 index bf1aeca888..0000000000 --- a/packages/scratch-render/src/SVGSkin.js +++ /dev/null @@ -1,201 +0,0 @@ -const twgl = require('twgl.js'); - -const Skin = require('./Skin'); -const SvgRenderer = require('scratch-svg-renderer').SVGRenderer; -const ShaderManager = require('./ShaderManager'); - -const MAX_TEXTURE_DIMENSION = 2048; - -/** - * All scaled renderings of the SVG are stored in an array. The 1.0 scale of - * the SVG is stored at the 8th index. The smallest possible 1 / 256 scale - * rendering is stored at the 0th index. - * @const {number} - */ -const INDEX_OFFSET = 8; - -class SVGSkin extends Skin { - /** - * Create a new SVG skin. - * @param {!int} id - The ID for this Skin. - * @param {!RenderWebGL} renderer - The renderer which will use this skin. - * @constructor - * @extends Skin - */ - constructor (id, renderer) { - super(id); - - /** @type {RenderWebGL} */ - this._renderer = renderer; - - /** @type {SvgRenderer} */ - this._svgRenderer = new SvgRenderer(); - - /** @type {Array} */ - this._scaledMIPs = []; - - /** @type {number} */ - this._largestMIPScale = 0; - - /** - * Ratio of the size of the SVG and the max size of the WebGL texture - * @type {Number} - */ - this._maxTextureScale = 1; - } - - /** - * Dispose of this object. Do not use it after calling this method. - */ - dispose () { - this.resetMIPs(); - super.dispose(); - } - - /** - * @return {Array} the natural size, in Scratch units, of this skin. - */ - get size () { - return this._svgRenderer.size; - } - - useNearest (scale, drawable) { - // If the effect bits for mosaic, pixelate, whirl, or fisheye are set, use linear - if ((drawable.enabledEffects & ( - ShaderManager.EFFECT_INFO.fisheye.mask | - ShaderManager.EFFECT_INFO.whirl.mask | - ShaderManager.EFFECT_INFO.pixelate.mask | - ShaderManager.EFFECT_INFO.mosaic.mask - )) !== 0) { - return false; - } - - // We can't use nearest neighbor unless we are a multiple of 90 rotation - if (drawable._direction % 90 !== 0) { - return false; - } - - // Because SVG skins' bounding boxes are currently not pixel-aligned, the idea here is to hide blurriness - // by using nearest-neighbor scaling if one screen-space pixel is "close enough" to one texture pixel. - // If the scale of the skin is very close to 100 (0.99999 variance is okay I guess) - // TODO: Make this check more precise. We should use nearest if there's less than one pixel's difference - // between the screen-space and texture-space sizes of the skin. Mipmaps make this harder because there are - // multiple textures (and hence multiple texture spaces) and we need to know which one to choose. - if (Math.abs(scale[0]) > 99 && Math.abs(scale[0]) < 101 && - Math.abs(scale[1]) > 99 && Math.abs(scale[1]) < 101) { - return true; - } - return false; - } - - /** - * Create a MIP for a given scale. - * @param {number} scale - The relative size of the MIP - * @return {SVGMIP} An object that handles creating and updating SVG textures. - */ - createMIP (scale) { - this._svgRenderer.draw(scale); - - // Pull out the ImageData from the canvas. ImageData speeds up - // updating Silhouette and is better handled by more browsers in - // regards to memory. - const canvas = this._svgRenderer.canvas; - // If one of the canvas dimensions is 0, set this MIP to an empty image texture. - // This avoids an IndexSizeError from attempting to getImageData when one of the dimensions is 0. - if (canvas.width === 0 || canvas.height === 0) return super.getTexture(); - - const context = canvas.getContext('2d'); - const textureData = context.getImageData(0, 0, canvas.width, canvas.height); - - const textureOptions = { - auto: false, - wrap: this._renderer.gl.CLAMP_TO_EDGE, - src: textureData, - premultiplyAlpha: true - }; - - const mip = twgl.createTexture(this._renderer.gl, textureOptions); - - // Check if this is the largest MIP created so far. Currently, silhouettes only get scaled up. - if (this._largestMIPScale < scale) { - this._silhouette.update(textureData); - this._largestMIPScale = scale; - } - - return mip; - } - - updateSilhouette (scale = [100, 100]) { - // Ensure a silhouette exists. - this.getTexture(scale); - } - - /** - * @param {Array} scale - The scaling factors to be used, each in the [0,100] range. - * @return {WebGLTexture} The GL texture representation of this skin when drawing at the given scale. - */ - getTexture (scale) { - // The texture only ever gets uniform scale. Take the larger of the two axes. - const scaleMax = scale ? Math.max(Math.abs(scale[0]), Math.abs(scale[1])) : 100; - const requestedScale = Math.min(scaleMax / 100, this._maxTextureScale); - - // Math.ceil(Math.log2(scale)) means we use the "1x" texture at (0.5, 1] scale, - // the "2x" texture at (1, 2] scale, the "4x" texture at (2, 4] scale, etc. - // This means that one texture pixel will always be between 0.5x and 1x the size of one rendered pixel, - // but never bigger than one rendered pixel--this prevents blurriness from blowing up the texture too much. - const mipLevel = Math.max(Math.ceil(Math.log2(requestedScale)) + INDEX_OFFSET, 0); - // Can't use bitwise stuff here because we need to handle negative exponents - const mipScale = Math.pow(2, mipLevel - INDEX_OFFSET); - - if (this._svgRenderer.loaded && !this._scaledMIPs[mipLevel]) { - this._scaledMIPs[mipLevel] = this.createMIP(mipScale); - } - - return this._scaledMIPs[mipLevel] || super.getTexture(); - } - - /** - * Do a hard reset of the existing MIPs by deleting them. - * @param {Array} [rotationCenter] - Optional rotation center for the SVG. If not supplied, it will be - * calculated from the bounding box - * @fires Skin.event:WasAltered - */ - resetMIPs () { - this._scaledMIPs.forEach(oldMIP => this._renderer.gl.deleteTexture(oldMIP)); - this._scaledMIPs.length = 0; - this._largestMIPScale = 0; - } - - /** - * Set the contents of this skin to a snapshot of the provided SVG data. - * @param {string} svgData - new SVG to use. - * @param {Array} [rotationCenter] - Optional rotation center for the SVG. - */ - setSVG (svgData, rotationCenter) { - this._svgRenderer.loadSVG(svgData, false, () => { - const svgSize = this._svgRenderer.size; - if (svgSize[0] === 0 || svgSize[1] === 0) { - super.setEmptyImageData(); - return; - } - - const maxDimension = Math.ceil(Math.max(this.size[0], this.size[1])); - let testScale = 2; - for (testScale; maxDimension * testScale <= MAX_TEXTURE_DIMENSION; testScale *= 2) { - this._maxTextureScale = testScale; - } - - this.resetMIPs(); - - if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); - const viewOffset = this._svgRenderer.viewOffset; - this._rotationCenter[0] = rotationCenter[0] - viewOffset[0]; - this._rotationCenter[1] = rotationCenter[1] - viewOffset[1]; - - this.emit(Skin.Events.WasAltered); - }); - } - -} - -module.exports = SVGSkin; diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js deleted file mode 100644 index 61f67feea3..0000000000 --- a/packages/scratch-vm/src/engine/runtime.js +++ /dev/null @@ -1,2618 +0,0 @@ -const EventEmitter = require('events'); -const {OrderedMap} = require('immutable'); - -const ArgumentType = require('../extension-support/argument-type'); -const Blocks = require('./blocks'); -const BlocksRuntimeCache = require('./blocks-runtime-cache'); -const BlockType = require('../extension-support/block-type'); -const Profiler = require('./profiler'); -const Sequencer = require('./sequencer'); -const execute = require('./execute.js'); -const ScratchBlocksConstants = require('./scratch-blocks-constants'); -const TargetType = require('../extension-support/target-type'); -const Thread = require('./thread'); -const log = require('../util/log'); -const maybeFormatMessage = require('../util/maybe-format-message'); -const StageLayering = require('./stage-layering'); -const Variable = require('./variable'); -const xmlEscape = require('../util/xml-escape'); -const ScratchLinkWebSocket = require('../util/scratch-link-websocket'); - -// Virtual I/O devices. -const Clock = require('../io/clock'); -const Cloud = require('../io/cloud'); -const Keyboard = require('../io/keyboard'); -const Mouse = require('../io/mouse'); -const MouseWheel = require('../io/mouseWheel'); -const UserData = require('../io/userData'); -const Video = require('../io/video'); - -const StringUtil = require('../util/string-util'); -const uid = require('../util/uid'); - -const defaultBlockPackages = { - scratch3_control: require('../blocks/scratch3_control'), - scratch3_event: require('../blocks/scratch3_event'), - scratch3_looks: require('../blocks/scratch3_looks'), - scratch3_motion: require('../blocks/scratch3_motion'), - scratch3_operators: require('../blocks/scratch3_operators'), - scratch3_sound: require('../blocks/scratch3_sound'), - scratch3_sensing: require('../blocks/scratch3_sensing'), - scratch3_data: require('../blocks/scratch3_data'), - scratch3_procedures: require('../blocks/scratch3_procedures') -}; - -const defaultExtensionColors = ['#0FBD8C', '#0DA57A', '#0B8E69']; - -/** - * Information used for converting Scratch argument types into scratch-blocks data. - * @type {object.} - */ -const ArgumentTypeMap = (() => { - const map = {}; - map[ArgumentType.ANGLE] = { - shadow: { - type: 'math_angle', - // We specify fieldNames here so that we can pick - // create and populate a field with the defaultValue - // specified in the extension. - // When the `fieldName` property is not specified, - // the will be left out of the XML and - // the scratch-blocks defaults for that field will be - // used instead (e.g. default of 0 for number fields) - fieldName: 'NUM' - } - }; - map[ArgumentType.COLOR] = { - shadow: { - type: 'colour_picker', - fieldName: 'COLOUR' - } - }; - map[ArgumentType.NUMBER] = { - shadow: { - type: 'math_number', - fieldName: 'NUM' - } - }; - map[ArgumentType.STRING] = { - shadow: { - type: 'text', - fieldName: 'TEXT' - } - }; - map[ArgumentType.BOOLEAN] = { - check: 'Boolean' - }; - map[ArgumentType.MATRIX] = { - shadow: { - type: 'matrix', - fieldName: 'MATRIX' - } - }; - map[ArgumentType.NOTE] = { - shadow: { - type: 'note', - fieldName: 'NOTE' - } - }; - map[ArgumentType.IMAGE] = { - // Inline images are weird because they're not actually "arguments". - // They are more analagous to the label on a block. - fieldType: 'field_image' - }; - return map; -})(); - -/** - * A pair of functions used to manage the cloud variable limit, - * to be used when adding (or attempting to add) or removing a cloud variable. - * @typedef {object} CloudDataManager - * @property {function} canAddCloudVariable A function to call to check that - * a cloud variable can be added. - * @property {function} addCloudVariable A function to call to track a new - * cloud variable on the runtime. - * @property {function} removeCloudVariable A function to call when - * removing an existing cloud variable. - * @property {function} hasCloudVariables A function to call to check that - * the runtime has any cloud variables. - */ - -/** - * Creates and manages cloud variable limit in a project, - * and returns two functions to be used to add a new - * cloud variable (while checking that it can be added) - * and remove an existing cloud variable. - * These are to be called whenever attempting to create or delete - * a cloud variable. - * @return {CloudDataManager} The functions to be used when adding or removing a - * cloud variable. - */ -const cloudDataManager = () => { - const limit = 10; - let count = 0; - - const canAddCloudVariable = () => count < limit; - - const addCloudVariable = () => { - count++; - }; - - const removeCloudVariable = () => { - count--; - }; - - const hasCloudVariables = () => count > 0; - - return { - canAddCloudVariable, - addCloudVariable, - removeCloudVariable, - hasCloudVariables - }; -}; - -/** - * Numeric ID for Runtime._step in Profiler instances. - * @type {number} - */ -let stepProfilerId = -1; - -/** - * Numeric ID for Sequencer.stepThreads in Profiler instances. - * @type {number} - */ -let stepThreadsProfilerId = -1; - -/** - * Numeric ID for RenderWebGL.draw in Profiler instances. - * @type {number} - */ -let rendererDrawProfilerId = -1; - -/** - * Manages targets, scripts, and the sequencer. - * @constructor - */ -class Runtime extends EventEmitter { - constructor () { - super(); - - /** - * Target management and storage. - * @type {Array.} - */ - this.targets = []; - - /** - * Targets in reverse order of execution. Shares its order with drawables. - * @type {Array.} - */ - this.executableTargets = []; - - /** - * A list of threads that are currently running in the VM. - * Threads are added when execution starts and pruned when execution ends. - * @type {Array.} - */ - this.threads = []; - - /** @type {!Sequencer} */ - this.sequencer = new Sequencer(this); - - /** - * Storage container for flyout blocks. - * These will execute on `_editingTarget.` - * @type {!Blocks} - */ - this.flyoutBlocks = new Blocks(this, true /* force no glow */); - - /** - * Storage container for monitor blocks. - * These will execute on a target maybe - * @type {!Blocks} - */ - this.monitorBlocks = new Blocks(this, true /* force no glow */); - - /** - * Currently known editing target for the VM. - * @type {?Target} - */ - this._editingTarget = null; - - /** - * Map to look up a block primitive's implementation function by its opcode. - * This is a two-step lookup: package name first, then primitive name. - * @type {Object.} - */ - this._primitives = {}; - - /** - * Map to look up all block information by extended opcode. - * @type {Array.} - * @private - */ - this._blockInfo = []; - - /** - * Map to look up hat blocks' metadata. - * Keys are opcode for hat, values are metadata objects. - * @type {Object.} - */ - this._hats = {}; - - /** - * A list of script block IDs that were glowing during the previous frame. - * @type {!Array.} - */ - this._scriptGlowsPreviousFrame = []; - - /** - * Number of non-monitor threads running during the previous frame. - * @type {number} - */ - this._nonMonitorThreadCount = 0; - - /** - * All threads that finished running and were removed from this.threads - * by behaviour in Sequencer.stepThreads. - * @type {Array} - */ - this._lastStepDoneThreads = null; - - /** - * Currently known number of clones, used to enforce clone limit. - * @type {number} - */ - this._cloneCounter = 0; - - /** - * Flag to emit a targets update at the end of a step. When target data - * changes, this flag is set to true. - * @type {boolean} - */ - this._refreshTargets = false; - - /** - * Map to look up all monitor block information by opcode. - * @type {object} - * @private - */ - this.monitorBlockInfo = {}; - - /** - * Ordered map of all monitors, which are MonitorReporter objects. - */ - this._monitorState = OrderedMap({}); - - /** - * Monitor state from last tick - */ - this._prevMonitorState = OrderedMap({}); - - /** - * Whether the project is in "turbo mode." - * @type {Boolean} - */ - this.turboMode = false; - - /** - * Whether the project is in "compatibility mode" (30 TPS). - * @type {Boolean} - */ - this.compatibilityMode = false; - - /** - * A reference to the current runtime stepping interval, set - * by a `setInterval`. - * @type {!number} - */ - this._steppingInterval = null; - - /** - * Current length of a step. - * Changes as mode switches, and used by the sequencer to calculate - * WORK_TIME. - * @type {!number} - */ - this.currentStepTime = null; - - // Set an intial value for this.currentMSecs - this.updateCurrentMSecs(); - - /** - * Whether any primitive has requested a redraw. - * Affects whether `Sequencer.stepThreads` will yield - * after stepping each thread. - * Reset on every frame. - * @type {boolean} - */ - this.redrawRequested = false; - - // Register all given block packages. - this._registerBlockPackages(); - - // Register and initialize "IO devices", containers for processing - // I/O related data. - /** @type {Object.} */ - this.ioDevices = { - clock: new Clock(this), - cloud: new Cloud(this), - keyboard: new Keyboard(this), - mouse: new Mouse(this), - mouseWheel: new MouseWheel(this), - userData: new UserData(), - video: new Video(this) - }; - - /** - * A list of extensions, used to manage hardware connection. - */ - this.peripheralExtensions = {}; - - /** - * A runtime profiler that records timed events for later playback to - * diagnose Scratch performance. - * @type {Profiler} - */ - this.profiler = null; - - const newCloudDataManager = cloudDataManager(); - - /** - * Check wether the runtime has any cloud data. - * @type {function} - * @return {boolean} Whether or not the runtime currently has any - * cloud variables. - */ - this.hasCloudData = newCloudDataManager.hasCloudVariables; - - /** - * A function which checks whether a new cloud variable can be added - * to the runtime. - * @type {function} - * @return {boolean} Whether or not a new cloud variable can be added - * to the runtime. - */ - this.canAddCloudVariable = newCloudDataManager.canAddCloudVariable; - - /** - * A function that tracks a new cloud variable in the runtime, - * updating the cloud variable limit. Calling this function will - * emit a cloud data update event if this is the first cloud variable - * being added. - * @type {function} - */ - this.addCloudVariable = this._initializeAddCloudVariable(newCloudDataManager); - - /** - * A function which updates the runtime's cloud variable limit - * when removing a cloud variable and emits a cloud update event - * if the last of the cloud variables is being removed. - * @type {function} - */ - this.removeCloudVariable = this._initializeRemoveCloudVariable(newCloudDataManager); - } - - /** - * Width of the stage, in pixels. - * @const {number} - */ - static get STAGE_WIDTH () { - return 480; - } - - /** - * Height of the stage, in pixels. - * @const {number} - */ - static get STAGE_HEIGHT () { - return 360; - } - - /** - * Event name for glowing a script. - * @const {string} - */ - static get SCRIPT_GLOW_ON () { - return 'SCRIPT_GLOW_ON'; - } - - /** - * Event name for unglowing a script. - * @const {string} - */ - static get SCRIPT_GLOW_OFF () { - return 'SCRIPT_GLOW_OFF'; - } - - /** - * Event name for glowing a block. - * @const {string} - */ - static get BLOCK_GLOW_ON () { - return 'BLOCK_GLOW_ON'; - } - - /** - * Event name for unglowing a block. - * @const {string} - */ - static get BLOCK_GLOW_OFF () { - return 'BLOCK_GLOW_OFF'; - } - - /** - * Event name for a cloud data update - * to this project. - * @const {string} - */ - static get HAS_CLOUD_DATA_UPDATE () { - return 'HAS_CLOUD_DATA_UPDATE'; - } - - /** - * Event name for turning on turbo mode. - * @const {string} - */ - static get TURBO_MODE_ON () { - return 'TURBO_MODE_ON'; - } - - /** - * Event name for turning off turbo mode. - * @const {string} - */ - static get TURBO_MODE_OFF () { - return 'TURBO_MODE_OFF'; - } - - /** - * Event name when the project is started (threads may not necessarily be - * running). - * @const {string} - */ - static get PROJECT_START () { - return 'PROJECT_START'; - } - - /** - * Event name when threads start running. - * Used by the UI to indicate running status. - * @const {string} - */ - static get PROJECT_RUN_START () { - return 'PROJECT_RUN_START'; - } - - /** - * Event name when threads stop running - * Used by the UI to indicate not-running status. - * @const {string} - */ - static get PROJECT_RUN_STOP () { - return 'PROJECT_RUN_STOP'; - } - - /** - * Event name for project being stopped or restarted by the user. - * Used by blocks that need to reset state. - * @const {string} - */ - static get PROJECT_STOP_ALL () { - return 'PROJECT_STOP_ALL'; - } - - /** - * Event name for target being stopped by a stop for target call. - * Used by blocks that need to stop individual targets. - * @const {string} - */ - static get STOP_FOR_TARGET () { - return 'STOP_FOR_TARGET'; - } - - /** - * Event name for visual value report. - * @const {string} - */ - static get VISUAL_REPORT () { - return 'VISUAL_REPORT'; - } - - /** - * Event name for project loaded report. - * @const {string} - */ - static get PROJECT_LOADED () { - return 'PROJECT_LOADED'; - } - - /** - * Event name for report that a change was made that can be saved - * @const {string} - */ - static get PROJECT_CHANGED () { - return 'PROJECT_CHANGED'; - } - - /** - * Event name for report that a change was made to an extension in the toolbox. - * @const {string} - */ - static get TOOLBOX_EXTENSIONS_NEED_UPDATE () { - return 'TOOLBOX_EXTENSIONS_NEED_UPDATE'; - } - - /** - * Event name for targets update report. - * @const {string} - */ - static get TARGETS_UPDATE () { - return 'TARGETS_UPDATE'; - } - - /** - * Event name for monitors update. - * @const {string} - */ - static get MONITORS_UPDATE () { - return 'MONITORS_UPDATE'; - } - - /** - * Event name for block drag update. - * @const {string} - */ - static get BLOCK_DRAG_UPDATE () { - return 'BLOCK_DRAG_UPDATE'; - } - - /** - * Event name for block drag end. - * @const {string} - */ - static get BLOCK_DRAG_END () { - return 'BLOCK_DRAG_END'; - } - - /** - * Event name for reporting that an extension was added. - * @const {string} - */ - static get EXTENSION_ADDED () { - return 'EXTENSION_ADDED'; - } - - /** - * Event name for reporting that an extension as asked for a custom field to be added - * @const {string} - */ - static get EXTENSION_FIELD_ADDED () { - return 'EXTENSION_FIELD_ADDED'; - } - - /** - * Event name for updating the available set of peripheral devices. - * This causes the peripheral connection modal to update a list of - * available peripherals. - * @const {string} - */ - static get PERIPHERAL_LIST_UPDATE () { - return 'PERIPHERAL_LIST_UPDATE'; - } - - /** - * Event name for when the user picks a bluetooth device to connect to - * via Companion Device Manager (CDM) - * @const {string} - */ - static get USER_PICKED_PERIPHERAL () { - return 'USER_PICKED_PERIPHERAL'; - } - - /** - * Event name for reporting that a peripheral has connected. - * This causes the status button in the blocks menu to indicate 'connected'. - * @const {string} - */ - static get PERIPHERAL_CONNECTED () { - return 'PERIPHERAL_CONNECTED'; - } - - /** - * Event name for reporting that a peripheral has been intentionally disconnected. - * This causes the status button in the blocks menu to indicate 'disconnected'. - * @const {string} - */ - static get PERIPHERAL_DISCONNECTED () { - return 'PERIPHERAL_DISCONNECTED'; - } - - /** - * Event name for reporting that a peripheral has encountered a request error. - * This causes the peripheral connection modal to switch to an error state. - * @const {string} - */ - static get PERIPHERAL_REQUEST_ERROR () { - return 'PERIPHERAL_REQUEST_ERROR'; - } - - /** - * Event name for reporting that a peripheral connection has been lost. - * This causes a 'peripheral connection lost' error alert to display. - * @const {string} - */ - static get PERIPHERAL_CONNECTION_LOST_ERROR () { - return 'PERIPHERAL_CONNECTION_LOST_ERROR'; - } - - /** - * Event name for reporting that a peripheral has not been discovered. - * This causes the peripheral connection modal to show a timeout state. - * @const {string} - */ - static get PERIPHERAL_SCAN_TIMEOUT () { - return 'PERIPHERAL_SCAN_TIMEOUT'; - } - - /** - * Event name to indicate that the microphone is being used to stream audio. - * @const {string} - */ - static get MIC_LISTENING () { - return 'MIC_LISTENING'; - } - - /** - * Event name for reporting that blocksInfo was updated. - * @const {string} - */ - static get BLOCKSINFO_UPDATE () { - return 'BLOCKSINFO_UPDATE'; - } - - /** - * Event name when the runtime tick loop has been started. - * @const {string} - */ - static get RUNTIME_STARTED () { - return 'RUNTIME_STARTED'; - } - - /** - * Event name when the runtime dispose has been called. - * @const {string} - */ - static get RUNTIME_DISPOSED () { - return 'RUNTIME_DISPOSED'; - } - - /** - * Event name for reporting that a block was updated and needs to be rerendered. - * @const {string} - */ - static get BLOCKS_NEED_UPDATE () { - return 'BLOCKS_NEED_UPDATE'; - } - - /** - * How rapidly we try to step threads by default, in ms. - */ - static get THREAD_STEP_INTERVAL () { - return 1000 / 60; - } - - /** - * In compatibility mode, how rapidly we try to step threads, in ms. - */ - static get THREAD_STEP_INTERVAL_COMPATIBILITY () { - return 1000 / 30; - } - - /** - * How many clones can be created at a time. - * @const {number} - */ - static get MAX_CLONES () { - return 300; - } - - // ----------------------------------------------------------------------------- - // ----------------------------------------------------------------------------- - - // Helper function for initializing the addCloudVariable function - _initializeAddCloudVariable (newCloudDataManager) { - // The addCloudVariable function - return (() => { - const hadCloudVarsBefore = this.hasCloudData(); - newCloudDataManager.addCloudVariable(); - if (!hadCloudVarsBefore && this.hasCloudData()) { - this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, true); - } - }); - } - - // Helper function for initializing the removeCloudVariable function - _initializeRemoveCloudVariable (newCloudDataManager) { - return (() => { - const hadCloudVarsBefore = this.hasCloudData(); - newCloudDataManager.removeCloudVariable(); - if (hadCloudVarsBefore && !this.hasCloudData()) { - this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, false); - } - }); - } - - /** - * Register default block packages with this runtime. - * @todo Prefix opcodes with package name. - * @private - */ - _registerBlockPackages () { - for (const packageName in defaultBlockPackages) { - if (defaultBlockPackages.hasOwnProperty(packageName)) { - // @todo pass a different runtime depending on package privilege? - const packageObject = new (defaultBlockPackages[packageName])(this); - // Collect primitives from package. - if (packageObject.getPrimitives) { - const packagePrimitives = packageObject.getPrimitives(); - for (const op in packagePrimitives) { - if (packagePrimitives.hasOwnProperty(op)) { - this._primitives[op] = - packagePrimitives[op].bind(packageObject); - } - } - } - // Collect hat metadata from package. - if (packageObject.getHats) { - const packageHats = packageObject.getHats(); - for (const hatName in packageHats) { - if (packageHats.hasOwnProperty(hatName)) { - this._hats[hatName] = packageHats[hatName]; - } - } - } - // Collect monitored from package. - if (packageObject.getMonitored) { - this.monitorBlockInfo = Object.assign({}, this.monitorBlockInfo, packageObject.getMonitored()); - } - } - } - } - - getMonitorState () { - return this._monitorState; - } - - /** - * Generate an extension-specific menu ID. - * @param {string} menuName - the name of the menu. - * @param {string} extensionId - the ID of the extension hosting the menu. - * @returns {string} - the constructed ID. - * @private - */ - _makeExtensionMenuId (menuName, extensionId) { - return `${extensionId}_menu_${xmlEscape(menuName)}`; - } - - /** - * Create a context ("args") object for use with `formatMessage` on messages which might be target-specific. - * @param {Target} [target] - the target to use as context. If a target is not provided, default to the current - * editing target or the stage. - */ - makeMessageContextForTarget (target) { - const context = {}; - target = target || this.getEditingTarget() || this.getTargetForStage(); - if (target) { - context.targetType = (target.isStage ? TargetType.STAGE : TargetType.SPRITE); - } - } - - /** - * Register the primitives provided by an extension. - * @param {ExtensionMetadata} extensionInfo - information about the extension (id, blocks, etc.) - * @private - */ - _registerExtensionPrimitives (extensionInfo) { - const categoryInfo = { - id: extensionInfo.id, - name: maybeFormatMessage(extensionInfo.name), - showStatusButton: extensionInfo.showStatusButton, - blockIconURI: extensionInfo.blockIconURI, - menuIconURI: extensionInfo.menuIconURI - }; - - if (extensionInfo.color1) { - categoryInfo.color1 = extensionInfo.color1; - categoryInfo.color2 = extensionInfo.color2; - categoryInfo.color3 = extensionInfo.color3; - } else { - categoryInfo.color1 = defaultExtensionColors[0]; - categoryInfo.color2 = defaultExtensionColors[1]; - categoryInfo.color3 = defaultExtensionColors[2]; - } - - this._blockInfo.push(categoryInfo); - - this._fillExtensionCategory(categoryInfo, extensionInfo); - - for (const fieldTypeName in categoryInfo.customFieldTypes) { - if (extensionInfo.customFieldTypes.hasOwnProperty(fieldTypeName)) { - const fieldTypeInfo = categoryInfo.customFieldTypes[fieldTypeName]; - - // Emit events for custom field types from extension - this.emit(Runtime.EXTENSION_FIELD_ADDED, { - name: `field_${fieldTypeInfo.extendedName}`, - implementation: fieldTypeInfo.fieldImplementation - }); - } - } - - this.emit(Runtime.EXTENSION_ADDED, categoryInfo); - } - - /** - * Reregister the primitives for an extension - * @param {ExtensionMetadata} extensionInfo - new info (results of running getInfo) for an extension - * @private - */ - _refreshExtensionPrimitives (extensionInfo) { - const categoryInfo = this._blockInfo.find(info => info.id === extensionInfo.id); - if (categoryInfo) { - categoryInfo.name = maybeFormatMessage(extensionInfo.name); - this._fillExtensionCategory(categoryInfo, extensionInfo); - - this.emit(Runtime.BLOCKSINFO_UPDATE, categoryInfo); - } - } - - /** - * Read extension information, convert menus, blocks and custom field types - * and store the results in the provided category object. - * @param {CategoryInfo} categoryInfo - the category to be filled - * @param {ExtensionMetadata} extensionInfo - the extension metadata to read - * @private - */ - _fillExtensionCategory (categoryInfo, extensionInfo) { - categoryInfo.blocks = []; - categoryInfo.customFieldTypes = {}; - categoryInfo.menus = []; - categoryInfo.menuInfo = {}; - - for (const menuName in extensionInfo.menus) { - if (extensionInfo.menus.hasOwnProperty(menuName)) { - const menuInfo = extensionInfo.menus[menuName]; - const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuInfo, categoryInfo); - categoryInfo.menus.push(convertedMenu); - categoryInfo.menuInfo[menuName] = menuInfo; - } - } - for (const fieldTypeName in extensionInfo.customFieldTypes) { - if (extensionInfo.customFieldTypes.hasOwnProperty(fieldTypeName)) { - const fieldType = extensionInfo.customFieldTypes[fieldTypeName]; - const fieldTypeInfo = this._buildCustomFieldInfo( - fieldTypeName, - fieldType, - extensionInfo.id, - categoryInfo - ); - - categoryInfo.customFieldTypes[fieldTypeName] = fieldTypeInfo; - } - } - - for (const blockInfo of extensionInfo.blocks) { - try { - const convertedBlock = this._convertForScratchBlocks(blockInfo, categoryInfo); - categoryInfo.blocks.push(convertedBlock); - if (convertedBlock.json) { - const opcode = convertedBlock.json.type; - if (blockInfo.blockType !== BlockType.EVENT) { - this._primitives[opcode] = convertedBlock.info.func; - } - if (blockInfo.blockType === BlockType.EVENT || blockInfo.blockType === BlockType.HAT) { - this._hats[opcode] = { - edgeActivated: blockInfo.isEdgeActivated, - restartExistingThreads: blockInfo.shouldRestartExistingThreads - }; - } - } - } catch (e) { - log.error('Error parsing block: ', {block: blockInfo, error: e}); - } - } - } - - /** - * Convert the given extension menu items into the scratch-blocks style of list of pairs. - * If the menu is dynamic (e.g. the passed in argument is a function), return the input unmodified. - * @param {object} menuItems - an array of menu items or a function to retrieve such an array - * @returns {object} - an array of 2 element arrays or the original input function - * @private - */ - _convertMenuItems (menuItems) { - if (typeof menuItems !== 'function') { - const extensionMessageContext = this.makeMessageContextForTarget(); - return menuItems.map(item => { - const formattedItem = maybeFormatMessage(item, extensionMessageContext); - switch (typeof formattedItem) { - case 'string': - return [formattedItem, formattedItem]; - case 'object': - return [maybeFormatMessage(item.text, extensionMessageContext), item.value]; - default: - throw new Error(`Can't interpret menu item: ${JSON.stringify(item)}`); - } - }); - } - return menuItems; - } - - /** - * Build the scratch-blocks JSON for a menu. Note that scratch-blocks treats menus as a special kind of block. - * @param {string} menuName - the name of the menu - * @param {object} menuInfo - a description of this menu and its items - * @property {*} items - an array of menu items or a function to retrieve such an array - * @property {boolean} [acceptReporters] - if true, allow dropping reporters onto this menu - * @param {CategoryInfo} categoryInfo - the category for this block - * @returns {object} - a JSON-esque object ready for scratch-blocks' consumption - * @private - */ - _buildMenuForScratchBlocks (menuName, menuInfo, categoryInfo) { - const menuId = this._makeExtensionMenuId(menuName, categoryInfo.id); - const menuItems = this._convertMenuItems(menuInfo.items); - return { - json: { - message0: '%1', - type: menuId, - inputsInline: true, - output: 'String', - colour: categoryInfo.color1, - colourSecondary: categoryInfo.color2, - colourTertiary: categoryInfo.color3, - outputShape: menuInfo.acceptReporters ? - ScratchBlocksConstants.OUTPUT_SHAPE_ROUND : ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE, - args0: [ - { - type: 'field_dropdown', - name: menuName, - options: menuItems - } - ] - } - }; - } - - _buildCustomFieldInfo (fieldName, fieldInfo, extensionId, categoryInfo) { - const extendedName = `${extensionId}_${fieldName}`; - return { - fieldName: fieldName, - extendedName: extendedName, - argumentTypeInfo: { - shadow: { - type: extendedName, - fieldName: `field_${extendedName}` - } - }, - scratchBlocksDefinition: this._buildCustomFieldTypeForScratchBlocks( - extendedName, - fieldInfo.output, - fieldInfo.outputShape, - categoryInfo - ), - fieldImplementation: fieldInfo.implementation - }; - } - - /** - * Build the scratch-blocks JSON needed for a fieldType. - * Custom field types need to be namespaced to the extension so that extensions can't interfere with each other - * @param {string} fieldName - The name of the field - * @param {string} output - The output of the field - * @param {number} outputShape - Shape of the field (from ScratchBlocksConstants) - * @param {object} categoryInfo - The category the field belongs to (Used to set its colors) - * @returns {object} - Object to be inserted into scratch-blocks - */ - _buildCustomFieldTypeForScratchBlocks (fieldName, output, outputShape, categoryInfo) { - return { - json: { - type: fieldName, - message0: '%1', - inputsInline: true, - output: output, - colour: categoryInfo.color1, - colourSecondary: categoryInfo.color2, - colourTertiary: categoryInfo.color3, - outputShape: outputShape, - args0: [ - { - name: `field_${fieldName}`, - type: `field_${fieldName}` - } - ] - } - }; - } - - /** - * Convert ExtensionBlockMetadata into data ready for scratch-blocks. - * @param {ExtensionBlockMetadata} blockInfo - the block info to convert - * @param {CategoryInfo} categoryInfo - the category for this block - * @returns {ConvertedBlockInfo} - the converted & original block information - * @private - */ - _convertForScratchBlocks (blockInfo, categoryInfo) { - if (blockInfo === '---') { - return this._convertSeparatorForScratchBlocks(blockInfo); - } - - if (blockInfo.blockType === BlockType.BUTTON) { - return this._convertButtonForScratchBlocks(blockInfo); - } - - return this._convertBlockForScratchBlocks(blockInfo, categoryInfo); - } - - /** - * Convert ExtensionBlockMetadata into scratch-blocks JSON & XML, and generate a proxy function. - * @param {ExtensionBlockMetadata} blockInfo - the block to convert - * @param {CategoryInfo} categoryInfo - the category for this block - * @returns {ConvertedBlockInfo} - the converted & original block information - * @private - */ - _convertBlockForScratchBlocks (blockInfo, categoryInfo) { - const extendedOpcode = `${categoryInfo.id}_${blockInfo.opcode}`; - - const blockJSON = { - type: extendedOpcode, - inputsInline: true, - category: categoryInfo.name, - colour: categoryInfo.color1, - colourSecondary: categoryInfo.color2, - colourTertiary: categoryInfo.color3 - }; - const context = { - // TODO: store this somewhere so that we can map args appropriately after translation. - // This maps an arg name to its relative position in the original (usually English) block text. - // When displaying a block in another language we'll need to run a `replace` action similar to the one - // below, but each `[ARG]` will need to be replaced with the number in this map. - argsMap: {}, - blockJSON, - categoryInfo, - blockInfo, - inputList: [] - }; - - // If an icon for the extension exists, prepend it to each block, with a vertical separator. - // We can overspecify an icon for each block, but if no icon exists on a block, fall back to - // the category block icon. - const iconURI = blockInfo.blockIconURI || categoryInfo.blockIconURI; - - if (iconURI) { - blockJSON.extensions = ['scratch_extension']; - blockJSON.message0 = '%1 %2'; - const iconJSON = { - type: 'field_image', - src: iconURI, - width: 40, - height: 40 - }; - const separatorJSON = { - type: 'field_vertical_separator' - }; - blockJSON.args0 = [ - iconJSON, - separatorJSON - ]; - } - - switch (blockInfo.blockType) { - case BlockType.COMMAND: - blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE; - blockJSON.previousStatement = null; // null = available connection; undefined = hat - if (!blockInfo.isTerminal) { - blockJSON.nextStatement = null; // null = available connection; undefined = terminal - } - break; - case BlockType.REPORTER: - blockJSON.output = 'String'; // TODO: distinguish number & string here? - blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_ROUND; - break; - case BlockType.BOOLEAN: - blockJSON.output = 'Boolean'; - blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_HEXAGONAL; - break; - case BlockType.HAT: - case BlockType.EVENT: - if (!blockInfo.hasOwnProperty('isEdgeActivated')) { - // if absent, this property defaults to true - blockInfo.isEdgeActivated = true; - } - blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE; - blockJSON.nextStatement = null; // null = available connection; undefined = terminal - break; - case BlockType.CONDITIONAL: - case BlockType.LOOP: - blockInfo.branchCount = blockInfo.branchCount || 1; - blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE; - blockJSON.previousStatement = null; // null = available connection; undefined = hat - if (!blockInfo.isTerminal) { - blockJSON.nextStatement = null; // null = available connection; undefined = terminal - } - break; - } - - const blockText = Array.isArray(blockInfo.text) ? blockInfo.text : [blockInfo.text]; - let inTextNum = 0; // text for the next block "arm" is blockText[inTextNum] - let inBranchNum = 0; // how many branches have we placed into the JSON so far? - let outLineNum = 0; // used for scratch-blocks `message${outLineNum}` and `args${outLineNum}` - const convertPlaceholders = this._convertPlaceholders.bind(this, context); - const extensionMessageContext = this.makeMessageContextForTarget(); - - // alternate between a block "arm" with text on it and an open slot for a substack - while (inTextNum < blockText.length || inBranchNum < blockInfo.branchCount) { - if (inTextNum < blockText.length) { - context.outLineNum = outLineNum; - const lineText = maybeFormatMessage(blockText[inTextNum], extensionMessageContext); - const convertedText = lineText.replace(/\[(.+?)]/g, convertPlaceholders); - if (blockJSON[`message${outLineNum}`]) { - blockJSON[`message${outLineNum}`] += convertedText; - } else { - blockJSON[`message${outLineNum}`] = convertedText; - } - ++inTextNum; - ++outLineNum; - } - if (inBranchNum < blockInfo.branchCount) { - blockJSON[`message${outLineNum}`] = '%1'; - blockJSON[`args${outLineNum}`] = [{ - type: 'input_statement', - name: `SUBSTACK${inBranchNum > 0 ? inBranchNum + 1 : ''}` - }]; - ++inBranchNum; - ++outLineNum; - } - } - - if (blockInfo.blockType === BlockType.REPORTER) { - if (!blockInfo.disableMonitor && context.inputList.length === 0) { - blockJSON.checkboxInFlyout = true; - } - } else if (blockInfo.blockType === BlockType.LOOP) { - // Add icon to the bottom right of a loop block - blockJSON[`lastDummyAlign${outLineNum}`] = 'RIGHT'; - blockJSON[`message${outLineNum}`] = '%1'; - blockJSON[`args${outLineNum}`] = [{ - type: 'field_image', - src: './static/blocks-media/repeat.svg', // TODO: use a constant or make this configurable? - width: 24, - height: 24, - alt: '*', // TODO remove this since we don't use collapsed blocks in scratch - flip_rtl: true - }]; - ++outLineNum; - } - - const mutation = blockInfo.isDynamic ? `` : ''; - const inputs = context.inputList.join(''); - const blockXML = `${mutation}${inputs}`; - - return { - info: context.blockInfo, - json: context.blockJSON, - xml: blockXML - }; - } - - /** - * Generate a separator between blocks categories or sub-categories. - * @param {ExtensionBlockMetadata} blockInfo - the block to convert - * @param {CategoryInfo} categoryInfo - the category for this block - * @returns {ConvertedBlockInfo} - the converted & original block information - * @private - */ - _convertSeparatorForScratchBlocks (blockInfo) { - return { - info: blockInfo, - xml: '' - }; - } - - /** - * Convert a button for scratch-blocks. A button has no opcode but specifies a callback name in the `func` field. - * @param {ExtensionBlockMetadata} buttonInfo - the button to convert - * @property {string} func - the callback name - * @param {CategoryInfo} categoryInfo - the category for this button - * @returns {ConvertedBlockInfo} - the converted & original button information - * @private - */ - _convertButtonForScratchBlocks (buttonInfo) { - // for now we only support these pre-defined callbacks handled in scratch-blocks - const supportedCallbackKeys = ['MAKE_A_LIST', 'MAKE_A_PROCEDURE', 'MAKE_A_VARIABLE']; - if (supportedCallbackKeys.indexOf(buttonInfo.func) < 0) { - log.error(`Custom button callbacks not supported yet: ${buttonInfo.func}`); - } - - const extensionMessageContext = this.makeMessageContextForTarget(); - const buttonText = maybeFormatMessage(buttonInfo.text, extensionMessageContext); - return { - info: buttonInfo, - xml: `` - }; - } - - /** - * Helper for _convertPlaceholdes which handles inline images which are a specialized case of block "arguments". - * @param {object} argInfo Metadata about the inline image as specified by the extension - * @return {object} JSON blob for a scratch-blocks image field. - * @private - */ - _constructInlineImageJson (argInfo) { - if (!argInfo.dataURI) { - log.warn('Missing data URI in extension block with argument type IMAGE'); - } - return { - type: 'field_image', - src: argInfo.dataURI || '', - // TODO these probably shouldn't be hardcoded...? - width: 24, - height: 24, - // Whether or not the inline image should be flipped horizontally - // in RTL languages. Defaults to false, indicating that the - // image will not be flipped. - flip_rtl: argInfo.flipRTL || false - }; - } - - /** - * Helper for _convertForScratchBlocks which handles linearization of argument placeholders. Called as a callback - * from string#replace. In addition to the return value the JSON and XML items in the context will be filled. - * @param {object} context - information shared with _convertForScratchBlocks about the block, etc. - * @param {string} match - the overall string matched by the placeholder regex, including brackets: '[FOO]'. - * @param {string} placeholder - the name of the placeholder being matched: 'FOO'. - * @return {string} scratch-blocks placeholder for the argument: '%1'. - * @private - */ - _convertPlaceholders (context, match, placeholder) { - // Sanitize the placeholder to ensure valid XML - placeholder = placeholder.replace(/[<"&]/, '_'); - - // Determine whether the argument type is one of the known standard field types - const argInfo = context.blockInfo.arguments[placeholder] || {}; - let argTypeInfo = ArgumentTypeMap[argInfo.type] || {}; - - // Field type not a standard field type, see if extension has registered custom field type - if (!ArgumentTypeMap[argInfo.type] && context.categoryInfo.customFieldTypes[argInfo.type]) { - argTypeInfo = context.categoryInfo.customFieldTypes[argInfo.type].argumentTypeInfo; - } - - // Start to construct the scratch-blocks style JSON defining how the block should be - // laid out - let argJSON; - - // Most field types are inputs (slots on the block that can have other blocks plugged into them) - // check if this is not one of those cases. E.g. an inline image on a block. - if (argTypeInfo.fieldType === 'field_image') { - argJSON = this._constructInlineImageJson(argInfo); - } else { - // Construct input value - - // Layout a block argument (e.g. an input slot on the block) - argJSON = { - type: 'input_value', - name: placeholder - }; - - const defaultValue = - typeof argInfo.defaultValue === 'undefined' ? '' : - xmlEscape(maybeFormatMessage(argInfo.defaultValue, this.makeMessageContextForTarget()).toString()); - - if (argTypeInfo.check) { - // Right now the only type of 'check' we have specifies that the - // input slot on the block accepts Boolean reporters, so it should be - // shaped like a hexagon - argJSON.check = argTypeInfo.check; - } - - let valueName; - let shadowType; - let fieldName; - if (argInfo.menu) { - const menuInfo = context.categoryInfo.menuInfo[argInfo.menu]; - if (menuInfo.acceptReporters) { - valueName = placeholder; - shadowType = this._makeExtensionMenuId(argInfo.menu, context.categoryInfo.id); - fieldName = argInfo.menu; - } else { - argJSON.type = 'field_dropdown'; - argJSON.options = this._convertMenuItems(menuInfo.items); - valueName = null; - shadowType = null; - fieldName = placeholder; - } - } else { - valueName = placeholder; - shadowType = (argTypeInfo.shadow && argTypeInfo.shadow.type) || null; - fieldName = (argTypeInfo.shadow && argTypeInfo.shadow.fieldName) || null; - } - - // is the ScratchBlocks name for a block input. - if (valueName) { - context.inputList.push(``); - } - - // The is a placeholder for a reporter and is visible when there's no reporter in this input. - // Boolean inputs don't need to specify a shadow in the XML. - if (shadowType) { - context.inputList.push(``); - } - - // A displays a dynamic value: a user-editable text field, a drop-down menu, etc. - // Leave out the field if defaultValue or fieldName are not specified - if (defaultValue && fieldName) { - context.inputList.push(`${defaultValue}`); - } - - if (shadowType) { - context.inputList.push(''); - } - - if (valueName) { - context.inputList.push(''); - } - } - - const argsName = `args${context.outLineNum}`; - const blockArgs = (context.blockJSON[argsName] = context.blockJSON[argsName] || []); - if (argJSON) blockArgs.push(argJSON); - const argNum = blockArgs.length; - context.argsMap[placeholder] = argNum; - - return `%${argNum}`; - } - - /** - * @returns {Array.} scratch-blocks XML for each category of extension blocks, in category order. - * @param {?Target} [target] - the active editing target (optional) - * @property {string} id - the category / extension ID - * @property {string} xml - the XML text for this category, starting with `` and ending with `` - */ - getBlocksXML (target) { - return this._blockInfo.map(categoryInfo => { - const {name, color1, color2} = categoryInfo; - // Filter out blocks that aren't supposed to be shown on this target, as determined by the block info's - // `hideFromPalette` and `filter` properties. - const paletteBlocks = categoryInfo.blocks.filter(block => { - let blockFilterIncludesTarget = true; - // If an editing target is not passed, include all blocks - // If the block info doesn't include a `filter` property, always include it - if (target && block.info.filter) { - blockFilterIncludesTarget = block.info.filter.includes( - target.isStage ? TargetType.STAGE : TargetType.SPRITE - ); - } - // If the block info's `hideFromPalette` is true, then filter out this block - return blockFilterIncludesTarget && !block.info.hideFromPalette; - }); - - const colorXML = `colour="${color1}" secondaryColour="${color2}"`; - - // Use a menu icon if there is one. Otherwise, use the block icon. If there's no icon, - // the category menu will show its default colored circle. - let menuIconURI = ''; - if (categoryInfo.menuIconURI) { - menuIconURI = categoryInfo.menuIconURI; - } else if (categoryInfo.blockIconURI) { - menuIconURI = categoryInfo.blockIconURI; - } - const menuIconXML = menuIconURI ? - `iconURI="${menuIconURI}"` : ''; - - let statusButtonXML = ''; - if (categoryInfo.showStatusButton) { - statusButtonXML = 'showStatusButton="true"'; - } - - return { - id: categoryInfo.id, - xml: `${ - paletteBlocks.map(block => block.xml).join('')}` - }; - }); - } - - /** - * @returns {Array.} - an array containing the scratch-blocks JSON information for each dynamic block. - */ - getBlocksJSON () { - return this._blockInfo.reduce( - (result, categoryInfo) => result.concat(categoryInfo.blocks.map(blockInfo => blockInfo.json)), []); - } - - /** - * Get a scratch link socket. - * @param {string} type Either BLE or BT - * @returns {ScratchLinkSocket} The scratch link socket. - */ - getScratchLinkSocket (type) { - const factory = this._linkSocketFactory || this._defaultScratchLinkSocketFactory; - return factory(type); - } - - /** - * Configure how ScratchLink sockets are created. Factory must consume a "type" parameter - * either BT or BLE. - * @param {Function} factory The new factory for creating ScratchLink sockets. - */ - configureScratchLinkSocketFactory (factory) { - this._linkSocketFactory = factory; - } - - /** - * The default scratch link socket creator, using websockets to the installed device manager. - * @param {string} type Either BLE or BT - * @returns {ScratchLinkSocket} The new scratch link socket (a WebSocket object) - */ - _defaultScratchLinkSocketFactory (type) { - return new ScratchLinkWebSocket(type); - } - - /** - * Register an extension that communications with a hardware peripheral by id, - * to have access to it and its peripheral functions in the future. - * @param {string} extensionId - the id of the extension. - * @param {object} extension - the extension to register. - */ - registerPeripheralExtension (extensionId, extension) { - this.peripheralExtensions[extensionId] = extension; - } - - /** - * Tell the specified extension to scan for a peripheral. - * @param {string} extensionId - the id of the extension. - */ - scanForPeripheral (extensionId) { - if (this.peripheralExtensions[extensionId]) { - this.peripheralExtensions[extensionId].scan(); - } - } - - /** - * Connect to the extension's specified peripheral. - * @param {string} extensionId - the id of the extension. - * @param {number} peripheralId - the id of the peripheral. - */ - connectPeripheral (extensionId, peripheralId) { - if (this.peripheralExtensions[extensionId]) { - this.peripheralExtensions[extensionId].connect(peripheralId); - } - } - - /** - * Disconnect from the extension's connected peripheral. - * @param {string} extensionId - the id of the extension. - */ - disconnectPeripheral (extensionId) { - if (this.peripheralExtensions[extensionId]) { - this.peripheralExtensions[extensionId].disconnect(); - } - } - - /** - * Returns whether the extension has a currently connected peripheral. - * @param {string} extensionId - the id of the extension. - * @return {boolean} - whether the extension has a connected peripheral. - */ - getPeripheralIsConnected (extensionId) { - let isConnected = false; - if (this.peripheralExtensions[extensionId]) { - isConnected = this.peripheralExtensions[extensionId].isConnected(); - } - return isConnected; - } - - /** - * Emit an event to indicate that the microphone is being used to stream audio. - * @param {boolean} listening - true if the microphone is currently listening. - */ - emitMicListening (listening) { - this.emit(Runtime.MIC_LISTENING, listening); - } - - /** - * Retrieve the function associated with the given opcode. - * @param {!string} opcode The opcode to look up. - * @return {Function} The function which implements the opcode. - */ - getOpcodeFunction (opcode) { - return this._primitives[opcode]; - } - - /** - * Return whether an opcode represents a hat block. - * @param {!string} opcode The opcode to look up. - * @return {boolean} True if the op is known to be a hat. - */ - getIsHat (opcode) { - return this._hats.hasOwnProperty(opcode); - } - - /** - * Return whether an opcode represents an edge-activated hat block. - * @param {!string} opcode The opcode to look up. - * @return {boolean} True if the op is known to be a edge-activated hat. - */ - getIsEdgeActivatedHat (opcode) { - return this._hats.hasOwnProperty(opcode) && - this._hats[opcode].edgeActivated; - } - - - /** - * Attach the audio engine - * @param {!AudioEngine} audioEngine The audio engine to attach - */ - attachAudioEngine (audioEngine) { - this.audioEngine = audioEngine; - } - - /** - * Attach the renderer - * @param {!RenderWebGL} renderer The renderer to attach - */ - attachRenderer (renderer) { - this.renderer = renderer; - this.renderer.setLayerGroupOrdering(StageLayering.LAYER_GROUPS); - } - - /** - * Set the svg adapter, which converts scratch 2 svgs to scratch 3 svgs - * @param {!SvgRenderer} svgAdapter The adapter to attach - */ - attachV2SVGAdapter (svgAdapter) { - this.v2SvgAdapter = svgAdapter; - } - - /** - * Set the bitmap adapter for the VM/runtime, which converts scratch 2 - * bitmaps to scratch 3 bitmaps. (Scratch 3 bitmaps are all bitmap resolution 2) - * @param {!function} bitmapAdapter The adapter to attach - */ - attachV2BitmapAdapter (bitmapAdapter) { - this.v2BitmapAdapter = bitmapAdapter; - } - - /** - * Attach the storage module - * @param {!ScratchStorage} storage The storage module to attach - */ - attachStorage (storage) { - this.storage = storage; - } - - // ----------------------------------------------------------------------------- - // ----------------------------------------------------------------------------- - - /** - * Create a thread and push it to the list of threads. - * @param {!string} id ID of block that starts the stack. - * @param {!Target} target Target to run thread on. - * @param {?object} opts optional arguments - * @param {?boolean} opts.stackClick true if the script was activated by clicking on the stack - * @param {?boolean} opts.updateMonitor true if the script should update a monitor value - * @return {!Thread} The newly created thread. - */ - _pushThread (id, target, opts) { - const thread = new Thread(id); - thread.target = target; - thread.stackClick = Boolean(opts && opts.stackClick); - thread.updateMonitor = Boolean(opts && opts.updateMonitor); - thread.blockContainer = thread.updateMonitor ? - this.monitorBlocks : - target.blocks; - - thread.pushStack(id); - this.threads.push(thread); - return thread; - } - - /** - * Stop a thread: stop running it immediately, and remove it from the thread list later. - * @param {!Thread} thread Thread object to remove from actives - */ - _stopThread (thread) { - // Mark the thread for later removal - thread.isKilled = true; - // Inform sequencer to stop executing that thread. - this.sequencer.retireThread(thread); - } - - /** - * Restart a thread in place, maintaining its position in the list of threads. - * This is used by `startHats` to and is necessary to ensure 2.0-like execution order. - * Test project: https://scratch.mit.edu/projects/130183108/ - * @param {!Thread} thread Thread object to restart. - * @return {Thread} The restarted thread. - */ - _restartThread (thread) { - const newThread = new Thread(thread.topBlock); - newThread.target = thread.target; - newThread.stackClick = thread.stackClick; - newThread.updateMonitor = thread.updateMonitor; - newThread.blockContainer = thread.blockContainer; - newThread.pushStack(thread.topBlock); - const i = this.threads.indexOf(thread); - if (i > -1) { - this.threads[i] = newThread; - return newThread; - } - this.threads.push(thread); - return thread; - } - - /** - * Return whether a thread is currently active/running. - * @param {?Thread} thread Thread object to check. - * @return {boolean} True if the thread is active/running. - */ - isActiveThread (thread) { - return ( - ( - thread.stack.length > 0 && - thread.status !== Thread.STATUS_DONE) && - this.threads.indexOf(thread) > -1); - } - - /** - * Return whether a thread is waiting for more information or done. - * @param {?Thread} thread Thread object to check. - * @return {boolean} True if the thread is waiting - */ - isWaitingThread (thread) { - return ( - thread.status === Thread.STATUS_PROMISE_WAIT || - thread.status === Thread.STATUS_YIELD_TICK || - !this.isActiveThread(thread) - ); - } - - /** - * Toggle a script. - * @param {!string} topBlockId ID of block that starts the script. - * @param {?object} opts optional arguments to toggle script - * @param {?string} opts.target target ID for target to run script on. If not supplied, uses editing target. - * @param {?boolean} opts.stackClick true if the user activated the stack by clicking, false if not. This - * determines whether we show a visual report when turning on the script. - */ - toggleScript (topBlockId, opts) { - opts = Object.assign({ - target: this._editingTarget, - stackClick: false - }, opts); - // Remove any existing thread. - for (let i = 0; i < this.threads.length; i++) { - // Toggling a script that's already running turns it off - if (this.threads[i].topBlock === topBlockId && this.threads[i].status !== Thread.STATUS_DONE) { - const blockContainer = opts.target.blocks; - const opcode = blockContainer.getOpcode(blockContainer.getBlock(topBlockId)); - - if (this.getIsEdgeActivatedHat(opcode) && this.threads[i].stackClick !== opts.stackClick) { - // Allow edge activated hat thread stack click to coexist with - // edge activated hat thread that runs every frame - continue; - } - this._stopThread(this.threads[i]); - return; - } - } - // Otherwise add it. - this._pushThread(topBlockId, opts.target, opts); - } - - /** - * Enqueue a script that when finished will update the monitor for the block. - * @param {!string} topBlockId ID of block that starts the script. - * @param {?Target} optTarget target Target to run script on. If not supplied, uses editing target. - */ - addMonitorScript (topBlockId, optTarget) { - if (!optTarget) optTarget = this._editingTarget; - for (let i = 0; i < this.threads.length; i++) { - // Don't re-add the script if it's already running - if (this.threads[i].topBlock === topBlockId && this.threads[i].status !== Thread.STATUS_DONE && - this.threads[i].updateMonitor) { - return; - } - } - // Otherwise add it. - this._pushThread(topBlockId, optTarget, {updateMonitor: true}); - } - - /** - * Run a function `f` for all scripts in a workspace. - * `f` will be called with two parameters: - * - the top block ID of the script. - * - the target that owns the script. - * @param {!Function} f Function to call for each script. - * @param {Target=} optTarget Optionally, a target to restrict to. - */ - allScriptsDo (f, optTarget) { - let targets = this.executableTargets; - if (optTarget) { - targets = [optTarget]; - } - for (let t = targets.length - 1; t >= 0; t--) { - const target = targets[t]; - const scripts = target.blocks.getScripts(); - for (let j = 0; j < scripts.length; j++) { - const topBlockId = scripts[j]; - f(topBlockId, target); - } - } - } - - allScriptsByOpcodeDo (opcode, f, optTarget) { - let targets = this.executableTargets; - if (optTarget) { - targets = [optTarget]; - } - for (let t = targets.length - 1; t >= 0; t--) { - const target = targets[t]; - const scripts = BlocksRuntimeCache.getScripts(target.blocks, opcode); - for (let j = 0; j < scripts.length; j++) { - f(scripts[j], target); - } - } - } - - /** - * Start all relevant hats. - * @param {!string} requestedHatOpcode Opcode of hats to start. - * @param {object=} optMatchFields Optionally, fields to match on the hat. - * @param {Target=} optTarget Optionally, a target to restrict to. - * @return {Array.} List of threads started by this function. - */ - startHats (requestedHatOpcode, - optMatchFields, optTarget) { - if (!this._hats.hasOwnProperty(requestedHatOpcode)) { - // No known hat with this opcode. - return; - } - const instance = this; - const newThreads = []; - // Look up metadata for the relevant hat. - const hatMeta = instance._hats[requestedHatOpcode]; - - for (const opts in optMatchFields) { - if (!optMatchFields.hasOwnProperty(opts)) continue; - optMatchFields[opts] = optMatchFields[opts].toUpperCase(); - } - - // Consider all scripts, looking for hats with opcode `requestedHatOpcode`. - this.allScriptsByOpcodeDo(requestedHatOpcode, (script, target) => { - const { - blockId: topBlockId, - fieldsOfInputs: hatFields - } = script; - - // Match any requested fields. - // For example: ensures that broadcasts match. - // This needs to happen before the block is evaluated - // (i.e., before the predicate can be run) because "broadcast and wait" - // needs to have a precise collection of started threads. - for (const matchField in optMatchFields) { - if (hatFields[matchField].value !== optMatchFields[matchField]) { - // Field mismatch. - return; - } - } - - if (hatMeta.restartExistingThreads) { - // If `restartExistingThreads` is true, we should stop - // any existing threads starting with the top block. - for (let i = 0; i < this.threads.length; i++) { - if (this.threads[i].target === target && - this.threads[i].topBlock === topBlockId && - // stack click threads and hat threads can coexist - !this.threads[i].stackClick) { - newThreads.push(this._restartThread(this.threads[i])); - return; - } - } - } else { - // If `restartExistingThreads` is false, we should - // give up if any threads with the top block are running. - for (let j = 0; j < this.threads.length; j++) { - if (this.threads[j].target === target && - this.threads[j].topBlock === topBlockId && - // stack click threads and hat threads can coexist - !this.threads[j].stackClick && - this.threads[j].status !== Thread.STATUS_DONE) { - // Some thread is already running. - return; - } - } - } - // Start the thread with this top block. - newThreads.push(this._pushThread(topBlockId, target)); - }, optTarget); - // For compatibility with Scratch 2, edge triggered hats need to be processed before - // threads are stepped. See ScratchRuntime.as for original implementation - newThreads.forEach(thread => { - execute(this.sequencer, thread); - thread.goToNextBlock(); - }); - return newThreads; - } - - - /** - * Dispose all targets. Return to clean state. - */ - dispose () { - this.stopAll(); - // Deleting each target's variable's monitors. - this.targets.forEach(target => { - if (target.isOriginal) target.deleteMonitors(); - }); - - this.targets.map(this.disposeTarget, this); - this._monitorState = OrderedMap({}); - this.emit(Runtime.RUNTIME_DISPOSED); - this.ioDevices.clock.resetProjectTimer(); - // @todo clear out extensions? turboMode? etc. - - // *********** Cloud ******************* - - // If the runtime currently has cloud data, - // emit a has cloud data update event resetting - // it to false - if (this.hasCloudData()) { - this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, false); - } - - this.ioDevices.cloud.clear(); - - // Reset runtime cloud data info - const newCloudDataManager = cloudDataManager(); - this.hasCloudData = newCloudDataManager.hasCloudVariables; - this.canAddCloudVariable = newCloudDataManager.canAddCloudVariable; - this.addCloudVariable = this._initializeAddCloudVariable(newCloudDataManager); - this.removeCloudVariable = this._initializeRemoveCloudVariable(newCloudDataManager); - } - - /** - * Add a target to the runtime. This tracks the sprite pane - * ordering of the target. The target still needs to be put - * into the correct execution order after calling this function. - * @param {Target} target target to add - */ - addTarget (target) { - this.targets.push(target); - this.executableTargets.push(target); - } - - /** - * Move a target in the execution order by a relative amount. - * - * A positve number will make the target execute earlier. A negative number - * will make the target execute later in the order. - * - * @param {Target} executableTarget target to move - * @param {number} delta number of positions to move target by - * @returns {number} new position in execution order - */ - moveExecutable (executableTarget, delta) { - const oldIndex = this.executableTargets.indexOf(executableTarget); - this.executableTargets.splice(oldIndex, 1); - let newIndex = oldIndex + delta; - if (newIndex > this.executableTargets.length) { - newIndex = this.executableTargets.length; - } - if (newIndex <= 0) { - if (this.executableTargets.length > 0 && this.executableTargets[0].isStage) { - newIndex = 1; - } else { - newIndex = 0; - } - } - this.executableTargets.splice(newIndex, 0, executableTarget); - return newIndex; - } - - /** - * Set a target to execute at a specific position in the execution order. - * - * Infinity will set the target to execute first. 0 will set the target to - * execute last (before the stage). - * - * @param {Target} executableTarget target to move - * @param {number} newIndex position in execution order to place the target - * @returns {number} new position in the execution order - */ - setExecutablePosition (executableTarget, newIndex) { - const oldIndex = this.executableTargets.indexOf(executableTarget); - return this.moveExecutable(executableTarget, newIndex - oldIndex); - } - - /** - * Remove a target from the execution set. - * @param {Target} executableTarget target to remove - */ - removeExecutable (executableTarget) { - const oldIndex = this.executableTargets.indexOf(executableTarget); - if (oldIndex > -1) { - this.executableTargets.splice(oldIndex, 1); - } - } - - /** - * Dispose of a target. - * @param {!Target} disposingTarget Target to dispose of. - */ - disposeTarget (disposingTarget) { - this.targets = this.targets.filter(target => { - if (disposingTarget !== target) return true; - // Allow target to do dispose actions. - target.dispose(); - // Remove from list of targets. - return false; - }); - } - - /** - * Stop any threads acting on the target. - * @param {!Target} target Target to stop threads for. - * @param {Thread=} optThreadException Optional thread to skip. - */ - stopForTarget (target, optThreadException) { - // Emit stop event to allow blocks to clean up any state. - this.emit(Runtime.STOP_FOR_TARGET, target, optThreadException); - - // Stop any threads on the target. - for (let i = 0; i < this.threads.length; i++) { - if (this.threads[i] === optThreadException) { - continue; - } - if (this.threads[i].target === target) { - this._stopThread(this.threads[i]); - } - } - } - - /** - * Start all threads that start with the green flag. - */ - greenFlag () { - this.stopAll(); - this.emit(Runtime.PROJECT_START); - this.ioDevices.clock.resetProjectTimer(); - this.targets.forEach(target => target.clearEdgeActivatedValues()); - // Inform all targets of the green flag. - for (let i = 0; i < this.targets.length; i++) { - this.targets[i].onGreenFlag(); - } - this.startHats('event_whenflagclicked'); - } - - /** - * Stop "everything." - */ - stopAll () { - // Emit stop event to allow blocks to clean up any state. - this.emit(Runtime.PROJECT_STOP_ALL); - - // Dispose all clones. - const newTargets = []; - for (let i = 0; i < this.targets.length; i++) { - this.targets[i].onStopAll(); - if (this.targets[i].hasOwnProperty('isOriginal') && - !this.targets[i].isOriginal) { - this.targets[i].dispose(); - } else { - newTargets.push(this.targets[i]); - } - } - this.targets = newTargets; - // Dispose of the active thread. - if (this.sequencer.activeThread !== null) { - this._stopThread(this.sequencer.activeThread); - } - // Remove all remaining threads from executing in the next tick. - this.threads = []; - } - - /** - * Repeatedly run `sequencer.stepThreads` and filter out - * inactive threads after each iteration. - */ - _step () { - if (this.profiler !== null) { - if (stepProfilerId === -1) { - stepProfilerId = this.profiler.idByName('Runtime._step'); - } - this.profiler.start(stepProfilerId); - } - - // Clean up threads that were told to stop during or since the last step - this.threads = this.threads.filter(thread => !thread.isKilled); - - // Find all edge-activated hats, and add them to threads to be evaluated. - for (const hatType in this._hats) { - if (!this._hats.hasOwnProperty(hatType)) continue; - const hat = this._hats[hatType]; - if (hat.edgeActivated) { - this.startHats(hatType); - } - } - this.redrawRequested = false; - this._pushMonitors(); - if (this.profiler !== null) { - if (stepThreadsProfilerId === -1) { - stepThreadsProfilerId = this.profiler.idByName('Sequencer.stepThreads'); - } - this.profiler.start(stepThreadsProfilerId); - } - const doneThreads = this.sequencer.stepThreads(); - if (this.profiler !== null) { - this.profiler.stop(); - } - this._updateGlows(doneThreads); - // Add done threads so that even if a thread finishes within 1 frame, the green - // flag will still indicate that a script ran. - this._emitProjectRunStatus( - this.threads.length + doneThreads.length - - this._getMonitorThreadCount([...this.threads, ...doneThreads])); - // Store threads that completed this iteration for testing and other - // internal purposes. - this._lastStepDoneThreads = doneThreads; - if (this.renderer) { - // @todo: Only render when this.redrawRequested or clones rendered. - if (this.profiler !== null) { - if (rendererDrawProfilerId === -1) { - rendererDrawProfilerId = this.profiler.idByName('RenderWebGL.draw'); - } - this.profiler.start(rendererDrawProfilerId); - } - this.renderer.draw(); - if (this.profiler !== null) { - this.profiler.stop(); - } - } - - if (this._refreshTargets) { - this.emit(Runtime.TARGETS_UPDATE, false /* Don't emit project changed */); - this._refreshTargets = false; - } - - if (!this._prevMonitorState.equals(this._monitorState)) { - this.emit(Runtime.MONITORS_UPDATE, this._monitorState); - this._prevMonitorState = this._monitorState; - } - - if (this.profiler !== null) { - this.profiler.stop(); - this.profiler.reportFrames(); - } - } - - /** - * Get the number of threads in the given array that are monitor threads (threads - * that update monitor values, and don't count as running a script). - * @param {!Array.} threads The set of threads to look through. - * @return {number} The number of monitor threads in threads. - */ - _getMonitorThreadCount (threads) { - let count = 0; - threads.forEach(thread => { - if (thread.updateMonitor) count++; - }); - return count; - } - - /** - * Queue monitor blocks to sequencer to be run. - */ - _pushMonitors () { - this.monitorBlocks.runAllMonitored(this); - } - - /** - * Set the current editing target known by the runtime. - * @param {!Target} editingTarget New editing target. - */ - setEditingTarget (editingTarget) { - const oldEditingTarget = this._editingTarget; - this._editingTarget = editingTarget; - // Script glows must be cleared. - this._scriptGlowsPreviousFrame = []; - this._updateGlows(); - - if (oldEditingTarget !== this._editingTarget) { - this.requestToolboxExtensionsUpdate(); - } - } - - /** - * Set whether we are in 30 TPS compatibility mode. - * @param {boolean} compatibilityModeOn True iff in compatibility mode. - */ - setCompatibilityMode (compatibilityModeOn) { - this.compatibilityMode = compatibilityModeOn; - if (this._steppingInterval) { - clearInterval(this._steppingInterval); - this._steppingInterval = null; - this.start(); - } - } - - /** - * Emit glows/glow clears for scripts after a single tick. - * Looks at `this.threads` and notices which have turned on/off new glows. - * @param {Array.=} optExtraThreads Optional list of inactive threads. - */ - _updateGlows (optExtraThreads) { - const searchThreads = []; - searchThreads.push.apply(searchThreads, this.threads); - if (optExtraThreads) { - searchThreads.push.apply(searchThreads, optExtraThreads); - } - // Set of scripts that request a glow this frame. - const requestedGlowsThisFrame = []; - // Final set of scripts glowing during this frame. - const finalScriptGlows = []; - // Find all scripts that should be glowing. - for (let i = 0; i < searchThreads.length; i++) { - const thread = searchThreads[i]; - const target = thread.target; - if (target === this._editingTarget) { - const blockForThread = thread.blockGlowInFrame; - if (thread.requestScriptGlowInFrame || thread.stackClick) { - let script = target.blocks.getTopLevelScript(blockForThread); - if (!script) { - // Attempt to find in flyout blocks. - script = this.flyoutBlocks.getTopLevelScript( - blockForThread - ); - } - if (script) { - requestedGlowsThisFrame.push(script); - } - } - } - } - // Compare to previous frame. - for (let j = 0; j < this._scriptGlowsPreviousFrame.length; j++) { - const previousFrameGlow = this._scriptGlowsPreviousFrame[j]; - if (requestedGlowsThisFrame.indexOf(previousFrameGlow) < 0) { - // Glow turned off. - this.glowScript(previousFrameGlow, false); - } else { - // Still glowing. - finalScriptGlows.push(previousFrameGlow); - } - } - for (let k = 0; k < requestedGlowsThisFrame.length; k++) { - const currentFrameGlow = requestedGlowsThisFrame[k]; - if (this._scriptGlowsPreviousFrame.indexOf(currentFrameGlow) < 0) { - // Glow turned on. - this.glowScript(currentFrameGlow, true); - finalScriptGlows.push(currentFrameGlow); - } - } - this._scriptGlowsPreviousFrame = finalScriptGlows; - } - - /** - * Emit run start/stop after each tick. Emits when `this.threads.length` goes - * between non-zero and zero - * - * @param {number} nonMonitorThreadCount The new nonMonitorThreadCount - */ - _emitProjectRunStatus (nonMonitorThreadCount) { - if (this._nonMonitorThreadCount === 0 && nonMonitorThreadCount > 0) { - this.emit(Runtime.PROJECT_RUN_START); - } - if (this._nonMonitorThreadCount > 0 && nonMonitorThreadCount === 0) { - this.emit(Runtime.PROJECT_RUN_STOP); - } - this._nonMonitorThreadCount = nonMonitorThreadCount; - } - - /** - * "Quiet" a script's glow: stop the VM from generating glow/unglow events - * about that script. Use when a script has just been deleted, but we may - * still be tracking glow data about it. - * @param {!string} scriptBlockId Id of top-level block in script to quiet. - */ - quietGlow (scriptBlockId) { - const index = this._scriptGlowsPreviousFrame.indexOf(scriptBlockId); - if (index > -1) { - this._scriptGlowsPreviousFrame.splice(index, 1); - } - } - - /** - * Emit feedback for block glowing (used in the sequencer). - * @param {?string} blockId ID for the block to update glow - * @param {boolean} isGlowing True to turn on glow; false to turn off. - */ - glowBlock (blockId, isGlowing) { - if (isGlowing) { - this.emit(Runtime.BLOCK_GLOW_ON, {id: blockId}); - } else { - this.emit(Runtime.BLOCK_GLOW_OFF, {id: blockId}); - } - } - - /** - * Emit feedback for script glowing. - * @param {?string} topBlockId ID for the top block to update glow - * @param {boolean} isGlowing True to turn on glow; false to turn off. - */ - glowScript (topBlockId, isGlowing) { - if (isGlowing) { - this.emit(Runtime.SCRIPT_GLOW_ON, {id: topBlockId}); - } else { - this.emit(Runtime.SCRIPT_GLOW_OFF, {id: topBlockId}); - } - } - - /** - * Emit whether blocks are being dragged over gui - * @param {boolean} areBlocksOverGui True if blocks are dragged out of blocks workspace, false otherwise - */ - emitBlockDragUpdate (areBlocksOverGui) { - this.emit(Runtime.BLOCK_DRAG_UPDATE, areBlocksOverGui); - } - - /** - * Emit event to indicate that the block drag has ended with the blocks outside the blocks workspace - * @param {Array.} blocks The set of blocks dragged to the GUI - * @param {string} topBlockId The original id of the top block being dragged - */ - emitBlockEndDrag (blocks, topBlockId) { - this.emit(Runtime.BLOCK_DRAG_END, blocks, topBlockId); - } - - /** - * Emit value for reporter to show in the blocks. - * @param {string} blockId ID for the block. - * @param {string} value Value to show associated with the block. - */ - visualReport (blockId, value) { - this.emit(Runtime.VISUAL_REPORT, {id: blockId, value: String(value)}); - } - - /** - * Add a monitor to the state. If the monitor already exists in the state, - * updates those properties that are defined in the given monitor record. - * @param {!MonitorRecord} monitor Monitor to add. - */ - requestAddMonitor (monitor) { - const id = monitor.get('id'); - if (!this.requestUpdateMonitor(monitor)) { // update monitor if it exists in the state - // if the monitor did not exist in the state, add it - this._monitorState = this._monitorState.set(id, monitor); - } - } - - /** - * Update a monitor in the state and report success/failure of update. - * @param {!Map} monitor Monitor values to update. Values on the monitor with overwrite - * values on the old monitor with the same ID. If a value isn't defined on the new monitor, - * the old monitor will keep its old value. - * @return {boolean} true if monitor exists in the state and was updated, false if it did not exist. - */ - requestUpdateMonitor (monitor) { - const id = monitor.get('id'); - if (this._monitorState.has(id)) { - this._monitorState = - // Use mergeWith here to prevent undefined values from overwriting existing ones - this._monitorState.set(id, this._monitorState.get(id).mergeWith((prev, next) => { - if (typeof next === 'undefined' || next === null) { - return prev; - } - return next; - }, monitor)); - return true; - } - return false; - } - - /** - * Removes a monitor from the state. Does nothing if the monitor already does - * not exist in the state. - * @param {!string} monitorId ID of the monitor to remove. - */ - requestRemoveMonitor (monitorId) { - this._monitorState = this._monitorState.delete(monitorId); - } - - /** - * Hides a monitor and returns success/failure of action. - * @param {!string} monitorId ID of the monitor to hide. - * @return {boolean} true if monitor exists and was updated, false otherwise - */ - requestHideMonitor (monitorId) { - return this.requestUpdateMonitor(new Map([ - ['id', monitorId], - ['visible', false] - ])); - } - - /** - * Shows a monitor and returns success/failure of action. - * not exist in the state. - * @param {!string} monitorId ID of the monitor to show. - * @return {boolean} true if monitor exists and was updated, false otherwise - */ - requestShowMonitor (monitorId) { - return this.requestUpdateMonitor(new Map([ - ['id', monitorId], - ['visible', true] - ])); - } - - /** - * Removes all monitors with the given target ID from the state. Does nothing if - * the monitor already does not exist in the state. - * @param {!string} targetId Remove all monitors with given target ID. - */ - requestRemoveMonitorByTargetId (targetId) { - this._monitorState = this._monitorState.filterNot(value => value.targetId === targetId); - } - - /** - * Get a target by its id. - * @param {string} targetId Id of target to find. - * @return {?Target} The target, if found. - */ - getTargetById (targetId) { - for (let i = 0; i < this.targets.length; i++) { - const target = this.targets[i]; - if (target.id === targetId) { - return target; - } - } - } - - /** - * Get the first original (non-clone-block-created) sprite given a name. - * @param {string} spriteName Name of sprite to look for. - * @return {?Target} Target representing a sprite of the given name. - */ - getSpriteTargetByName (spriteName) { - for (let i = 0; i < this.targets.length; i++) { - const target = this.targets[i]; - if (target.isStage) { - continue; - } - if (target.sprite && target.sprite.name === spriteName) { - return target; - } - } - } - - /** - * Get a target by its drawable id. - * @param {number} drawableID drawable id of target to find - * @return {?Target} The target, if found - */ - getTargetByDrawableId (drawableID) { - for (let i = 0; i < this.targets.length; i++) { - const target = this.targets[i]; - if (target.drawableID === drawableID) return target; - } - } - - /** - * Update the clone counter to track how many clones are created. - * @param {number} changeAmount How many clones have been created/destroyed. - */ - changeCloneCounter (changeAmount) { - this._cloneCounter += changeAmount; - } - - /** - * Return whether there are clones available. - * @return {boolean} True until the number of clones hits Runtime.MAX_CLONES. - */ - clonesAvailable () { - return this._cloneCounter < Runtime.MAX_CLONES; - } - - /** - * Report that the project has loaded in the Virtual Machine. - */ - emitProjectLoaded () { - this.emit(Runtime.PROJECT_LOADED); - } - - /** - * Report that the project has changed in a way that would affect serialization - */ - emitProjectChanged () { - this.emit(Runtime.PROJECT_CHANGED); - } - - /** - * Report that a new target has been created, possibly by cloning an existing target. - * @param {Target} newTarget - the newly created target. - * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any. - * @fires Runtime#targetWasCreated - */ - fireTargetWasCreated (newTarget, sourceTarget) { - this.emit('targetWasCreated', newTarget, sourceTarget); - } - - /** - * Report that a clone target is being removed. - * @param {Target} target - the target being removed - * @fires Runtime#targetWasRemoved - */ - fireTargetWasRemoved (target) { - this.emit('targetWasRemoved', target); - } - - /** - * Get a target representing the Scratch stage, if one exists. - * @return {?Target} The target, if found. - */ - getTargetForStage () { - for (let i = 0; i < this.targets.length; i++) { - const target = this.targets[i]; - if (target.isStage) { - return target; - } - } - } - - /** - * Get the editing target. - * @return {?Target} The editing target. - */ - getEditingTarget () { - return this._editingTarget; - } - - getAllVarNamesOfType (varType) { - let varNames = []; - for (const target of this.targets) { - const targetVarNames = target.getAllVariableNamesInScopeByType(varType, true); - varNames = varNames.concat(targetVarNames); - } - return varNames; - } - - /** - * Get the label or label function for an opcode - * @param {string} extendedOpcode - the opcode you want a label for - * @return {object} - object with label and category - * @property {string} category - the category for this opcode - * @property {Function} [labelFn] - function to generate the label for this opcode - * @property {string} [label] - the label for this opcode if `labelFn` is absent - */ - getLabelForOpcode (extendedOpcode) { - const [category, opcode] = StringUtil.splitFirst(extendedOpcode, '_'); - if (!(category && opcode)) return; - - const categoryInfo = this._blockInfo.find(ci => ci.id === category); - if (!categoryInfo) return; - - const block = categoryInfo.blocks.find(b => b.info.opcode === opcode); - if (!block) return; - - // TODO: we may want to format the label in a locale-specific way. - return { - category: 'extension', // This assumes that all extensions have the same monitor color. - label: `${categoryInfo.name}: ${block.info.text}` - }; - } - - /** - * Create a new global variable avoiding conflicts with other variable names. - * @param {string} variableName The desired variable name for the new global variable. - * This can be turned into a fresh name as necessary. - * @param {string} optVarId An optional ID to use for the variable. A new one will be generated - * if a falsey value for this parameter is provided. - * @param {string} optVarType The type of the variable to create. Defaults to Variable.SCALAR_TYPE. - * @return {Variable} The new variable that was created. - */ - createNewGlobalVariable (variableName, optVarId, optVarType) { - const varType = (typeof optVarType === 'string') ? optVarType : Variable.SCALAR_TYPE; - const allVariableNames = this.getAllVarNamesOfType(varType); - const newName = StringUtil.unusedName(variableName, allVariableNames); - const variable = new Variable(optVarId || uid(), newName, varType); - const stage = this.getTargetForStage(); - stage.variables[variable.id] = variable; - return variable; - } - - /** - * Tell the runtime to request a redraw. - * Use after a clone/sprite has completed some visible operation on the stage. - */ - requestRedraw () { - this.redrawRequested = true; - } - - /** - * Emit a targets update at the end of the step if the provided target is - * the original sprite - * @param {!Target} target Target requesting the targets update - */ - requestTargetsUpdate (target) { - if (!target.isOriginal) return; - this._refreshTargets = true; - } - - /** - * Emit an event that indicates that the blocks on the workspace need updating. - */ - requestBlocksUpdate () { - this.emit(Runtime.BLOCKS_NEED_UPDATE); - } - - /** - * Emit an event that indicates that the toolbox extension blocks need updating. - */ - requestToolboxExtensionsUpdate () { - this.emit(Runtime.TOOLBOX_EXTENSIONS_NEED_UPDATE); - } - - /** - * Set up timers to repeatedly step in a browser. - */ - start () { - // Do not start if we are already running - if (this._steppingInterval) return; - - let interval = Runtime.THREAD_STEP_INTERVAL; - if (this.compatibilityMode) { - interval = Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY; - } - this.currentStepTime = interval; - this._steppingInterval = setInterval(() => { - this._step(); - }, interval); - this.emit(Runtime.RUNTIME_STARTED); - } - - /** - * Turn on profiling. - * @param {Profiler/FrameCallback} onFrame A callback handle passed a - * profiling frame when the profiler reports its collected data. - */ - enableProfiling (onFrame) { - if (Profiler.available()) { - this.profiler = new Profiler(onFrame); - } - } - - /** - * Turn off profiling. - */ - disableProfiling () { - this.profiler = null; - } - - /** - * Update a millisecond timestamp value that is saved on the Runtime. - * This value is helpful in certain instances for compatibility with Scratch 2, - * which sometimes uses a `currentMSecs` timestamp value in Interpreter.as - */ - updateCurrentMSecs () { - this.currentMSecs = Date.now(); - } -} - -/** - * Event fired after a new target has been created, possibly by cloning an existing target. - * - * @event Runtime#targetWasCreated - * @param {Target} newTarget - the newly created target. - * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any. - */ - -module.exports = Runtime; diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js deleted file mode 100644 index 7cb556c5d0..0000000000 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ /dev/null @@ -1,440 +0,0 @@ -const dispatch = require('../dispatch/central-dispatch'); -const log = require('../util/log'); -const maybeFormatMessage = require('../util/maybe-format-message'); - -const BlockType = require('./block-type'); - -// These extensions are currently built into the VM repository but should not be loaded at startup. -// TODO: move these out into a separate repository? -// TODO: change extension spec so that library info, including extension ID, can be collected through static methods - -const builtinExtensions = { - // This is an example that isn't loaded with the other core blocks, - // but serves as a reference for loading core blocks as extensions. - coreExample: () => require('../blocks/scratch3_core_example'), - // These are the non-core built-in extensions. - pen: () => require('../extensions/scratch3_pen'), - wedo2: () => require('../extensions/scratch3_wedo2'), - music: () => require('../extensions/scratch3_music'), - microbit: () => require('../extensions/scratch3_microbit'), - text2speech: () => require('../extensions/scratch3_text2speech'), - translate: () => require('../extensions/scratch3_translate'), - videoSensing: () => require('../extensions/scratch3_video_sensing'), - ev3: () => require('../extensions/scratch3_ev3'), - makeymakey: () => require('../extensions/scratch3_makeymakey'), - boost: () => require('../extensions/scratch3_boost'), - gdxfor: () => require('../extensions/scratch3_gdx_for') -}; - -/** - * @typedef {object} ArgumentInfo - Information about an extension block argument - * @property {ArgumentType} type - the type of value this argument can take - * @property {*|undefined} default - the default value of this argument (default: blank) - */ - -/** - * @typedef {object} ConvertedBlockInfo - Raw extension block data paired with processed data ready for scratch-blocks - * @property {ExtensionBlockMetadata} info - the raw block info - * @property {object} json - the scratch-blocks JSON definition for this block - * @property {string} xml - the scratch-blocks XML definition for this block - */ - -/** - * @typedef {object} CategoryInfo - Information about a block category - * @property {string} id - the unique ID of this category - * @property {string} name - the human-readable name of this category - * @property {string|undefined} blockIconURI - optional URI for the block icon image - * @property {string} color1 - the primary color for this category, in '#rrggbb' format - * @property {string} color2 - the secondary color for this category, in '#rrggbb' format - * @property {string} color3 - the tertiary color for this category, in '#rrggbb' format - * @property {Array.} blocks - the blocks, separators, etc. in this category - * @property {Array.} menus - the menus provided by this category - */ - -/** - * @typedef {object} PendingExtensionWorker - Information about an extension worker still initializing - * @property {string} extensionURL - the URL of the extension to be loaded by this worker - * @property {Function} resolve - function to call on successful worker startup - * @property {Function} reject - function to call on failed worker startup - */ - -class ExtensionManager { - constructor (runtime) { - /** - * The ID number to provide to the next extension worker. - * @type {int} - */ - this.nextExtensionWorker = 0; - - /** - * FIFO queue of extensions which have been requested but not yet loaded in a worker, - * along with promise resolution functions to call once the worker is ready or failed. - * - * @type {Array.} - */ - this.pendingExtensions = []; - - /** - * Map of worker ID to workers which have been allocated but have not yet finished initialization. - * @type {Array.} - */ - this.pendingWorkers = []; - - /** - * Set of loaded extension URLs/IDs (equivalent for built-in extensions). - * @type {Set.} - * @private - */ - this._loadedExtensions = new Map(); - - /** - * Keep a reference to the runtime so we can construct internal extension objects. - * TODO: remove this in favor of extensions accessing the runtime as a service. - * @type {Runtime} - */ - this.runtime = runtime; - - dispatch.setService('extensions', this).catch(e => { - log.error(`ExtensionManager was unable to register extension service: ${JSON.stringify(e)}`); - }); - } - - /** - * Check whether an extension is registered or is in the process of loading. This is intended to control loading or - * adding extensions so it may return `true` before the extension is ready to be used. Use the promise returned by - * `loadExtensionURL` if you need to wait until the extension is truly ready. - * @param {string} extensionID - the ID of the extension. - * @returns {boolean} - true if loaded, false otherwise. - */ - isExtensionLoaded (extensionID) { - return this._loadedExtensions.has(extensionID); - } - - /** - * Synchronously load an internal extension (core or non-core) by ID. This call will - * fail if the provided id is not does not match an internal extension. - * @param {string} extensionId - the ID of an internal extension - */ - loadExtensionIdSync (extensionId) { - if (!builtinExtensions.hasOwnProperty(extensionId)) { - log.warn(`Could not find extension ${extensionId} in the built in extensions.`); - return; - } - - /** @TODO dupe handling for non-builtin extensions. See commit 670e51d33580e8a2e852b3b038bb3afc282f81b9 */ - if (this.isExtensionLoaded(extensionId)) { - const message = `Rejecting attempt to load a second extension with ID ${extensionId}`; - log.warn(message); - return; - } - - const extension = builtinExtensions[extensionId](); - const extensionInstance = new extension(this.runtime); - const serviceName = this._registerInternalExtension(extensionInstance); - this._loadedExtensions.set(extensionId, serviceName); - } - - /** - * Load an extension by URL or internal extension ID - * @param {string} extensionURL - the URL for the extension to load OR the ID of an internal extension - * @returns {Promise} resolved once the extension is loaded and initialized or rejected on failure - */ - loadExtensionURL (extensionURL) { - if (builtinExtensions.hasOwnProperty(extensionURL)) { - /** @TODO dupe handling for non-builtin extensions. See commit 670e51d33580e8a2e852b3b038bb3afc282f81b9 */ - if (this.isExtensionLoaded(extensionURL)) { - const message = `Rejecting attempt to load a second extension with ID ${extensionURL}`; - log.warn(message); - return Promise.resolve(); - } - - const extension = builtinExtensions[extensionURL](); - const extensionInstance = new extension(this.runtime); - const serviceName = this._registerInternalExtension(extensionInstance); - this._loadedExtensions.set(extensionURL, serviceName); - return Promise.resolve(); - } - - return new Promise((resolve, reject) => { - // If we `require` this at the global level it breaks non-webpack targets, including tests - const ExtensionWorker = require('worker-loader?name=extension-worker.js!./extension-worker'); - - this.pendingExtensions.push({extensionURL, resolve, reject}); - dispatch.addWorker(new ExtensionWorker()); - }); - } - - /** - * Regenerate blockinfo for any loaded extensions - * @returns {Promise} resolved once all the extensions have been reinitialized - */ - refreshBlocks () { - const allPromises = Array.from(this._loadedExtensions.values()).map(serviceName => - dispatch.call(serviceName, 'getInfo') - .then(info => { - info = this._prepareExtensionInfo(serviceName, info); - dispatch.call('runtime', '_refreshExtensionPrimitives', info); - }) - .catch(e => { - log.error(`Failed to refresh built-in extension primitives: ${JSON.stringify(e)}`); - }) - ); - return Promise.all(allPromises); - } - - allocateWorker () { - const id = this.nextExtensionWorker++; - const workerInfo = this.pendingExtensions.shift(); - this.pendingWorkers[id] = workerInfo; - return [id, workerInfo.extensionURL]; - } - - /** - * Synchronously collect extension metadata from the specified service and begin the extension registration process. - * @param {string} serviceName - the name of the service hosting the extension. - */ - registerExtensionServiceSync (serviceName) { - const info = dispatch.callSync(serviceName, 'getInfo'); - this._registerExtensionInfo(serviceName, info); - } - - /** - * Collect extension metadata from the specified service and begin the extension registration process. - * @param {string} serviceName - the name of the service hosting the extension. - */ - registerExtensionService (serviceName) { - dispatch.call(serviceName, 'getInfo').then(info => { - this._registerExtensionInfo(serviceName, info); - }); - } - - /** - * Called by an extension worker to indicate that the worker has finished initialization. - * @param {int} id - the worker ID. - * @param {*?} e - the error encountered during initialization, if any. - */ - onWorkerInit (id, e) { - const workerInfo = this.pendingWorkers[id]; - delete this.pendingWorkers[id]; - if (e) { - workerInfo.reject(e); - } else { - workerInfo.resolve(id); - } - } - - /** - * Register an internal (non-Worker) extension object - * @param {object} extensionObject - the extension object to register - * @returns {string} The name of the registered extension service - */ - _registerInternalExtension (extensionObject) { - const extensionInfo = extensionObject.getInfo(); - const fakeWorkerId = this.nextExtensionWorker++; - const serviceName = `extension_${fakeWorkerId}_${extensionInfo.id}`; - dispatch.setServiceSync(serviceName, extensionObject); - dispatch.callSync('extensions', 'registerExtensionServiceSync', serviceName); - return serviceName; - } - - /** - * Sanitize extension info then register its primitives with the VM. - * @param {string} serviceName - the name of the service hosting the extension - * @param {ExtensionInfo} extensionInfo - the extension's metadata - * @private - */ - _registerExtensionInfo (serviceName, extensionInfo) { - extensionInfo = this._prepareExtensionInfo(serviceName, extensionInfo); - dispatch.call('runtime', '_registerExtensionPrimitives', extensionInfo).catch(e => { - log.error(`Failed to register primitives for extension on service ${serviceName}:`, e); - }); - } - - /** - * Modify the provided text as necessary to ensure that it may be used as an attribute value in valid XML. - * @param {string} text - the text to be sanitized - * @returns {string} - the sanitized text - * @private - */ - _sanitizeID (text) { - return text.toString().replace(/[<"&]/, '_'); - } - - /** - * Apply minor cleanup and defaults for optional extension fields. - * TODO: make the ID unique in cases where two copies of the same extension are loaded. - * @param {string} serviceName - the name of the service hosting this extension block - * @param {ExtensionInfo} extensionInfo - the extension info to be sanitized - * @returns {ExtensionInfo} - a new extension info object with cleaned-up values - * @private - */ - _prepareExtensionInfo (serviceName, extensionInfo) { - extensionInfo = Object.assign({}, extensionInfo); - if (!/^[a-z0-9]+$/i.test(extensionInfo.id)) { - throw new Error('Invalid extension id'); - } - extensionInfo.name = extensionInfo.name || extensionInfo.id; - extensionInfo.blocks = extensionInfo.blocks || []; - extensionInfo.targetTypes = extensionInfo.targetTypes || []; - extensionInfo.blocks = extensionInfo.blocks.reduce((results, blockInfo) => { - try { - let result; - switch (blockInfo) { - case '---': // separator - result = '---'; - break; - default: // an ExtensionBlockMetadata object - result = this._prepareBlockInfo(serviceName, blockInfo); - break; - } - results.push(result); - } catch (e) { - // TODO: more meaningful error reporting - log.error(`Error processing block: ${e.message}, Block:\n${JSON.stringify(blockInfo)}`); - } - return results; - }, []); - extensionInfo.menus = extensionInfo.menus || {}; - extensionInfo.menus = this._prepareMenuInfo(serviceName, extensionInfo.menus); - return extensionInfo; - } - - /** - * Prepare extension menus. e.g. setup binding for dynamic menu functions. - * @param {string} serviceName - the name of the service hosting this extension block - * @param {Array.} menus - the menu defined by the extension. - * @returns {Array.} - a menuInfo object with all preprocessing done. - * @private - */ - _prepareMenuInfo (serviceName, menus) { - const menuNames = Object.getOwnPropertyNames(menus); - for (let i = 0; i < menuNames.length; i++) { - const menuName = menuNames[i]; - let menuInfo = menus[menuName]; - - // If the menu description is in short form (items only) then normalize it to general form: an object with - // its items listed in an `items` property. - if (!menuInfo.items) { - menuInfo = { - items: menuInfo - }; - menus[menuName] = menuInfo; - } - // If `items` is a string, it should be the name of a function in the extension object. Calling the - // function should return an array of items to populate the menu when it is opened. - if (typeof menuInfo.items === 'string') { - const menuItemFunctionName = menuInfo.items; - const serviceObject = dispatch.services[serviceName]; - // Bind the function here so we can pass a simple item generation function to Scratch Blocks later. - menuInfo.items = this._getExtensionMenuItems.bind(this, serviceObject, menuItemFunctionName); - } - } - return menus; - } - - /** - * Fetch the items for a particular extension menu, providing the target ID for context. - * @param {object} extensionObject - the extension object providing the menu. - * @param {string} menuItemFunctionName - the name of the menu function to call. - * @returns {Array} menu items ready for scratch-blocks. - * @private - */ - _getExtensionMenuItems (extensionObject, menuItemFunctionName) { - // Fetch the items appropriate for the target currently being edited. This assumes that menus only - // collect items when opened by the user while editing a particular target. - const editingTarget = this.runtime.getEditingTarget() || this.runtime.getTargetForStage(); - const editingTargetID = editingTarget ? editingTarget.id : null; - const extensionMessageContext = this.runtime.makeMessageContextForTarget(editingTarget); - - // TODO: Fix this to use dispatch.call when extensions are running in workers. - const menuFunc = extensionObject[menuItemFunctionName]; - const menuItems = menuFunc.call(extensionObject, editingTargetID).map( - item => { - item = maybeFormatMessage(item, extensionMessageContext); - switch (typeof item) { - case 'object': - return [ - maybeFormatMessage(item.text, extensionMessageContext), - item.value - ]; - case 'string': - return [item, item]; - default: - return item; - } - }); - - if (!menuItems || menuItems.length < 1) { - throw new Error(`Extension menu returned no items: ${menuItemFunctionName}`); - } - return menuItems; - } - - /** - * Apply defaults for optional block fields. - * @param {string} serviceName - the name of the service hosting this extension block - * @param {ExtensionBlockMetadata} blockInfo - the block info from the extension - * @returns {ExtensionBlockMetadata} - a new block info object which has values for all relevant optional fields. - * @private - */ - _prepareBlockInfo (serviceName, blockInfo) { - blockInfo = Object.assign({}, { - blockType: BlockType.COMMAND, - terminal: false, - blockAllThreads: false, - arguments: {} - }, blockInfo); - blockInfo.opcode = blockInfo.opcode && this._sanitizeID(blockInfo.opcode); - blockInfo.text = blockInfo.text || blockInfo.opcode; - - switch (blockInfo.blockType) { - case BlockType.EVENT: - if (blockInfo.func) { - log.warn(`Ignoring function "${blockInfo.func}" for event block ${blockInfo.opcode}`); - } - break; - case BlockType.BUTTON: - if (blockInfo.opcode) { - log.warn(`Ignoring opcode "${blockInfo.opcode}" for button with text: ${blockInfo.text}`); - } - break; - default: { - if (!blockInfo.opcode) { - throw new Error('Missing opcode for block'); - } - - const funcName = blockInfo.func ? this._sanitizeID(blockInfo.func) : blockInfo.opcode; - - const getBlockInfo = blockInfo.isDynamic ? - args => args && args.mutation && args.mutation.blockInfo : - () => blockInfo; - const callBlockFunc = (() => { - if (dispatch._isRemoteService(serviceName)) { - return (args, util, realBlockInfo) => - dispatch.call(serviceName, funcName, args, util, realBlockInfo); - } - - // avoid promise latency if we can call direct - const serviceObject = dispatch.services[serviceName]; - if (!serviceObject[funcName]) { - // The function might show up later as a dynamic property of the service object - log.warn(`Could not find extension block function called ${funcName}`); - } - return (args, util, realBlockInfo) => - serviceObject[funcName](args, util, realBlockInfo); - })(); - - blockInfo.func = (args, util) => { - const realBlockInfo = getBlockInfo(args); - // TODO: filter args using the keys of realBlockInfo.arguments? maybe only if sandboxed? - return callBlockFunc(args, util, realBlockInfo); - }; - break; - } - } - - return blockInfo; - } -} - -module.exports = ExtensionManager; diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js deleted file mode 100644 index b0c1ebfed9..0000000000 --- a/packages/scratch-vm/src/sprites/rendered-target.js +++ /dev/null @@ -1,1115 +0,0 @@ -const MathUtil = require('../util/math-util'); -const StringUtil = require('../util/string-util'); -const Cast = require('../util/cast'); -const Clone = require('../util/clone'); -const Target = require('../engine/target'); -const StageLayering = require('../engine/stage-layering'); - -/** - * Rendered target: instance of a sprite (clone), or the stage. - */ -class RenderedTarget extends Target { - /** - * @param {!Sprite} sprite Reference to the parent sprite. - * @param {Runtime} runtime Reference to the runtime. - * @constructor - */ - constructor (sprite, runtime) { - super(runtime, sprite.blocks); - - /** - * Reference to the sprite that this is a render of. - * @type {!Sprite} - */ - this.sprite = sprite; - /** - * Reference to the global renderer for this VM, if one exists. - * @type {?RenderWebGL} - */ - this.renderer = null; - if (this.runtime) { - this.renderer = this.runtime.renderer; - } - /** - * ID of the drawable for this rendered target, - * returned by the renderer, if rendered. - * @type {?Number} - */ - this.drawableID = null; - - /** - * Drag state of this rendered target. If true, x/y position can't be - * changed by blocks. - * @type {boolean} - */ - this.dragging = false; - - /** - * Map of current graphic effect values. - * @type {!Object.} - */ - this.effects = { - color: 0, - fisheye: 0, - whirl: 0, - pixelate: 0, - mosaic: 0, - brightness: 0, - ghost: 0 - }; - - /** - * Whether this represents an "original" non-clone rendered-target for a sprite, - * i.e., created by the editor and not clone blocks. - * @type {boolean} - */ - this.isOriginal = true; - - /** - * Whether this rendered target represents the Scratch stage. - * @type {boolean} - */ - this.isStage = false; - - /** - * Scratch X coordinate. Currently should range from -240 to 240. - * @type {Number} - */ - this.x = 0; - - /** - * Scratch Y coordinate. Currently should range from -180 to 180. - * @type {number} - */ - this.y = 0; - - /** - * Scratch direction. Currently should range from -179 to 180. - * @type {number} - */ - this.direction = 90; - - /** - * Whether the rendered target is draggable on the stage - * @type {boolean} - */ - this.draggable = false; - - /** - * Whether the rendered target is currently visible. - * @type {boolean} - */ - this.visible = true; - - /** - * Size of rendered target as a percent of costume size. - * @type {number} - */ - this.size = 100; - - /** - * Currently selected costume index. - * @type {number} - */ - this.currentCostume = 0; - - /** - * Current rotation style. - * @type {!string} - */ - this.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND; - - /** - * Loudness for sound playback for this target, as a percentage. - * @type {number} - */ - this.volume = 100; - - /** - * Current tempo (used by the music extension). - * This property is global to the project and stored in the stage. - * @type {number} - */ - this.tempo = 60; - - /** - * The transparency of the video (used by extensions with camera input). - * This property is global to the project and stored in the stage. - * @type {number} - */ - this.videoTransparency = 50; - - /** - * The state of the video input (used by extensions with camera input). - * This property is global to the project and stored in the stage. - * - * Defaults to ON. This setting does not turn the video by itself. A - * video extension once loaded will set the video device to this - * setting. Set to ON when a video extension is added in the editor the - * video will start ON. If the extension is loaded as part of loading a - * saved project the extension will see the value set when the stage - * was loaded from the saved values including the video state. - * - * @type {string} - */ - this.videoState = RenderedTarget.VIDEO_STATE.ON; - - /** - * The language to use for speech synthesis, in the text2speech extension. - * It is initialized to null so that on extension load, we can check for - * this and try setting it using the editor locale. - * @type {string} - */ - this.textToSpeechLanguage = null; - } - - /** - * Create a drawable with the this.renderer. - * @param {boolean} layerGroup The layer group this drawable should be added to - */ - initDrawable (layerGroup) { - if (this.renderer) { - this.drawableID = this.renderer.createDrawable(layerGroup); - } - // If we're a clone, start the hats. - if (!this.isOriginal) { - this.runtime.startHats( - 'control_start_as_clone', null, this - ); - } - } - - get audioPlayer () { - /* eslint-disable no-console */ - console.warn('get audioPlayer deprecated, please update to use .sprite.soundBank methods'); - console.warn(new Error('stack for debug').stack); - /* eslint-enable no-console */ - const bank = this.sprite.soundBank; - const audioPlayerProxy = { - playSound: soundId => bank.play(this, soundId) - }; - - Object.defineProperty(this, 'audioPlayer', { - configurable: false, - enumerable: true, - writable: false, - value: audioPlayerProxy - }); - - return audioPlayerProxy; - } - - /** - * Initialize the audio player for this sprite or clone. - */ - initAudio () { - } - - /** - * Event which fires when a target moves. - * @type {string} - */ - static get EVENT_TARGET_MOVED () { - return 'TARGET_MOVED'; - } - - /** - * Event which fires when a target changes visually, for updating say bubbles. - * @type {string} - */ - static get EVENT_TARGET_VISUAL_CHANGE () { - return 'EVENT_TARGET_VISUAL_CHANGE'; - } - - /** - * Rotation style for "all around"/spinning. - * @type {string} - */ - static get ROTATION_STYLE_ALL_AROUND () { - return 'all around'; - } - - /** - * Rotation style for "left-right"/flipping. - * @type {string} - */ - static get ROTATION_STYLE_LEFT_RIGHT () { - return 'left-right'; - } - - /** - * Rotation style for "no rotation." - * @type {string} - */ - static get ROTATION_STYLE_NONE () { - return "don't rotate"; - } - - /** - * Available states for video input. - * @enum {string} - */ - static get VIDEO_STATE () { - return { - OFF: 'off', - ON: 'on', - ON_FLIPPED: 'on-flipped' - }; - } - - /** - * Set the X and Y coordinates. - * @param {!number} x New X coordinate, in Scratch coordinates. - * @param {!number} y New Y coordinate, in Scratch coordinates. - * @param {?boolean} force Force setting X/Y, in case of dragging - */ - setXY (x, y, force) { - if (this.isStage) return; - if (this.dragging && !force) return; - const oldX = this.x; - const oldY = this.y; - if (this.renderer) { - const position = this.renderer.getFencedPositionOfDrawable(this.drawableID, [x, y]); - this.x = position[0]; - this.y = position[1]; - - this.renderer.updateDrawablePosition(this.drawableID, position); - if (this.visible) { - this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); - this.runtime.requestRedraw(); - } - } else { - this.x = x; - this.y = y; - } - this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY, force); - this.runtime.requestTargetsUpdate(this); - } - - /** - * Get the rendered direction and scale, after applying rotation style. - * @return {object} Direction and scale to render. - */ - _getRenderedDirectionAndScale () { - // Default: no changes to `this.direction` or `this.scale`. - let finalDirection = this.direction; - let finalScale = [this.size, this.size]; - if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_NONE) { - // Force rendered direction to be 90. - finalDirection = 90; - } else if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_LEFT_RIGHT) { - // Force rendered direction to be 90, and flip drawable if needed. - finalDirection = 90; - const scaleFlip = (this.direction < 0) ? -1 : 1; - finalScale = [scaleFlip * this.size, this.size]; - } - return {direction: finalDirection, scale: finalScale}; - } - - /** - * Set the direction. - * @param {!number} direction New direction. - */ - setDirection (direction) { - if (this.isStage) { - return; - } - if (!isFinite(direction)) { - return; - } - // Keep direction between -179 and +180. - this.direction = MathUtil.wrapClamp(direction, -179, 180); - if (this.renderer) { - const {direction: renderedDirection, scale} = this._getRenderedDirectionAndScale(); - this.renderer.updateDrawableDirectionScale(this.drawableID, renderedDirection, scale); - if (this.visible) { - this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); - this.runtime.requestRedraw(); - } - } - this.runtime.requestTargetsUpdate(this); - } - - /** - * Set draggability; i.e., whether it's able to be dragged in the player - * @param {!boolean} draggable True if should be draggable. - */ - setDraggable (draggable) { - if (this.isStage) return; - this.draggable = !!draggable; - this.runtime.requestTargetsUpdate(this); - } - - /** - * Set visibility; i.e., whether it's shown or hidden. - * @param {!boolean} visible True if should be shown. - */ - setVisible (visible) { - if (this.isStage) { - return; - } - this.visible = !!visible; - if (this.renderer) { - this.renderer.updateDrawableVisible(this.drawableID, this.visible); - if (this.visible) { - this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); - this.runtime.requestRedraw(); - } - } - this.runtime.requestTargetsUpdate(this); - } - - /** - * Set size, as a percentage of the costume size. - * @param {!number} size Size of rendered target, as % of costume size. - */ - setSize (size) { - if (this.isStage) { - return; - } - if (this.renderer) { - // Clamp to scales relative to costume and stage size. - // See original ScratchSprite.as:setSize. - const costumeSize = this.renderer.getCurrentSkinSize(this.drawableID); - const origW = costumeSize[0]; - const origH = costumeSize[1]; - const minScale = Math.min(1, Math.max(5 / origW, 5 / origH)); - const maxScale = Math.min( - (1.5 * this.runtime.constructor.STAGE_WIDTH) / origW, - (1.5 * this.runtime.constructor.STAGE_HEIGHT) / origH - ); - this.size = MathUtil.clamp(size / 100, minScale, maxScale) * 100; - const {direction, scale} = this._getRenderedDirectionAndScale(); - this.renderer.updateDrawableDirectionScale(this.drawableID, direction, scale); - if (this.visible) { - this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); - this.runtime.requestRedraw(); - } - } - this.runtime.requestTargetsUpdate(this); - } - - /** - * Set a particular graphic effect value. - * @param {!string} effectName Name of effect (see `RenderedTarget.prototype.effects`). - * @param {!number} value Numerical magnitude of effect. - */ - setEffect (effectName, value) { - if (!this.effects.hasOwnProperty(effectName)) return; - this.effects[effectName] = value; - if (this.renderer) { - this.renderer.updateDrawableEffect(this.drawableID, effectName, value); - if (this.visible) { - this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); - this.runtime.requestRedraw(); - } - } - } - - /** - * Clear all graphic effects on this rendered target. - */ - clearEffects () { - for (const effectName in this.effects) { - if (!this.effects.hasOwnProperty(effectName)) continue; - this.effects[effectName] = 0; - } - if (this.renderer) { - for (const effectName in this.effects) { - if (!this.effects.hasOwnProperty(effectName)) continue; - this.renderer.updateDrawableEffect(this.drawableID, effectName, 0); - } - if (this.visible) { - this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); - this.runtime.requestRedraw(); - } - } - } - - /** - * Set the current costume. - * @param {number} index New index of costume. - */ - setCostume (index) { - // Keep the costume index within possible values. - index = Math.round(index); - if ([Infinity, -Infinity, NaN].includes(index)) index = 0; - - this.currentCostume = MathUtil.wrapClamp( - index, 0, this.sprite.costumes.length - 1 - ); - if (this.renderer) { - const costume = this.getCostumes()[this.currentCostume]; - this.renderer.updateDrawableSkinId(this.drawableID, costume.skinId); - - if (this.visible) { - this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); - this.runtime.requestRedraw(); - } - } - this.runtime.requestTargetsUpdate(this); - } - - /** - * Add a costume, taking care to avoid duplicate names. - * @param {!object} costumeObject Object representing the costume. - * @param {?int} index Index at which to add costume - */ - addCostume (costumeObject, index) { - if (typeof index === 'number' && !isNaN(index)) { - this.sprite.addCostumeAt(costumeObject, index); - } else { - this.sprite.addCostumeAt(costumeObject, this.sprite.costumes.length); - } - } - - /** - * Rename a costume, taking care to avoid duplicate names. - * @param {int} costumeIndex - the index of the costume to be renamed. - * @param {string} newName - the desired new name of the costume (will be modified if already in use). - */ - renameCostume (costumeIndex, newName) { - const usedNames = this.sprite.costumes - .filter((costume, index) => costumeIndex !== index) - .map(costume => costume.name); - const oldName = this.getCostumes()[costumeIndex].name; - const newUnusedName = StringUtil.unusedName(newName, usedNames); - this.getCostumes()[costumeIndex].name = newUnusedName; - - if (this.isStage) { - // Since this is a backdrop, go through all targets and - // update any blocks referencing the old backdrop name - const targets = this.runtime.targets; - for (let i = 0; i < targets.length; i++) { - const currTarget = targets[i]; - currTarget.blocks.updateAssetName(oldName, newUnusedName, 'backdrop'); - } - } else { - this.blocks.updateAssetName(oldName, newUnusedName, 'costume'); - } - - } - - /** - * Delete a costume by index. - * @param {number} index Costume index to be deleted - * @return {?object} The costume that was deleted or null - * if the index was out of bounds of the costumes list or - * this target only has one costume. - */ - deleteCostume (index) { - const originalCostumeCount = this.sprite.costumes.length; - if (originalCostumeCount === 1) return null; - - if (index < 0 || index >= originalCostumeCount) { - return null; - } - - const deletedCostume = this.sprite.deleteCostumeAt(index); - - if (index === this.currentCostume && index === originalCostumeCount - 1) { - this.setCostume(index - 1); - } else if (index < this.currentCostume) { - this.setCostume(this.currentCostume - 1); - } else { - this.setCostume(this.currentCostume); - } - - this.runtime.requestTargetsUpdate(this); - return deletedCostume; - } - - /** - * Add a sound, taking care to avoid duplicate names. - * @param {!object} soundObject Object representing the sound. - * @param {?int} index Index at which to add costume - */ - addSound (soundObject, index) { - const usedNames = this.sprite.sounds.map(sound => sound.name); - soundObject.name = StringUtil.unusedName(soundObject.name, usedNames); - if (typeof index === 'number' && !isNaN(index)) { - this.sprite.sounds.splice(index, 0, soundObject); - } else { - this.sprite.sounds.push(soundObject); - } - } - - /** - * Rename a sound, taking care to avoid duplicate names. - * @param {int} soundIndex - the index of the sound to be renamed. - * @param {string} newName - the desired new name of the sound (will be modified if already in use). - */ - renameSound (soundIndex, newName) { - const usedNames = this.sprite.sounds - .filter((sound, index) => soundIndex !== index) - .map(sound => sound.name); - const oldName = this.sprite.sounds[soundIndex].name; - const newUnusedName = StringUtil.unusedName(newName, usedNames); - this.sprite.sounds[soundIndex].name = newUnusedName; - this.blocks.updateAssetName(oldName, newUnusedName, 'sound'); - } - - /** - * Delete a sound by index. - * @param {number} index Sound index to be deleted - * @return {object} The deleted sound object, or null if no sound was deleted. - */ - deleteSound (index) { - // Make sure the sound index is not out of bounds - if (index < 0 || index >= this.sprite.sounds.length) { - return null; - } - // Delete the sound at the given index - const deletedSound = this.sprite.sounds.splice(index, 1)[0]; - this.runtime.requestTargetsUpdate(this); - return deletedSound; - } - - /** - * Update the rotation style. - * @param {!string} rotationStyle New rotation style. - */ - setRotationStyle (rotationStyle) { - if (rotationStyle === RenderedTarget.ROTATION_STYLE_NONE) { - this.rotationStyle = RenderedTarget.ROTATION_STYLE_NONE; - } else if (rotationStyle === RenderedTarget.ROTATION_STYLE_ALL_AROUND) { - this.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND; - } else if (rotationStyle === RenderedTarget.ROTATION_STYLE_LEFT_RIGHT) { - this.rotationStyle = RenderedTarget.ROTATION_STYLE_LEFT_RIGHT; - } - if (this.renderer) { - const {direction, scale} = this._getRenderedDirectionAndScale(); - this.renderer.updateDrawableDirectionScale(this.drawableID, direction, scale); - if (this.visible) { - this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); - this.runtime.requestRedraw(); - } - } - this.runtime.requestTargetsUpdate(this); - } - - /** - * Get a costume index of this rendered target, by name of the costume. - * @param {?string} costumeName Name of a costume. - * @return {number} Index of the named costume, or -1 if not present. - */ - getCostumeIndexByName (costumeName) { - for (let i = 0; i < this.sprite.costumes.length; i++) { - if (this.getCostumes()[i].name === costumeName) { - return i; - } - } - return -1; - } - - /** - * Get a costume of this rendered target by id. - * @return {object} current costume - */ - getCurrentCostume () { - return this.getCostumes()[this.currentCostume]; - } - - /** - * Get full costume list - * @return {object[]} list of costumes - */ - getCostumes () { - return this.sprite.costumes; - } - - /** - * Reorder costume list by moving costume at costumeIndex to newIndex. - * @param {!number} costumeIndex Index of the costume to move. - * @param {!number} newIndex New index for that costume. - * @returns {boolean} If a change occurred (i.e. if the indices do not match) - */ - reorderCostume (costumeIndex, newIndex) { - newIndex = MathUtil.clamp(newIndex, 0, this.sprite.costumes.length - 1); - costumeIndex = MathUtil.clamp(costumeIndex, 0, this.sprite.costumes.length - 1); - - if (newIndex === costumeIndex) return false; - - const currentCostume = this.getCurrentCostume(); - const costume = this.sprite.costumes[costumeIndex]; - - // Use the sprite method for deleting costumes because setCostume is handled manually - this.sprite.deleteCostumeAt(costumeIndex); - - this.addCostume(costume, newIndex); - this.currentCostume = this.getCostumeIndexByName(currentCostume.name); - return true; - } - - /** - * Reorder sound list by moving sound at soundIndex to newIndex. - * @param {!number} soundIndex Index of the sound to move. - * @param {!number} newIndex New index for that sound. - * @returns {boolean} If a change occurred (i.e. if the indices do not match) - */ - reorderSound (soundIndex, newIndex) { - newIndex = MathUtil.clamp(newIndex, 0, this.sprite.sounds.length - 1); - soundIndex = MathUtil.clamp(soundIndex, 0, this.sprite.sounds.length - 1); - - if (newIndex === soundIndex) return false; - - const sound = this.sprite.sounds[soundIndex]; - this.deleteSound(soundIndex); - this.addSound(sound, newIndex); - return true; - } - - /** - * Get full sound list - * @return {object[]} list of sounds - */ - getSounds () { - return this.sprite.sounds; - } - - /** - * Update all drawable properties for this rendered target. - * Use when a batch has changed, e.g., when the drawable is first created. - */ - updateAllDrawableProperties () { - if (this.renderer) { - const {direction, scale} = this._getRenderedDirectionAndScale(); - this.renderer.updateDrawablePosition(this.drawableID, [this.x, this.y]); - this.renderer.updateDrawableDirectionScale(this.drawableID, direction, scale); - this.renderer.updateDrawableVisible(this.drawableID, this.visible); - - const costume = this.getCostumes()[this.currentCostume]; - this.renderer.updateDrawableSkinId(this.drawableID, costume.skinId); - - for (const effectName in this.effects) { - if (!this.effects.hasOwnProperty(effectName)) continue; - this.renderer.updateDrawableEffect(this.drawableID, effectName, this.effects[effectName]); - } - - if (this.visible) { - this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); - this.runtime.requestRedraw(); - } - } - this.runtime.requestTargetsUpdate(this); - } - - /** - * Return the human-readable name for this rendered target, e.g., the sprite's name. - * @override - * @returns {string} Human-readable name. - */ - getName () { - return this.sprite.name; - } - - /** - * Return whether this rendered target is a sprite (not a clone, not the stage). - * @return {boolean} True if not a clone and not the stage. - */ - isSprite () { - return !this.isStage && this.isOriginal; - } - - /** - * Return the rendered target's tight bounding box. - * Includes top, left, bottom, right attributes in Scratch coordinates. - * @return {?object} Tight bounding box, or null. - */ - getBounds () { - if (this.renderer) { - return this.runtime.renderer.getBounds(this.drawableID); - } - return null; - } - - /** - * Return the bounding box around a slice of the top 8px of the rendered target. - * Includes top, left, bottom, right attributes in Scratch coordinates. - * @return {?object} Tight bounding box, or null. - */ - getBoundsForBubble () { - if (this.renderer) { - return this.runtime.renderer.getBoundsForBubble(this.drawableID); - } - return null; - } - - /** - * Return whether this target is touching the mouse, an edge, or a sprite. - * @param {string} requestedObject an id for mouse or edge, or a sprite name. - * @return {boolean} True if the sprite is touching the object. - */ - isTouchingObject (requestedObject) { - if (requestedObject === '_mouse_') { - if (!this.runtime.ioDevices.mouse) return false; - const mouseX = this.runtime.ioDevices.mouse.getClientX(); - const mouseY = this.runtime.ioDevices.mouse.getClientY(); - return this.isTouchingPoint(mouseX, mouseY); - } else if (requestedObject === '_edge_') { - return this.isTouchingEdge(); - } - return this.isTouchingSprite(requestedObject); - } - - /** - * Return whether touching a point. - * @param {number} x X coordinate of test point. - * @param {number} y Y coordinate of test point. - * @return {boolean} True iff the rendered target is touching the point. - */ - isTouchingPoint (x, y) { - if (this.renderer) { - return this.renderer.drawableTouching(this.drawableID, x, y); - } - return false; - } - - /** - * Return whether touching a stage edge. - * @return {boolean} True iff the rendered target is touching the stage edge. - */ - isTouchingEdge () { - if (this.renderer) { - const stageWidth = this.runtime.constructor.STAGE_WIDTH; - const stageHeight = this.runtime.constructor.STAGE_HEIGHT; - const bounds = this.getBounds(); - if (bounds.left < -stageWidth / 2 || - bounds.right > stageWidth / 2 || - bounds.top > stageHeight / 2 || - bounds.bottom < -stageHeight / 2) { - return true; - } - } - return false; - } - - /** - * Return whether touching any of a named sprite's clones. - * @param {string} spriteName Name of the sprite. - * @return {boolean} True iff touching a clone of the sprite. - */ - isTouchingSprite (spriteName) { - spriteName = Cast.toString(spriteName); - const firstClone = this.runtime.getSpriteTargetByName(spriteName); - if (!firstClone || !this.renderer) { - return false; - } - // Filter out dragging targets. This means a sprite that is being dragged - // can detect other sprites using touching , but cannot be detected - // by other sprites while it is being dragged. This matches Scratch 2.0 behavior. - const drawableCandidates = firstClone.sprite.clones.filter(clone => !clone.dragging) - .map(clone => clone.drawableID); - return this.renderer.isTouchingDrawables( - this.drawableID, drawableCandidates); - } - - /** - * Return whether touching a color. - * @param {Array.} rgb [r,g,b], values between 0-255. - * @return {Promise.} True iff the rendered target is touching the color. - */ - isTouchingColor (rgb) { - if (this.renderer) { - return this.renderer.isTouchingColor(this.drawableID, rgb); - } - return false; - } - - /** - * Return whether rendered target's color is touching a color. - * @param {object} targetRgb {Array.} [r,g,b], values between 0-255. - * @param {object} maskRgb {Array.} [r,g,b], values between 0-255. - * @return {Promise.} True iff the color is touching the color. - */ - colorIsTouchingColor (targetRgb, maskRgb) { - if (this.renderer) { - return this.renderer.isTouchingColor( - this.drawableID, - targetRgb, - maskRgb - ); - } - return false; - } - - getLayerOrder () { - if (this.renderer) { - return this.renderer.getDrawableOrder(this.drawableID); - } - return null; - } - - /** - * Move to the front layer. - */ - goToFront () { // This should only ever be used for sprites - if (this.renderer) { - // Let the renderer re-order the sprite based on its knowledge - // of what layers are present - this.renderer.setDrawableOrder(this.drawableID, Infinity, StageLayering.SPRITE_LAYER); - } - - this.runtime.setExecutablePosition(this, Infinity); - } - - /** - * Move to the back layer. - */ - goToBack () { // This should only ever be used for sprites - if (this.renderer) { - // Let the renderer re-order the sprite based on its knowledge - // of what layers are present - this.renderer.setDrawableOrder(this.drawableID, -Infinity, StageLayering.SPRITE_LAYER, false); - } - - this.runtime.setExecutablePosition(this, -Infinity); - } - - /** - * Move forward a number of layers. - * @param {number} nLayers How many layers to go forward. - */ - goForwardLayers (nLayers) { - if (this.renderer) { - this.renderer.setDrawableOrder(this.drawableID, nLayers, StageLayering.SPRITE_LAYER, true); - } - - this.runtime.moveExecutable(this, nLayers); - } - - /** - * Move backward a number of layers. - * @param {number} nLayers How many layers to go backward. - */ - goBackwardLayers (nLayers) { - if (this.renderer) { - this.renderer.setDrawableOrder(this.drawableID, -nLayers, StageLayering.SPRITE_LAYER, true); - } - - this.runtime.moveExecutable(this, -nLayers); - } - - /** - * Move behind some other rendered target. - * @param {!RenderedTarget} other Other rendered target to move behind. - */ - goBehindOther (other) { - if (this.renderer) { - const otherLayer = this.renderer.setDrawableOrder( - other.drawableID, 0, StageLayering.SPRITE_LAYER, true); - this.renderer.setDrawableOrder(this.drawableID, otherLayer, StageLayering.SPRITE_LAYER); - } - - const executionPosition = this.runtime.executableTargets.indexOf(other); - this.runtime.setExecutablePosition(this, executionPosition); - } - - /** - * Keep a desired position within a fence. - * @param {number} newX New desired X position. - * @param {number} newY New desired Y position. - * @param {object=} optFence Optional fence with left, right, top bottom. - * @return {Array.} Fenced X and Y coordinates. - */ - keepInFence (newX, newY, optFence) { - let fence = optFence; - if (!fence) { - fence = { - left: -this.runtime.constructor.STAGE_WIDTH / 2, - right: this.runtime.constructor.STAGE_WIDTH / 2, - top: this.runtime.constructor.STAGE_HEIGHT / 2, - bottom: -this.runtime.constructor.STAGE_HEIGHT / 2 - }; - } - const bounds = this.getBounds(); - if (!bounds) return; - // Adjust the known bounds to the target position. - bounds.left += (newX - this.x); - bounds.right += (newX - this.x); - bounds.top += (newY - this.y); - bounds.bottom += (newY - this.y); - // Find how far we need to move the target position. - let dx = 0; - let dy = 0; - if (bounds.left < fence.left) { - dx += fence.left - bounds.left; - } - if (bounds.right > fence.right) { - dx += fence.right - bounds.right; - } - if (bounds.top > fence.top) { - dy += fence.top - bounds.top; - } - if (bounds.bottom < fence.bottom) { - dy += fence.bottom - bounds.bottom; - } - return [newX + dx, newY + dy]; - } - - /** - * Make a clone, copying any run-time properties. - * If we've hit the global clone limit, returns null. - * @return {RenderedTarget} New clone. - */ - makeClone () { - if (!this.runtime.clonesAvailable() || this.isStage) { - return null; // Hit max clone limit, or this is the stage. - } - this.runtime.changeCloneCounter(1); - const newClone = this.sprite.createClone(); - // Copy all properties. - newClone.x = this.x; - newClone.y = this.y; - newClone.direction = this.direction; - newClone.draggable = this.draggable; - newClone.visible = this.visible; - newClone.size = this.size; - newClone.currentCostume = this.currentCostume; - newClone.rotationStyle = this.rotationStyle; - newClone.effects = Clone.simple(this.effects); - newClone.variables = this.duplicateVariables(); - newClone._edgeActivatedHatValues = Clone.simple(this._edgeActivatedHatValues); - newClone.initDrawable(StageLayering.SPRITE_LAYER); - newClone.updateAllDrawableProperties(); - return newClone; - } - - /** - * Make a duplicate using a duplicate sprite. - * @return {RenderedTarget} New clone. - */ - duplicate () { - return this.sprite.duplicate().then(newSprite => { - const newTarget = newSprite.createClone(); - // Copy all properties. - // @todo refactor with clone methods - newTarget.x = (Math.random() - 0.5) * 400 / 2; - newTarget.y = (Math.random() - 0.5) * 300 / 2; - newTarget.direction = this.direction; - newTarget.draggable = this.draggable; - newTarget.visible = this.visible; - newTarget.size = this.size; - newTarget.currentCostume = this.currentCostume; - newTarget.rotationStyle = this.rotationStyle; - newTarget.effects = JSON.parse(JSON.stringify(this.effects)); - newTarget.variables = this.duplicateVariables(newTarget.blocks); - newTarget.updateAllDrawableProperties(); - return newTarget; - }); - } - - /** - * Called when the project receives a "green flag." - * For a rendered target, this clears graphic effects. - */ - onGreenFlag () { - this.clearEffects(); - } - - /** - * Called when the project receives a "stop all" - * Stop all sounds and clear graphic effects. - */ - onStopAll () { - this.clearEffects(); - } - - /** - * Post/edit sprite info. - * @param {object} data An object with sprite info data to set. - */ - postSpriteInfo (data) { - const force = data.hasOwnProperty('force') ? data.force : null; - const isXChanged = data.hasOwnProperty('x'); - const isYChanged = data.hasOwnProperty('y'); - if (isXChanged || isYChanged) { - this.setXY(isXChanged ? data.x : this.x, isYChanged ? data.y : this.y, force); - } - if (data.hasOwnProperty('direction')) { - this.setDirection(data.direction); - } - if (data.hasOwnProperty('draggable')) { - this.setDraggable(data.draggable); - } - if (data.hasOwnProperty('rotationStyle')) { - this.setRotationStyle(data.rotationStyle); - } - if (data.hasOwnProperty('visible')) { - this.setVisible(data.visible); - } - if (data.hasOwnProperty('size')) { - this.setSize(data.size); - } - } - - /** - * Put the sprite into the drag state. While in effect, setXY must be forced - */ - startDrag () { - this.dragging = true; - } - - /** - * Remove the sprite from the drag state. - */ - stopDrag () { - this.dragging = false; - } - - - /** - * Serialize sprite info, used when emitting events about the sprite - * @returns {object} Sprite data as a simple object - */ - toJSON () { - const costumes = this.getCostumes(); - return { - id: this.id, - name: this.getName(), - isStage: this.isStage, - x: this.x, - y: this.y, - size: this.size, - direction: this.direction, - draggable: this.draggable, - currentCostume: this.currentCostume, - costume: costumes[this.currentCostume], - costumeCount: costumes.length, - visible: this.visible, - rotationStyle: this.rotationStyle, - comments: this.comments, - blocks: this.blocks._blocks, - variables: this.variables, - costumes: costumes, - sounds: this.getSounds(), - textToSpeechLanguage: this.textToSpeechLanguage, - tempo: this.tempo, - volume: this.volume, - videoTransparency: this.videoTransparency, - videoState: this.videoState - - }; - } - - /** - * Dispose, destroying any run-time properties. - */ - dispose () { - this.runtime.changeCloneCounter(-1); - this.runtime.stopForTarget(this); - this.runtime.removeExecutable(this); - this.sprite.removeClone(this); - if (this.renderer && this.drawableID !== null) { - this.renderer.destroyDrawable(this.drawableID, this.isStage ? - StageLayering.BACKGROUND_LAYER : - StageLayering.SPRITE_LAYER); - if (this.visible) { - this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); - this.runtime.requestRedraw(); - } - } - } -} - -module.exports = RenderedTarget; diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js deleted file mode 100644 index 5cec44c10c..0000000000 --- a/packages/scratch-vm/src/virtual-machine.js +++ /dev/null @@ -1,1548 +0,0 @@ -let _TextEncoder; -if (typeof TextEncoder === 'undefined') { - _TextEncoder = require('text-encoding').TextEncoder; -} else { - /* global TextEncoder */ - _TextEncoder = TextEncoder; -} -const EventEmitter = require('events'); -const JSZip = require('jszip'); - -const Buffer = require('buffer').Buffer; -const centralDispatch = require('./dispatch/central-dispatch'); -const ExtensionManager = require('./extension-support/extension-manager'); -const log = require('./util/log'); -const MathUtil = require('./util/math-util'); -const Runtime = require('./engine/runtime'); -const StringUtil = require('./util/string-util'); -const formatMessage = require('format-message'); - -const Variable = require('./engine/variable'); -const newBlockIds = require('./util/new-block-ids'); - -const {loadCostume} = require('./import/load-costume.js'); -const {loadSound} = require('./import/load-sound.js'); -const {serializeSounds, serializeCostumes} = require('./serialization/serialize-assets'); -require('canvas-toBlob'); - -const RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_']; - -const CORE_EXTENSIONS = [ - // 'motion', - // 'looks', - // 'sound', - // 'events', - // 'control', - // 'sensing', - // 'operators', - // 'variables', - // 'myBlocks' -]; - -/** - * Handles connections between blocks, stage, and extensions. - * @constructor - */ -class VirtualMachine extends EventEmitter { - constructor () { - super(); - - /** - * VM runtime, to store blocks, I/O devices, sprites/targets, etc. - * @type {!Runtime} - */ - this.runtime = new Runtime(); - centralDispatch.setService('runtime', this.runtime).catch(e => { - log.error(`Failed to register runtime service: ${JSON.stringify(e)}`); - }); - - /** - * The "currently editing"/selected target ID for the VM. - * Block events from any Blockly workspace are routed to this target. - * @type {Target} - */ - this.editingTarget = null; - - /** - * The currently dragging target, for redirecting IO data. - * @type {Target} - */ - this._dragTarget = null; - - // Runtime emits are passed along as VM emits. - this.runtime.on(Runtime.SCRIPT_GLOW_ON, glowData => { - this.emit(Runtime.SCRIPT_GLOW_ON, glowData); - }); - this.runtime.on(Runtime.SCRIPT_GLOW_OFF, glowData => { - this.emit(Runtime.SCRIPT_GLOW_OFF, glowData); - }); - this.runtime.on(Runtime.BLOCK_GLOW_ON, glowData => { - this.emit(Runtime.BLOCK_GLOW_ON, glowData); - }); - this.runtime.on(Runtime.BLOCK_GLOW_OFF, glowData => { - this.emit(Runtime.BLOCK_GLOW_OFF, glowData); - }); - this.runtime.on(Runtime.PROJECT_START, () => { - this.emit(Runtime.PROJECT_START); - }); - this.runtime.on(Runtime.PROJECT_RUN_START, () => { - this.emit(Runtime.PROJECT_RUN_START); - }); - this.runtime.on(Runtime.PROJECT_RUN_STOP, () => { - this.emit(Runtime.PROJECT_RUN_STOP); - }); - this.runtime.on(Runtime.PROJECT_CHANGED, () => { - this.emit(Runtime.PROJECT_CHANGED); - }); - this.runtime.on(Runtime.VISUAL_REPORT, visualReport => { - this.emit(Runtime.VISUAL_REPORT, visualReport); - }); - this.runtime.on(Runtime.TARGETS_UPDATE, emitProjectChanged => { - this.emitTargetsUpdate(emitProjectChanged); - }); - this.runtime.on(Runtime.MONITORS_UPDATE, monitorList => { - this.emit(Runtime.MONITORS_UPDATE, monitorList); - }); - this.runtime.on(Runtime.BLOCK_DRAG_UPDATE, areBlocksOverGui => { - this.emit(Runtime.BLOCK_DRAG_UPDATE, areBlocksOverGui); - }); - this.runtime.on(Runtime.BLOCK_DRAG_END, (blocks, topBlockId) => { - this.emit(Runtime.BLOCK_DRAG_END, blocks, topBlockId); - }); - this.runtime.on(Runtime.EXTENSION_ADDED, categoryInfo => { - this.emit(Runtime.EXTENSION_ADDED, categoryInfo); - }); - this.runtime.on(Runtime.EXTENSION_FIELD_ADDED, (fieldName, fieldImplementation) => { - this.emit(Runtime.EXTENSION_FIELD_ADDED, fieldName, fieldImplementation); - }); - this.runtime.on(Runtime.BLOCKSINFO_UPDATE, categoryInfo => { - this.emit(Runtime.BLOCKSINFO_UPDATE, categoryInfo); - }); - this.runtime.on(Runtime.BLOCKS_NEED_UPDATE, () => { - this.emitWorkspaceUpdate(); - }); - this.runtime.on(Runtime.TOOLBOX_EXTENSIONS_NEED_UPDATE, () => { - this.extensionManager.refreshBlocks(); - }); - this.runtime.on(Runtime.PERIPHERAL_LIST_UPDATE, info => { - this.emit(Runtime.PERIPHERAL_LIST_UPDATE, info); - }); - this.runtime.on(Runtime.USER_PICKED_PERIPHERAL, info => { - this.emit(Runtime.USER_PICKED_PERIPHERAL, info); - }); - this.runtime.on(Runtime.PERIPHERAL_CONNECTED, () => - this.emit(Runtime.PERIPHERAL_CONNECTED) - ); - this.runtime.on(Runtime.PERIPHERAL_REQUEST_ERROR, () => - this.emit(Runtime.PERIPHERAL_REQUEST_ERROR) - ); - this.runtime.on(Runtime.PERIPHERAL_DISCONNECTED, () => - this.emit(Runtime.PERIPHERAL_DISCONNECTED) - ); - this.runtime.on(Runtime.PERIPHERAL_CONNECTION_LOST_ERROR, data => - this.emit(Runtime.PERIPHERAL_CONNECTION_LOST_ERROR, data) - ); - this.runtime.on(Runtime.PERIPHERAL_SCAN_TIMEOUT, () => - this.emit(Runtime.PERIPHERAL_SCAN_TIMEOUT) - ); - this.runtime.on(Runtime.MIC_LISTENING, listening => { - this.emit(Runtime.MIC_LISTENING, listening); - }); - this.runtime.on(Runtime.RUNTIME_STARTED, () => { - this.emit(Runtime.RUNTIME_STARTED); - }); - this.runtime.on(Runtime.HAS_CLOUD_DATA_UPDATE, hasCloudData => { - this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, hasCloudData); - }); - - this.extensionManager = new ExtensionManager(this.runtime); - - // Load core extensions - for (const id of CORE_EXTENSIONS) { - this.extensionManager.loadExtensionIdSync(id); - } - - this.blockListener = this.blockListener.bind(this); - this.flyoutBlockListener = this.flyoutBlockListener.bind(this); - this.monitorBlockListener = this.monitorBlockListener.bind(this); - this.variableListener = this.variableListener.bind(this); - } - - /** - * Start running the VM - do this before anything else. - */ - start () { - this.runtime.start(); - } - - /** - * "Green flag" handler - start all threads starting with a green flag. - */ - greenFlag () { - this.runtime.greenFlag(); - } - - /** - * Set whether the VM is in "turbo mode." - * When true, loops don't yield to redraw. - * @param {boolean} turboModeOn Whether turbo mode should be set. - */ - setTurboMode (turboModeOn) { - this.runtime.turboMode = !!turboModeOn; - if (this.runtime.turboMode) { - this.emit(Runtime.TURBO_MODE_ON); - } else { - this.emit(Runtime.TURBO_MODE_OFF); - } - } - - /** - * Set whether the VM is in 2.0 "compatibility mode." - * When true, ticks go at 2.0 speed (30 TPS). - * @param {boolean} compatibilityModeOn Whether compatibility mode is set. - */ - setCompatibilityMode (compatibilityModeOn) { - this.runtime.setCompatibilityMode(!!compatibilityModeOn); - } - - /** - * Stop all threads and running activities. - */ - stopAll () { - this.runtime.stopAll(); - } - - /** - * Clear out current running project data. - */ - clear () { - this.runtime.dispose(); - this.editingTarget = null; - this.emitTargetsUpdate(false /* Don't emit project change */); - } - - /** - * Get data for playground. Data comes back in an emitted event. - */ - getPlaygroundData () { - const instance = this; - // Only send back thread data for the current editingTarget. - const threadData = this.runtime.threads.filter(thread => thread.target === instance.editingTarget); - // Remove the target key, since it's a circular reference. - const filteredThreadData = JSON.stringify(threadData, (key, value) => { - if (key === 'target' || key === 'blockContainer') return; - return value; - }, 2); - this.emit('playgroundData', { - blocks: this.editingTarget.blocks, - threads: filteredThreadData - }); - } - - /** - * Post I/O data to the virtual devices. - * @param {?string} device Name of virtual I/O device. - * @param {object} data Any data object to post to the I/O device. - */ - postIOData (device, data) { - if (this.runtime.ioDevices[device]) { - this.runtime.ioDevices[device].postData(data); - } - } - - setVideoProvider (videoProvider) { - this.runtime.ioDevices.video.setProvider(videoProvider); - } - - setCloudProvider (cloudProvider) { - this.runtime.ioDevices.cloud.setProvider(cloudProvider); - } - - /** - * Tell the specified extension to scan for a peripheral. - * @param {string} extensionId - the id of the extension. - */ - scanForPeripheral (extensionId) { - this.runtime.scanForPeripheral(extensionId); - } - - /** - * Connect to the extension's specified peripheral. - * @param {string} extensionId - the id of the extension. - * @param {number} peripheralId - the id of the peripheral. - */ - connectPeripheral (extensionId, peripheralId) { - this.runtime.connectPeripheral(extensionId, peripheralId); - } - - /** - * Disconnect from the extension's connected peripheral. - * @param {string} extensionId - the id of the extension. - */ - disconnectPeripheral (extensionId) { - this.runtime.disconnectPeripheral(extensionId); - } - - /** - * Returns whether the extension has a currently connected peripheral. - * @param {string} extensionId - the id of the extension. - * @return {boolean} - whether the extension has a connected peripheral. - */ - getPeripheralIsConnected (extensionId) { - return this.runtime.getPeripheralIsConnected(extensionId); - } - - /** - * Load a Scratch project from a .sb, .sb2, .sb3 or json string. - * @param {string | object} input A json string, object, or ArrayBuffer representing the project to load. - * @return {!Promise} Promise that resolves after targets are installed. - */ - loadProject (input) { - if (typeof input === 'object' && !(input instanceof ArrayBuffer) && - !ArrayBuffer.isView(input)) { - // If the input is an object and not any ArrayBuffer - // or an ArrayBuffer view (this includes all typed arrays and DataViews) - // turn the object into a JSON string, because we suspect - // this is a project.json as an object - // validate expects a string or buffer as input - // TODO not sure if we need to check that it also isn't a data view - input = JSON.stringify(input); - } - - const validationPromise = new Promise((resolve, reject) => { - const validate = require('scratch-parser'); - // The second argument of false below indicates to the validator that the - // input should be parsed/validated as an entire project (and not a single sprite) - validate(input, false, (error, res) => { - if (error) return reject(error); - resolve(res); - }); - }) - .catch(error => { - const {SB1File, ValidationError} = require('scratch-sb1-converter'); - - try { - const sb1 = new SB1File(input); - const json = sb1.json; - json.projectVersion = 2; - return Promise.resolve([json, sb1.zip]); - } catch (sb1Error) { - if (sb1Error instanceof ValidationError) { - // The input does not validate as a Scratch 1 file. - } else { - // The project appears to be a Scratch 1 file but it - // could not be successfully translated into a Scratch 2 - // project. - return Promise.reject(sb1Error); - } - } - // Throw original error since the input does not appear to be - // an SB1File. - return Promise.reject(error); - }); - - return validationPromise - .then(validatedInput => this.deserializeProject(validatedInput[0], validatedInput[1])) - .then(() => this.runtime.emitProjectLoaded()) - .catch(error => { - // Intentionally rejecting here (want errors to be handled by caller) - if (error.hasOwnProperty('validationError')) { - return Promise.reject(JSON.stringify(error)); - } - return Promise.reject(error); - }); - } - - /** - * Load a project from the Scratch web site, by ID. - * @param {string} id - the ID of the project to download, as a string. - */ - downloadProjectId (id) { - const storage = this.runtime.storage; - if (!storage) { - log.error('No storage module present; cannot load project: ', id); - return; - } - const vm = this; - const promise = storage.load(storage.AssetType.Project, id); - promise.then(projectAsset => { - vm.loadProject(projectAsset.data); - }); - } - - /** - * @returns {string} Project in a Scratch 3.0 JSON representation. - */ - saveProjectSb3 () { - const soundDescs = serializeSounds(this.runtime); - const costumeDescs = serializeCostumes(this.runtime); - const projectJson = this.toJSON(); - - // TODO want to eventually move zip creation out of here, and perhaps - // into scratch-storage - const zip = new JSZip(); - - // Put everything in a zip file - zip.file('project.json', projectJson); - this._addFileDescsToZip(soundDescs.concat(costumeDescs), zip); - - return zip.generateAsync({ - type: 'blob', - mimeType: 'application/x.scratch.sb3', - compression: 'DEFLATE', - compressionOptions: { - level: 6 // Tradeoff between best speed (1) and best compression (9) - } - }); - } - - /* - * @type {Array} Array of all costumes and sounds currently in the runtime - */ - get assets () { - return this.runtime.targets.reduce((acc, target) => ( - acc - .concat(target.sprite.sounds.map(sound => sound.asset)) - .concat(target.sprite.costumes.map(costume => costume.asset)) - ), []); - } - - _addFileDescsToZip (fileDescs, zip) { - for (let i = 0; i < fileDescs.length; i++) { - const currFileDesc = fileDescs[i]; - zip.file(currFileDesc.fileName, currFileDesc.fileContent); - } - } - - /** - * Exports a sprite in the sprite3 format. - * @param {string} targetId ID of the target to export - * @param {string=} optZipType Optional type that the resulting - * zip should be outputted in. Options are: base64, binarystring, - * array, uint8array, arraybuffer, blob, or nodebuffer. Defaults to - * blob if argument not provided. - * See https://stuk.github.io/jszip/documentation/api_jszip/generate_async.html#type-option - * for more information about these options. - * @return {object} A generated zip of the sprite and its assets in the format - * specified by optZipType or blob by default. - */ - exportSprite (targetId, optZipType) { - const sb3 = require('./serialization/sb3'); - - const soundDescs = serializeSounds(this.runtime, targetId); - const costumeDescs = serializeCostumes(this.runtime, targetId); - const spriteJson = StringUtil.stringify(sb3.serialize(this.runtime, targetId)); - - const zip = new JSZip(); - zip.file('sprite.json', spriteJson); - this._addFileDescsToZip(soundDescs.concat(costumeDescs), zip); - - return zip.generateAsync({ - type: typeof optZipType === 'string' ? optZipType : 'blob', - mimeType: 'application/x.scratch.sprite3', - compression: 'DEFLATE', - compressionOptions: { - level: 6 - } - }); - } - - /** - * Export project as a Scratch 3.0 JSON representation. - * @return {string} Serialized state of the runtime. - */ - toJSON () { - const sb3 = require('./serialization/sb3'); - return StringUtil.stringify(sb3.serialize(this.runtime)); - } - - // TODO do we still need this function? Keeping it here so as not to introduce - // a breaking change. - /** - * Load a project from a Scratch JSON representation. - * @param {string} json JSON string representing a project. - * @returns {Promise} Promise that resolves after the project has loaded - */ - fromJSON (json) { - log.warning('fromJSON is now just a wrapper around loadProject, please use that function instead.'); - return this.loadProject(json); - } - - /** - * Load a project from a Scratch JSON representation. - * @param {string} projectJSON JSON string representing a project. - * @param {?JSZip} zip Optional zipped project containing assets to be loaded. - * @returns {Promise} Promise that resolves after the project has loaded - */ - deserializeProject (projectJSON, zip) { - // Clear the current runtime - this.clear(); - - const runtime = this.runtime; - const deserializePromise = function () { - const projectVersion = projectJSON.projectVersion; - if (projectVersion === 2) { - const sb2 = require('./serialization/sb2'); - return sb2.deserialize(projectJSON, runtime, false, zip); - } - if (projectVersion === 3) { - const sb3 = require('./serialization/sb3'); - return sb3.deserialize(projectJSON, runtime, zip); - } - return Promise.reject('Unable to verify Scratch Project version.'); - }; - return deserializePromise() - .then(({targets, extensions}) => - this.installTargets(targets, extensions, true)); - } - - /** - * Install `deserialize` results: zero or more targets after the extensions (if any) used by those targets. - * @param {Array.} targets - the targets to be installed - * @param {ImportedExtensionsInfo} extensions - metadata about extensions used by these targets - * @param {boolean} wholeProject - set to true if installing a whole project, as opposed to a single sprite. - * @returns {Promise} resolved once targets have been installed - */ - installTargets (targets, extensions, wholeProject) { - const extensionPromises = []; - - extensions.extensionIDs.forEach(extensionID => { - if (!this.extensionManager.isExtensionLoaded(extensionID)) { - const extensionURL = extensions.extensionURLs.get(extensionID) || extensionID; - extensionPromises.push(this.extensionManager.loadExtensionURL(extensionURL)); - } - }); - - targets = targets.filter(target => !!target); - - return Promise.all(extensionPromises).then(() => { - targets.forEach(target => { - this.runtime.addTarget(target); - (/** @type RenderedTarget */ target).updateAllDrawableProperties(); - // Ensure unique sprite name - if (target.isSprite()) this.renameSprite(target.id, target.getName()); - }); - // Sort the executable targets by layerOrder. - // Remove layerOrder property after use. - this.runtime.executableTargets.sort((a, b) => a.layerOrder - b.layerOrder); - targets.forEach(target => { - delete target.layerOrder; - }); - - // Select the first target for editing, e.g., the first sprite. - if (wholeProject && (targets.length > 1)) { - this.editingTarget = targets[1]; - } else { - this.editingTarget = targets[0]; - } - - if (!wholeProject) { - this.editingTarget.fixUpVariableReferences(); - } - - // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(false /* Don't emit project change */); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); - this.runtime.ioDevices.cloud.setStage(this.runtime.getTargetForStage()); - }); - } - - /** - * Add a sprite, this could be .sprite2 or .sprite3. Unpack and validate - * such a file first. - * @param {string | object} input A json string, object, or ArrayBuffer representing the project to load. - * @return {!Promise} Promise that resolves after targets are installed. - */ - addSprite (input) { - const errorPrefix = 'Sprite Upload Error:'; - if (typeof input === 'object' && !(input instanceof ArrayBuffer) && - !ArrayBuffer.isView(input)) { - // If the input is an object and not any ArrayBuffer - // or an ArrayBuffer view (this includes all typed arrays and DataViews) - // turn the object into a JSON string, because we suspect - // this is a project.json as an object - // validate expects a string or buffer as input - // TODO not sure if we need to check that it also isn't a data view - input = JSON.stringify(input); - } - - const validationPromise = new Promise((resolve, reject) => { - const validate = require('scratch-parser'); - // The second argument of true below indicates to the parser/validator - // that the given input should be treated as a single sprite and not - // an entire project - validate(input, true, (error, res) => { - if (error) return reject(error); - resolve(res); - }); - }); - - return validationPromise - .then(validatedInput => { - const projectVersion = validatedInput[0].projectVersion; - if (projectVersion === 2) { - return this._addSprite2(validatedInput[0], validatedInput[1]); - } - if (projectVersion === 3) { - return this._addSprite3(validatedInput[0], validatedInput[1]); - } - return Promise.reject(`${errorPrefix} Unable to verify sprite version.`); - }) - .then(() => this.runtime.emitProjectChanged()) - .catch(error => { - // Intentionally rejecting here (want errors to be handled by caller) - if (error.hasOwnProperty('validationError')) { - return Promise.reject(JSON.stringify(error)); - } - return Promise.reject(`${errorPrefix} ${error}`); - }); - } - - /** - * Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format. - * @param {object} sprite Object representing 2.0 sprite to be added. - * @param {?ArrayBuffer} zip Optional zip of assets being referenced by json - * @returns {Promise} Promise that resolves after the sprite is added - */ - _addSprite2 (sprite, zip) { - // Validate & parse - - const sb2 = require('./serialization/sb2'); - return sb2.deserialize(sprite, this.runtime, true, zip) - .then(({targets, extensions}) => - this.installTargets(targets, extensions, false)); - } - - /** - * Add a single sb3 sprite. - * @param {object} sprite Object rperesenting 3.0 sprite to be added. - * @param {?ArrayBuffer} zip Optional zip of assets being referenced by target json - * @returns {Promise} Promise that resolves after the sprite is added - */ - _addSprite3 (sprite, zip) { - // Validate & parse - const sb3 = require('./serialization/sb3'); - return sb3 - .deserialize(sprite, this.runtime, zip, true) - .then(({targets, extensions}) => this.installTargets(targets, extensions, false)); - } - - /** - * Add a costume to the current editing target. - * @param {string} md5ext - the MD5 and extension of the costume to be loaded. - * @param {!object} costumeObject Object representing the costume. - * @property {int} skinId - the ID of the costume's render skin, once installed. - * @property {number} rotationCenterX - the X component of the costume's origin. - * @property {number} rotationCenterY - the Y component of the costume's origin. - * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume. - * @param {string} optTargetId - the id of the target to add to, if not the editing target. - * @param {string} optVersion - if this is 2, load costume as sb2, otherwise load costume as sb3. - * @returns {?Promise} - a promise that resolves when the costume has been added - */ - addCostume (md5ext, costumeObject, optTargetId, optVersion) { - const target = optTargetId ? this.runtime.getTargetById(optTargetId) : - this.editingTarget; - if (target) { - return loadCostume(md5ext, costumeObject, this.runtime, optVersion).then(() => { - target.addCostume(costumeObject); - target.setCostume( - target.getCostumes().length - 1 - ); - this.runtime.emitProjectChanged(); - }); - } - // If the target cannot be found by id, return a rejected promise - return Promise.reject(); - } - - /** - * Add a costume loaded from the library to the current editing target. - * @param {string} md5ext - the MD5 and extension of the costume to be loaded. - * @param {!object} costumeObject Object representing the costume. - * @property {int} skinId - the ID of the costume's render skin, once installed. - * @property {number} rotationCenterX - the X component of the costume's origin. - * @property {number} rotationCenterY - the Y component of the costume's origin. - * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume. - * @returns {?Promise} - a promise that resolves when the costume has been added - */ - addCostumeFromLibrary (md5ext, costumeObject) { - if (!this.editingTarget) return Promise.reject(); - return this.addCostume(md5ext, costumeObject, this.editingTarget.id, 2 /* optVersion */); - } - - /** - * Duplicate the costume at the given index. Add it at that index + 1. - * @param {!int} costumeIndex Index of costume to duplicate - * @returns {?Promise} - a promise that resolves when the costume has been decoded and added - */ - duplicateCostume (costumeIndex) { - const originalCostume = this.editingTarget.getCostumes()[costumeIndex]; - const clone = Object.assign({}, originalCostume); - const md5ext = `${clone.assetId}.${clone.dataFormat}`; - return loadCostume(md5ext, clone, this.runtime).then(() => { - this.editingTarget.addCostume(clone, costumeIndex + 1); - this.editingTarget.setCostume(costumeIndex + 1); - this.emitTargetsUpdate(); - }); - } - - /** - * Duplicate the sound at the given index. Add it at that index + 1. - * @param {!int} soundIndex Index of sound to duplicate - * @returns {?Promise} - a promise that resolves when the sound has been decoded and added - */ - duplicateSound (soundIndex) { - const originalSound = this.editingTarget.getSounds()[soundIndex]; - const clone = Object.assign({}, originalSound); - return loadSound(clone, this.runtime, this.editingTarget.sprite.soundBank).then(() => { - this.editingTarget.addSound(clone, soundIndex + 1); - this.emitTargetsUpdate(); - }); - } - - /** - * Rename a costume on the current editing target. - * @param {int} costumeIndex - the index of the costume to be renamed. - * @param {string} newName - the desired new name of the costume (will be modified if already in use). - */ - renameCostume (costumeIndex, newName) { - this.editingTarget.renameCostume(costumeIndex, newName); - this.emitTargetsUpdate(); - } - - /** - * Delete a costume from the current editing target. - * @param {int} costumeIndex - the index of the costume to be removed. - * @return {?function} A function to restore the deleted costume, or null, - * if no costume was deleted. - */ - deleteCostume (costumeIndex) { - const deletedCostume = this.editingTarget.deleteCostume(costumeIndex); - if (deletedCostume) { - const target = this.editingTarget; - this.runtime.emitProjectChanged(); - return () => { - target.addCostume(deletedCostume); - this.emitTargetsUpdate(); - }; - } - return null; - } - - /** - * Add a sound to the current editing target. - * @param {!object} soundObject Object representing the costume. - * @param {string} optTargetId - the id of the target to add to, if not the editing target. - * @returns {?Promise} - a promise that resolves when the sound has been decoded and added - */ - addSound (soundObject, optTargetId) { - const target = optTargetId ? this.runtime.getTargetById(optTargetId) : - this.editingTarget; - if (target) { - return loadSound(soundObject, this.runtime, target.sprite.soundBank).then(() => { - target.addSound(soundObject); - this.emitTargetsUpdate(); - }); - } - // If the target cannot be found by id, return a rejected promise - return new Promise.reject(); - } - - /** - * Rename a sound on the current editing target. - * @param {int} soundIndex - the index of the sound to be renamed. - * @param {string} newName - the desired new name of the sound (will be modified if already in use). - */ - renameSound (soundIndex, newName) { - this.editingTarget.renameSound(soundIndex, newName); - this.emitTargetsUpdate(); - } - - /** - * Get a sound buffer from the audio engine. - * @param {int} soundIndex - the index of the sound to be got. - * @return {AudioBuffer} the sound's audio buffer. - */ - getSoundBuffer (soundIndex) { - const id = this.editingTarget.sprite.sounds[soundIndex].soundId; - if (id && this.runtime && this.runtime.audioEngine) { - return this.editingTarget.sprite.soundBank.getSoundPlayer(id).buffer; - } - return null; - } - - /** - * Update a sound buffer. - * @param {int} soundIndex - the index of the sound to be updated. - * @param {AudioBuffer} newBuffer - new audio buffer for the audio engine. - * @param {ArrayBuffer} soundEncoding - the new (wav) encoded sound to be stored - */ - updateSoundBuffer (soundIndex, newBuffer, soundEncoding) { - const sound = this.editingTarget.sprite.sounds[soundIndex]; - const id = sound ? sound.soundId : null; - if (id && this.runtime && this.runtime.audioEngine) { - this.editingTarget.sprite.soundBank.getSoundPlayer(id).buffer = newBuffer; - } - // Update sound in runtime - if (soundEncoding) { - // Now that we updated the sound, the format should also be updated - // so that the sound can eventually be decoded the right way. - // Sounds that were formerly 'adpcm', but were updated in sound editor - // will not get decoded by the audio engine correctly unless the format - // is updated as below. - sound.format = ''; - const storage = this.runtime.storage; - sound.asset = storage.createAsset( - storage.AssetType.Sound, - storage.DataFormat.WAV, - soundEncoding, - null, - true // generate md5 - ); - sound.assetId = sound.asset.assetId; - sound.dataFormat = storage.DataFormat.WAV; - sound.md5 = `${sound.assetId}.${sound.dataFormat}`; - sound.sampleCount = newBuffer.length; - sound.rate = newBuffer.sampleRate; - } - // If soundEncoding is null, it's because gui had a problem - // encoding the updated sound. We don't want to store anything in this - // case, and gui should have logged an error. - - this.emitTargetsUpdate(); - } - - /** - * Delete a sound from the current editing target. - * @param {int} soundIndex - the index of the sound to be removed. - * @return {?Function} A function to restore the sound that was deleted, - * or null, if no sound was deleted. - */ - deleteSound (soundIndex) { - const target = this.editingTarget; - const deletedSound = this.editingTarget.deleteSound(soundIndex); - if (deletedSound) { - this.runtime.emitProjectChanged(); - const restoreFun = () => { - target.addSound(deletedSound); - this.emitTargetsUpdate(); - }; - return restoreFun; - } - return null; - } - - /** - * Get a string representation of the image from storage. - * @param {int} costumeIndex - the index of the costume to be got. - * @return {string} the costume's SVG string if it's SVG, - * a dataURI if it's a PNG or JPG, or null if it couldn't be found or decoded. - */ - getCostume (costumeIndex) { - const asset = this.editingTarget.getCostumes()[costumeIndex].asset; - if (!asset || !this.runtime || !this.runtime.storage) return null; - const format = asset.dataFormat; - if (format === this.runtime.storage.DataFormat.SVG) { - return asset.decodeText(); - } else if (format === this.runtime.storage.DataFormat.PNG || - format === this.runtime.storage.DataFormat.JPG) { - return asset.encodeDataURI(); - } - log.error(`Unhandled format: ${asset.dataFormat}`); - return null; - } - - /** - * Update a costume with the given bitmap - * @param {!int} costumeIndex - the index of the costume to be updated. - * @param {!ImageData} bitmap - new bitmap for the renderer. - * @param {!number} rotationCenterX x of point about which the costume rotates, relative to its upper left corner - * @param {!number} rotationCenterY y of point about which the costume rotates, relative to its upper left corner - * @param {!number} bitmapResolution 1 for bitmaps that have 1 pixel per unit of stage, - * 2 for double-resolution bitmaps - */ - updateBitmap (costumeIndex, bitmap, rotationCenterX, rotationCenterY, bitmapResolution) { - const costume = this.editingTarget.getCostumes()[costumeIndex]; - if (!(costume && this.runtime && this.runtime.renderer)) return; - - costume.rotationCenterX = rotationCenterX; - costume.rotationCenterY = rotationCenterY; - - // If the bitmap originally had a zero width or height, use that value - const bitmapWidth = bitmap.sourceWidth === 0 ? 0 : bitmap.width; - const bitmapHeight = bitmap.sourceHeight === 0 ? 0 : bitmap.height; - // @todo: updateBitmapSkin does not take ImageData - const canvas = document.createElement('canvas'); - canvas.width = bitmapWidth; - canvas.height = bitmapHeight; - const context = canvas.getContext('2d'); - context.putImageData(bitmap, 0, 0); - - // Divide by resolution because the renderer's definition of the rotation center - // is the rotation center divided by the bitmap resolution - this.runtime.renderer.updateBitmapSkin( - costume.skinId, - canvas, - bitmapResolution, - [rotationCenterX / bitmapResolution, rotationCenterY / bitmapResolution] - ); - - // @todo there should be a better way to get from ImageData to a decodable storage format - canvas.toBlob(blob => { - const reader = new FileReader(); - reader.addEventListener('loadend', () => { - const storage = this.runtime.storage; - costume.dataFormat = storage.DataFormat.PNG; - costume.bitmapResolution = bitmapResolution; - costume.size = [bitmapWidth, bitmapHeight]; - costume.asset = storage.createAsset( - storage.AssetType.ImageBitmap, - costume.dataFormat, - Buffer.from(reader.result), - null, // id - true // generate md5 - ); - costume.assetId = costume.asset.assetId; - costume.md5 = `${costume.assetId}.${costume.dataFormat}`; - this.emitTargetsUpdate(); - }); - // Bitmaps with a zero width or height return null for their blob - if (blob){ - reader.readAsArrayBuffer(blob); - } - }); - } - - /** - * Update a costume with the given SVG - * @param {int} costumeIndex - the index of the costume to be updated. - * @param {string} svg - new SVG for the renderer. - * @param {number} rotationCenterX x of point about which the costume rotates, relative to its upper left corner - * @param {number} rotationCenterY y of point about which the costume rotates, relative to its upper left corner - */ - updateSvg (costumeIndex, svg, rotationCenterX, rotationCenterY) { - const costume = this.editingTarget.getCostumes()[costumeIndex]; - if (costume && this.runtime && this.runtime.renderer) { - costume.rotationCenterX = rotationCenterX; - costume.rotationCenterY = rotationCenterY; - this.runtime.renderer.updateSVGSkin(costume.skinId, svg, [rotationCenterX, rotationCenterY]); - costume.size = this.runtime.renderer.getSkinSize(costume.skinId); - } - const storage = this.runtime.storage; - // If we're in here, we've edited an svg in the vector editor, - // so the dataFormat should be 'svg' - costume.dataFormat = storage.DataFormat.SVG; - costume.bitmapResolution = 1; - costume.asset = storage.createAsset( - storage.AssetType.ImageVector, - costume.dataFormat, - (new _TextEncoder()).encode(svg), - null, - true // generate md5 - ); - costume.assetId = costume.asset.assetId; - costume.md5 = `${costume.assetId}.${costume.dataFormat}`; - this.emitTargetsUpdate(); - } - - /** - * Add a backdrop to the stage. - * @param {string} md5ext - the MD5 and extension of the backdrop to be loaded. - * @param {!object} backdropObject Object representing the backdrop. - * @property {int} skinId - the ID of the backdrop's render skin, once installed. - * @property {number} rotationCenterX - the X component of the backdrop's origin. - * @property {number} rotationCenterY - the Y component of the backdrop's origin. - * @property {number} [bitmapResolution] - the resolution scale for a bitmap backdrop. - * @returns {?Promise} - a promise that resolves when the backdrop has been added - */ - addBackdrop (md5ext, backdropObject) { - return loadCostume(md5ext, backdropObject, this.runtime).then(() => { - const stage = this.runtime.getTargetForStage(); - stage.addCostume(backdropObject); - stage.setCostume(stage.getCostumes().length - 1); - this.runtime.emitProjectChanged(); - }); - } - - /** - * Rename a sprite. - * @param {string} targetId ID of a target whose sprite to rename. - * @param {string} newName New name of the sprite. - */ - renameSprite (targetId, newName) { - const target = this.runtime.getTargetById(targetId); - if (target) { - if (!target.isSprite()) { - throw new Error('Cannot rename non-sprite targets.'); - } - const sprite = target.sprite; - if (!sprite) { - throw new Error('No sprite associated with this target.'); - } - if (newName && RESERVED_NAMES.indexOf(newName) === -1) { - const names = this.runtime.targets - .filter(runtimeTarget => runtimeTarget.isSprite() && runtimeTarget.id !== target.id) - .map(runtimeTarget => runtimeTarget.sprite.name); - const oldName = sprite.name; - const newUnusedName = StringUtil.unusedName(newName, names); - sprite.name = newUnusedName; - const allTargets = this.runtime.targets; - for (let i = 0; i < allTargets.length; i++) { - const currTarget = allTargets[i]; - currTarget.blocks.updateAssetName(oldName, newName, 'sprite'); - } - - if (newUnusedName !== oldName) this.emitTargetsUpdate(); - } - } else { - throw new Error('No target with the provided id.'); - } - } - - /** - * Delete a sprite and all its clones. - * @param {string} targetId ID of a target whose sprite to delete. - * @return {Function} Returns a function to restore the sprite that was deleted - */ - deleteSprite (targetId) { - const target = this.runtime.getTargetById(targetId); - - if (target) { - const targetIndexBeforeDelete = this.runtime.targets.map(t => t.id).indexOf(target.id); - if (!target.isSprite()) { - throw new Error('Cannot delete non-sprite targets.'); - } - const sprite = target.sprite; - if (!sprite) { - throw new Error('No sprite associated with this target.'); - } - const spritePromise = this.exportSprite(targetId, 'uint8array'); - const restoreSprite = () => spritePromise.then(spriteBuffer => this.addSprite(spriteBuffer)); - // Remove monitors from the runtime state and remove the - // target-specific monitored blocks (e.g. local variables) - target.deleteMonitors(); - const currentEditingTarget = this.editingTarget; - for (let i = 0; i < sprite.clones.length; i++) { - const clone = sprite.clones[i]; - this.runtime.stopForTarget(sprite.clones[i]); - this.runtime.disposeTarget(sprite.clones[i]); - // Ensure editing target is switched if we are deleting it. - if (clone === currentEditingTarget) { - const nextTargetIndex = Math.min(this.runtime.targets.length - 1, targetIndexBeforeDelete); - if (this.runtime.targets.length > 0){ - this.setEditingTarget(this.runtime.targets[nextTargetIndex].id); - } else { - this.editingTarget = null; - } - } - } - // Sprite object should be deleted by GC. - this.emitTargetsUpdate(); - return restoreSprite; - } - - throw new Error('No target with the provided id.'); - } - - /** - * Duplicate a sprite. - * @param {string} targetId ID of a target whose sprite to duplicate. - * @returns {Promise} Promise that resolves when duplicated target has - * been added to the runtime. - */ - duplicateSprite (targetId) { - const target = this.runtime.getTargetById(targetId); - if (!target) { - throw new Error('No target with the provided id.'); - } else if (!target.isSprite()) { - throw new Error('Cannot duplicate non-sprite targets.'); - } else if (!target.sprite) { - throw new Error('No sprite associated with this target.'); - } - return target.duplicate().then(newTarget => { - this.runtime.addTarget(newTarget); - newTarget.goBehindOther(target); - this.setEditingTarget(newTarget.id); - }); - } - - /** - * Set the audio engine for the VM/runtime - * @param {!AudioEngine} audioEngine The audio engine to attach - */ - attachAudioEngine (audioEngine) { - this.runtime.attachAudioEngine(audioEngine); - } - - /** - * Set the renderer for the VM/runtime - * @param {!RenderWebGL} renderer The renderer to attach - */ - attachRenderer (renderer) { - this.runtime.attachRenderer(renderer); - } - - /** - * @returns {RenderWebGL} The renderer attached to the vm - */ - get renderer () { - return this.runtime && this.runtime.renderer; - } - - /** - * Set the svg adapter for the VM/runtime, which converts scratch 2 svgs to scratch 3 svgs - * @param {!SvgRenderer} svgAdapter The adapter to attach - */ - attachV2SVGAdapter (svgAdapter) { - this.runtime.attachV2SVGAdapter(svgAdapter); - } - - /** - * Set the bitmap adapter for the VM/runtime, which converts scratch 2 - * bitmaps to scratch 3 bitmaps. (Scratch 3 bitmaps are all bitmap resolution 2) - * @param {!function} bitmapAdapter The adapter to attach - */ - attachV2BitmapAdapter (bitmapAdapter) { - this.runtime.attachV2BitmapAdapter(bitmapAdapter); - } - - /** - * Set the storage module for the VM/runtime - * @param {!ScratchStorage} storage The storage module to attach - */ - attachStorage (storage) { - this.runtime.attachStorage(storage); - } - - /** - * set the current locale and builtin messages for the VM - * @param {!string} locale current locale - * @param {!object} messages builtin messages map for current locale - * @returns {Promise} Promise that resolves when all the blocks have been - * updated for a new locale (or empty if locale hasn't changed.) - */ - setLocale (locale, messages) { - if (locale !== formatMessage.setup().locale) { - formatMessage.setup({locale: locale, translations: {[locale]: messages}}); - } - return this.extensionManager.refreshBlocks(); - } - - /** - * get the current locale for the VM - * @returns {string} the current locale in the VM - */ - getLocale () { - return formatMessage.setup().locale; - } - - /** - * Handle a Blockly event for the current editing target. - * @param {!Blockly.Event} e Any Blockly event. - */ - blockListener (e) { - if (this.editingTarget) { - this.editingTarget.blocks.blocklyListen(e); - } - } - - /** - * Handle a Blockly event for the flyout. - * @param {!Blockly.Event} e Any Blockly event. - */ - flyoutBlockListener (e) { - this.runtime.flyoutBlocks.blocklyListen(e); - } - - /** - * Handle a Blockly event for the flyout to be passed to the monitor container. - * @param {!Blockly.Event} e Any Blockly event. - */ - monitorBlockListener (e) { - // Filter events by type, since monitor blocks only need to listen to these events. - // Monitor blocks shouldn't be destroyed when flyout blocks are deleted. - if (['create', 'change'].indexOf(e.type) !== -1) { - this.runtime.monitorBlocks.blocklyListen(e); - } - } - - /** - * Handle a Blockly event for the variable map. - * @param {!Blockly.Event} e Any Blockly event. - */ - variableListener (e) { - // Filter events by type, since blocks only needs to listen to these - // var events. - if (['var_create', 'var_rename', 'var_delete'].indexOf(e.type) !== -1) { - this.runtime.getTargetForStage().blocks.blocklyListen(e); - } - } - - /** - * Set an editing target. An editor UI can use this function to switch - * between editing different targets, sprites, etc. - * After switching the editing target, the VM may emit updates - * to the list of targets and any attached workspace blocks - * (see `emitTargetsUpdate` and `emitWorkspaceUpdate`). - * @param {string} targetId Id of target to set as editing. - */ - setEditingTarget (targetId) { - // Has the target id changed? If not, exit. - if (this.editingTarget && targetId === this.editingTarget.id) { - return; - } - const target = this.runtime.getTargetById(targetId); - if (target) { - this.editingTarget = target; - // Emit appropriate UI updates. - this.emitTargetsUpdate(false /* Don't emit project change */); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(target); - } - } - - /** - * Called when blocks are dragged from one sprite to another. Adds the blocks to the - * workspace of the given target. - * @param {!Array} blocks Blocks to add. - * @param {!string} targetId Id of target to add blocks to. - * @param {?string} optFromTargetId Optional target id indicating that blocks are being - * shared from that target. This is needed for resolving any potential variable conflicts. - * @return {!Promise} Promise that resolves when the extensions and blocks have been added. - */ - shareBlocksToTarget (blocks, targetId, optFromTargetId) { - const sb3 = require('./serialization/sb3'); - - const copiedBlocks = JSON.parse(JSON.stringify(blocks)); - newBlockIds(copiedBlocks); - const target = this.runtime.getTargetById(targetId); - - if (optFromTargetId) { - // If the blocks are being shared from another target, - // resolve any possible variable conflicts that may arise. - const fromTarget = this.runtime.getTargetById(optFromTargetId); - fromTarget.resolveVariableSharingConflictsWithTarget(copiedBlocks, target); - } - - // Create a unique set of extensionIds that are not yet loaded - const extensionIDs = new Set(copiedBlocks - .map(b => sb3.getExtensionIdForOpcode(b.opcode)) - .filter(id => !!id) // Remove ids that do not exist - .filter(id => !this.extensionManager.isExtensionLoaded(id)) // and remove loaded extensions - ); - - // Create an array promises for extensions to load - const extensionPromises = Array.from(extensionIDs, - id => this.extensionManager.loadExtensionURL(id) - ); - - return Promise.all(extensionPromises).then(() => { - copiedBlocks.forEach(block => { - target.blocks.createBlock(block); - }); - target.blocks.updateTargetSpecificBlocks(target.isStage); - }); - } - - /** - * Called when costumes are dragged from editing target to another target. - * Sets the newly added costume as the current costume. - * @param {!number} costumeIndex Index of the costume of the editing target to share. - * @param {!string} targetId Id of target to add the costume. - * @return {Promise} Promise that resolves when the new costume has been loaded. - */ - shareCostumeToTarget (costumeIndex, targetId) { - const originalCostume = this.editingTarget.getCostumes()[costumeIndex]; - const clone = Object.assign({}, originalCostume); - const md5ext = `${clone.assetId}.${clone.dataFormat}`; - return loadCostume(md5ext, clone, this.runtime).then(() => { - const target = this.runtime.getTargetById(targetId); - if (target) { - target.addCostume(clone); - target.setCostume( - target.getCostumes().length - 1 - ); - } - }); - } - - /** - * Called when sounds are dragged from editing target to another target. - * @param {!number} soundIndex Index of the sound of the editing target to share. - * @param {!string} targetId Id of target to add the sound. - * @return {Promise} Promise that resolves when the new sound has been loaded. - */ - shareSoundToTarget (soundIndex, targetId) { - const originalSound = this.editingTarget.getSounds()[soundIndex]; - const clone = Object.assign({}, originalSound); - const target = this.runtime.getTargetById(targetId); - return loadSound(clone, this.runtime, target.sprite.soundBank).then(() => { - if (target) { - target.addSound(clone); - this.emitTargetsUpdate(); - } - }); - } - - /** - * Repopulate the workspace with the blocks of the current editingTarget. This - * allows us to get around bugs like gui#413. - */ - refreshWorkspace () { - if (this.editingTarget) { - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); - this.emitTargetsUpdate(false /* Don't emit project change */); - } - } - - /** - * Emit metadata about available targets. - * An editor UI could use this to display a list of targets and show - * the currently editing one. - * @param {bool} triggerProjectChange If true, also emit a project changed event. - * Disabled selectively by updates that don't affect project serialization. - * Defaults to true. - */ - emitTargetsUpdate (triggerProjectChange) { - if (typeof triggerProjectChange === 'undefined') triggerProjectChange = true; - this.emit('targetsUpdate', { - // [[target id, human readable target name], ...]. - targetList: this.runtime.targets - .filter( - // Don't report clones. - target => !target.hasOwnProperty('isOriginal') || target.isOriginal - ).map( - target => target.toJSON() - ), - // Currently editing target id. - editingTarget: this.editingTarget ? this.editingTarget.id : null - }); - if (triggerProjectChange) { - this.runtime.emitProjectChanged(); - } - } - - /** - * Emit an Blockly/scratch-blocks compatible XML representation - * of the current editing target's blocks. - */ - emitWorkspaceUpdate () { - // Create a list of broadcast message Ids according to the stage variables - const stageVariables = this.runtime.getTargetForStage().variables; - let messageIds = []; - for (const varId in stageVariables) { - if (stageVariables[varId].type === Variable.BROADCAST_MESSAGE_TYPE) { - messageIds.push(varId); - } - } - // Go through all blocks on all targets, removing referenced - // broadcast ids from the list. - for (let i = 0; i < this.runtime.targets.length; i++) { - const currTarget = this.runtime.targets[i]; - const currBlocks = currTarget.blocks._blocks; - for (const blockId in currBlocks) { - if (currBlocks[blockId].fields.BROADCAST_OPTION) { - const id = currBlocks[blockId].fields.BROADCAST_OPTION.id; - const index = messageIds.indexOf(id); - if (index !== -1) { - messageIds = messageIds.slice(0, index) - .concat(messageIds.slice(index + 1)); - } - } - } - } - // Anything left in messageIds is not referenced by a block, so delete it. - for (let i = 0; i < messageIds.length; i++) { - const id = messageIds[i]; - delete this.runtime.getTargetForStage().variables[id]; - } - const globalVarMap = Object.assign({}, this.runtime.getTargetForStage().variables); - const localVarMap = this.editingTarget.isStage ? - Object.create(null) : - Object.assign({}, this.editingTarget.variables); - - const globalVariables = Object.keys(globalVarMap).map(k => globalVarMap[k]); - const localVariables = Object.keys(localVarMap).map(k => localVarMap[k]); - const workspaceComments = Object.keys(this.editingTarget.comments) - .map(k => this.editingTarget.comments[k]) - .filter(c => c.blockId === null); - - const xmlString = ` - - ${globalVariables.map(v => v.toXML()).join()} - ${localVariables.map(v => v.toXML(true)).join()} - - ${workspaceComments.map(c => c.toXML()).join()} - ${this.editingTarget.blocks.toXML(this.editingTarget.comments)} - `; - - this.emit('workspaceUpdate', {xml: xmlString}); - } - - /** - * Get a target id for a drawable id. Useful for interacting with the renderer - * @param {int} drawableId The drawable id to request the target id for - * @returns {?string} The target id, if found. Will also be null if the target found is the stage. - */ - getTargetIdForDrawableId (drawableId) { - const target = this.runtime.getTargetByDrawableId(drawableId); - if (target && target.hasOwnProperty('id') && target.hasOwnProperty('isStage') && !target.isStage) { - return target.id; - } - return null; - } - - /** - * Reorder target by index. Return whether a change was made. - * @param {!string} targetIndex Index of the target. - * @param {!number} newIndex index that the target should be moved to. - * @returns {boolean} Whether a target was reordered. - */ - reorderTarget (targetIndex, newIndex) { - let targets = this.runtime.targets; - targetIndex = MathUtil.clamp(targetIndex, 0, targets.length - 1); - newIndex = MathUtil.clamp(newIndex, 0, targets.length - 1); - if (targetIndex === newIndex) return false; - const target = targets[targetIndex]; - targets = targets.slice(0, targetIndex).concat(targets.slice(targetIndex + 1)); - targets.splice(newIndex, 0, target); - this.runtime.targets = targets; - this.emitTargetsUpdate(); - return true; - } - - /** - * Reorder the costumes of a target if it exists. Return whether it succeeded. - * @param {!string} targetId ID of the target which owns the costumes. - * @param {!number} costumeIndex index of the costume to move. - * @param {!number} newIndex index that the costume should be moved to. - * @returns {boolean} Whether a costume was reordered. - */ - reorderCostume (targetId, costumeIndex, newIndex) { - const target = this.runtime.getTargetById(targetId); - if (target) { - const reorderSuccessful = target.reorderCostume(costumeIndex, newIndex); - if (reorderSuccessful) { - this.runtime.emitProjectChanged(); - } - return reorderSuccessful; - } - return false; - } - - /** - * Reorder the sounds of a target if it exists. Return whether it occured. - * @param {!string} targetId ID of the target which owns the sounds. - * @param {!number} soundIndex index of the sound to move. - * @param {!number} newIndex index that the sound should be moved to. - * @returns {boolean} Whether a sound was reordered. - */ - reorderSound (targetId, soundIndex, newIndex) { - const target = this.runtime.getTargetById(targetId); - if (target) { - const reorderSuccessful = target.reorderSound(soundIndex, newIndex); - if (reorderSuccessful) { - this.runtime.emitProjectChanged(); - } - return reorderSuccessful; - } - return false; - } - - /** - * Put a target into a "drag" state, during which its X/Y positions will be unaffected - * by blocks. - * @param {string} targetId The id for the target to put into a drag state - */ - startDrag (targetId) { - const target = this.runtime.getTargetById(targetId); - if (target) { - this._dragTarget = target; - target.startDrag(); - } - } - - /** - * Remove a target from a drag state, so blocks may begin affecting X/Y position again - * @param {string} targetId The id for the target to remove from the drag state - */ - stopDrag (targetId) { - const target = this.runtime.getTargetById(targetId); - if (target) { - this._dragTarget = null; - target.stopDrag(); - this.setEditingTarget(target.sprite && target.sprite.clones[0] ? - target.sprite.clones[0].id : target.id); - } - } - - /** - * Post/edit sprite info for the current editing target or the drag target. - * @param {object} data An object with sprite info data to set. - */ - postSpriteInfo (data) { - if (this._dragTarget) { - this._dragTarget.postSpriteInfo(data); - } else { - this.editingTarget.postSpriteInfo(data); - } - // Post sprite info means the gui has changed something about a sprite, - // either through the sprite info pane fields (e.g. direction, size) or - // through dragging a sprite on the stage - // Emit a project changed event. - this.runtime.emitProjectChanged(); - } - - /** - * Set a target's variable's value. Return whether it succeeded. - * @param {!string} targetId ID of the target which owns the variable. - * @param {!string} variableId ID of the variable to set. - * @param {!*} value The new value of that variable. - * @returns {boolean} whether the target and variable were found and updated. - */ - setVariableValue (targetId, variableId, value) { - const target = this.runtime.getTargetById(targetId); - if (target) { - const variable = target.lookupVariableById(variableId); - if (variable) { - variable.value = value; - - if (variable.isCloud) { - this.runtime.ioDevices.cloud.requestUpdateVariable(variable.name, variable.value); - } - - return true; - } - } - return false; - } - - /** - * Get a target's variable's value. Return null if the target or variable does not exist. - * @param {!string} targetId ID of the target which owns the variable. - * @param {!string} variableId ID of the variable to set. - * @returns {?*} The value of the variable, or null if it could not be looked up. - */ - getVariableValue (targetId, variableId) { - const target = this.runtime.getTargetById(targetId); - if (target) { - const variable = target.lookupVariableById(variableId); - if (variable) { - return variable.value; - } - } - return null; - } - - /** - * Allow VM consumer to configure the ScratchLink socket creator. - * @param {Function} factory The custom ScratchLink socket factory. - */ - configureScratchLinkSocketFactory (factory) { - this.runtime.configureScratchLinkSocketFactory(factory); - } -} - -module.exports = VirtualMachine; From 3024124ef72136decfb7985921f46feaa898b707 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 22 Dec 2020 09:32:59 -0500 Subject: [PATCH 1948/1971] Update scratch-render to latest upstream, keeping the scratch coord helper function --- packages/scratch-render/src/RenderWebGL.js | 46 ++++++++-------------- packages/scratch-render/src/SVGSkin.js | 30 ++++++++++++++ 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index 5d7a05f831..d8ba0c2f9d 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -11,7 +11,6 @@ const RenderConstants = require('./RenderConstants'); const ShaderManager = require('./ShaderManager'); const SVGSkin = require('./SVGSkin'); const TextBubbleSkin = require('./TextBubbleSkin'); -const TextCostumeSkin = require('./TextCostumeSkin'); const EffectTransform = require('./EffectTransform'); const log = require('./util/log'); @@ -425,23 +424,6 @@ class RenderWebGL extends EventEmitter { this._reskin(skinId, newSkin); } - updateTextCostumeSkin (textState) { - // update existing skin - if (textState.skinId && (this._allSkins[textState.skinId] instanceof TextCostumeSkin)) { - this._allSkins[textState.skinId].setTextAndStyle(textState); - return textState.skinId; - } - - // create and update a new skin - const skinId = this._nextSkinId++; - const newSkin = new TextCostumeSkin(skinId, this); - this._allSkins[skinId] = newSkin; - newSkin.setTextAndStyle(textState); - // this._reskin(skinId, newSkin); // this is erroring- might be necessary? - - return skinId; - } - _reskin (skinId, newSkin) { const oldSkin = this._allSkins[skinId]; this._allSkins[skinId] = newSkin; @@ -490,7 +472,7 @@ class RenderWebGL extends EventEmitter { * @returns {int} The ID of the new Drawable. */ createDrawable (group) { - if (!group || !this._layerGroups.hasOwnProperty(group)) { + if (!group || !Object.prototype.hasOwnProperty.call(this._layerGroups, group)) { log.warn('Cannot create a drawable without a known layer group'); return; } @@ -560,7 +542,7 @@ class RenderWebGL extends EventEmitter { * @param {string} group Group name that the drawable belongs to */ destroyDrawable (drawableID, group) { - if (!group || !this._layerGroups.hasOwnProperty(group)) { + if (!group || !Object.prototype.hasOwnProperty.call(this._layerGroups, group)) { log.warn('Cannot destroy drawable without known layer group.'); return; } @@ -614,7 +596,7 @@ class RenderWebGL extends EventEmitter { * @return {?number} New order if changed, or null. */ setDrawableOrder (drawableID, order, group, optIsRelative, optMin) { - if (!group || !this._layerGroups.hasOwnProperty(group)) { + if (!group || !Object.prototype.hasOwnProperty.call(this._layerGroups, group)) { log.warn('Cannot set the order of a drawable without a known layer group.'); return; } @@ -668,7 +650,7 @@ class RenderWebGL extends EventEmitter { twgl.bindFramebufferInfo(gl, null); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); - gl.clearColor.apply(gl, this._backgroundColor4f); + gl.clearColor(...this._backgroundColor4f); gl.clear(gl.COLOR_BUFFER_BIT); this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, this._projection); @@ -818,6 +800,8 @@ class RenderWebGL extends EventEmitter { const color = __touchingColor; const hasMask = Boolean(mask3b); + drawable.updateCPURenderAttributes(); + // Masked drawable ignores ghost effect const effectMask = ~ShaderManager.EFFECT_INFO.ghost.mask; @@ -987,6 +971,8 @@ class RenderWebGL extends EventEmitter { const drawable = this._allDrawables[drawableID]; const point = __isTouchingDrawablesPoint; + drawable.updateCPURenderAttributes(); + // This is an EXTREMELY brute force collision detector, but it is // still faster than asking the GPU to give us the pixels. for (let x = bounds.left; x <= bounds.right; x++) { @@ -1168,7 +1154,7 @@ class RenderWebGL extends EventEmitter { let hit = RenderConstants.ID_NONE; for (const hitID in hits) { - if (hits.hasOwnProperty(hitID) && (hits[hitID] > hits[hit])) { + if (Object.prototype.hasOwnProperty.call(hits, hitID) && (hits[hitID] > hits[hit])) { hit = hitID; } } @@ -1423,7 +1409,7 @@ class RenderWebGL extends EventEmitter { gl.viewport(0, 0, bounds.width, bounds.height); const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); - gl.clearColor.apply(gl, this._backgroundColor4f); + gl.clearColor(...this._backgroundColor4f); gl.clear(gl.COLOR_BUFFER_BIT); this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, projection); @@ -1471,8 +1457,6 @@ class RenderWebGL extends EventEmitter { /** @todo remove this once URL-based skin setting is removed. */ if (!drawable.skin || !drawable.skin.getTexture([100, 100])) return null; - - drawable.updateCPURenderAttributes(); const bounds = drawable.getFastBounds(); // Limit queries to the stage size. @@ -1919,7 +1903,7 @@ class RenderWebGL extends EventEmitter { const uniforms = {}; let effectBits = drawable.enabledEffects; - effectBits &= opts.hasOwnProperty('effectMask') ? opts.effectMask : effectBits; + effectBits &= Object.prototype.hasOwnProperty.call(opts, 'effectMask') ? opts.effectMask : effectBits; const newShader = this._shaderManager.getShader(drawMode, effectBits); // Manually perform region check. Do not create functions inside a @@ -1947,7 +1931,9 @@ class RenderWebGL extends EventEmitter { if (uniforms.u_skin) { twgl.setTextureParameters( - gl, uniforms.u_skin, {minMag: drawable.useNearest(drawableScale) ? gl.NEAREST : gl.LINEAR} + gl, uniforms.u_skin, { + minMag: drawable.skin.useNearest(drawableScale, drawable) ? gl.NEAREST : gl.LINEAR + } ); } @@ -1967,14 +1953,14 @@ class RenderWebGL extends EventEmitter { _getConvexHullPointsForDrawable (drawableID) { const drawable = this._allDrawables[drawableID]; - drawable.updateCPURenderAttributes(); - const [width, height] = drawable.skin.size; // No points in the hull if invisible or size is 0. if (!drawable.getVisible() || width === 0 || height === 0) { return []; } + drawable.updateCPURenderAttributes(); + /** * Return the determinant of two vectors, the vector from A to B and the vector from A to C. * diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index 0b58fdcd3a..bf1aeca888 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -2,6 +2,7 @@ const twgl = require('twgl.js'); const Skin = require('./Skin'); const SvgRenderer = require('scratch-svg-renderer').SVGRenderer; +const ShaderManager = require('./ShaderManager'); const MAX_TEXTURE_DIMENSION = 2048; @@ -58,6 +59,35 @@ class SVGSkin extends Skin { return this._svgRenderer.size; } + useNearest (scale, drawable) { + // If the effect bits for mosaic, pixelate, whirl, or fisheye are set, use linear + if ((drawable.enabledEffects & ( + ShaderManager.EFFECT_INFO.fisheye.mask | + ShaderManager.EFFECT_INFO.whirl.mask | + ShaderManager.EFFECT_INFO.pixelate.mask | + ShaderManager.EFFECT_INFO.mosaic.mask + )) !== 0) { + return false; + } + + // We can't use nearest neighbor unless we are a multiple of 90 rotation + if (drawable._direction % 90 !== 0) { + return false; + } + + // Because SVG skins' bounding boxes are currently not pixel-aligned, the idea here is to hide blurriness + // by using nearest-neighbor scaling if one screen-space pixel is "close enough" to one texture pixel. + // If the scale of the skin is very close to 100 (0.99999 variance is okay I guess) + // TODO: Make this check more precise. We should use nearest if there's less than one pixel's difference + // between the screen-space and texture-space sizes of the skin. Mipmaps make this harder because there are + // multiple textures (and hence multiple texture spaces) and we need to know which one to choose. + if (Math.abs(scale[0]) > 99 && Math.abs(scale[0]) < 101 && + Math.abs(scale[1]) > 99 && Math.abs(scale[1]) < 101) { + return true; + } + return false; + } + /** * Create a MIP for a given scale. * @param {number} scale - The relative size of the MIP From 8698a8e549fc331363bd573b366ffa16b4e9800d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 22 Dec 2020 09:39:59 -0500 Subject: [PATCH 1949/1971] Update vm to latest, stripping out text specific stuff --- packages/scratch-vm/src/engine/runtime.js | 17 +++++++++-------- .../src/extension-support/extension-manager.js | 1 - packages/scratch-vm/src/virtual-machine.js | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 4f9d124d73..61f67feea3 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -601,6 +601,15 @@ class Runtime extends EventEmitter { static get PERIPHERAL_LIST_UPDATE () { return 'PERIPHERAL_LIST_UPDATE'; } + + /** + * Event name for when the user picks a bluetooth device to connect to + * via Companion Device Manager (CDM) + * @const {string} + */ + static get USER_PICKED_PERIPHERAL () { + return 'USER_PICKED_PERIPHERAL'; + } /** * Event name for reporting that a peripheral has connected. @@ -655,10 +664,6 @@ class Runtime extends EventEmitter { return 'MIC_LISTENING'; } - static get EXTENSION_DATA_LOADING () { - return 'EXTENSION_DATA_LOADING'; - } - /** * Event name for reporting that blocksInfo was updated. * @const {string} @@ -1515,10 +1520,6 @@ class Runtime extends EventEmitter { this.emit(Runtime.MIC_LISTENING, listening); } - emitExtensionLoading (loading) { - this.emit(Runtime.EXTENSION_DATA_LOADING, loading); - } - /** * Retrieve the function associated with the given opcode. * @param {!string} opcode The opcode to look up. diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 743a9575b9..8e687217ed 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -24,7 +24,6 @@ const builtinExtensions = { makeymakey: () => require('../extensions/scratch3_makeymakey'), boost: () => require('../extensions/scratch3_boost'), gdxfor: () => require('../extensions/scratch3_gdx_for'), - text: () => require('../extensions/scratch3_text'), faceSensing: () => require('../extensions/scratch3_face_sensing') }; diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 876ae3083b..5cec44c10c 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -127,6 +127,9 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.PERIPHERAL_LIST_UPDATE, info => { this.emit(Runtime.PERIPHERAL_LIST_UPDATE, info); }); + this.runtime.on(Runtime.USER_PICKED_PERIPHERAL, info => { + this.emit(Runtime.USER_PICKED_PERIPHERAL, info); + }); this.runtime.on(Runtime.PERIPHERAL_CONNECTED, () => this.emit(Runtime.PERIPHERAL_CONNECTED) ); @@ -145,9 +148,6 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.MIC_LISTENING, listening => { this.emit(Runtime.MIC_LISTENING, listening); }); - this.runtime.on(Runtime.EXTENSION_DATA_LOADING, loading => { - this.emit(Runtime.EXTENSION_DATA_LOADING, loading); - }); this.runtime.on(Runtime.RUNTIME_STARTED, () => { this.emit(Runtime.RUNTIME_STARTED); }); From 14e894c50efaf533931050f241c95be1e17f0536 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 22 Dec 2020 09:46:15 -0500 Subject: [PATCH 1950/1971] Update gui to develop for face --- packages/scratch-gui/src/containers/stage.jsx | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 8e1ac92483..79a41db7ea 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -342,16 +342,29 @@ class Stage extends React.Component { } onStartDrag (x, y) { if (this.state.dragId) return; - const drawableId = this.renderer.pick(x, y); + + // Targets with no attached drawable cannot be dragged. + let draggableTargets = this.props.vm.runtime.targets.filter( + target => Number.isFinite(target.drawableID) + ); + + // Because pick queries can be expensive, only perform them for drawables that are currently draggable. + // If we're in the editor, we can drag all targets. Otherwise, filter. + if (!this.props.useEditorDragStyle) { + draggableTargets = draggableTargets.filter( + target => target.draggable + ); + } + if (draggableTargets.length === 0) return; + + const draggableIDs = draggableTargets.map(target => target.drawableID); + const drawableId = this.renderer.pick(x, y, 1, 1, draggableIDs); if (drawableId === null) return; const targetId = this.props.vm.getTargetIdForDrawableId(drawableId); if (targetId === null) return; const target = this.props.vm.runtime.getTargetById(targetId); - // Do not start drag unless in editor drag mode or target is draggable - if (!(this.props.useEditorDragStyle || target.draggable)) return; - // Dragging always brings the target to the front target.goToFront(); From 30f79c34d4874f35fef48d4a9bd7d30a3a6c18fb Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 22 Dec 2020 09:50:07 -0500 Subject: [PATCH 1951/1971] Add back the scratch-vm changes for when an extension is loaded --- packages/scratch-vm/src/engine/runtime.js | 8 ++++++++ packages/scratch-vm/src/virtual-machine.js | 3 +++ 2 files changed, 11 insertions(+) diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 61f67feea3..6ae31adb78 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -664,6 +664,10 @@ class Runtime extends EventEmitter { return 'MIC_LISTENING'; } + static get EXTENSION_DATA_LOADING () { + return 'EXTENSION_DATA_LOADING'; + } + /** * Event name for reporting that blocksInfo was updated. * @const {string} @@ -1520,6 +1524,10 @@ class Runtime extends EventEmitter { this.emit(Runtime.MIC_LISTENING, listening); } + emitExtensionLoading (loading) { + this.emit(Runtime.EXTENSION_DATA_LOADING, loading); + } + /** * Retrieve the function associated with the given opcode. * @param {!string} opcode The opcode to look up. diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 5cec44c10c..0630e46d53 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -148,6 +148,9 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.MIC_LISTENING, listening => { this.emit(Runtime.MIC_LISTENING, listening); }); + this.runtime.on(Runtime.EXTENSION_DATA_LOADING, loading => { + this.emit(Runtime.EXTENSION_DATA_LOADING, loading); + }); this.runtime.on(Runtime.RUNTIME_STARTED, () => { this.emit(Runtime.RUNTIME_STARTED); }); From 97a487cd0eff375f42b57383752f6f083285dd5c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 22 Dec 2020 10:05:18 -0500 Subject: [PATCH 1952/1971] Update name of extension in GUI --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index be9ff88d54..a10941fc04 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -371,7 +371,7 @@ class MenuBar extends React.Component { className={classNames(styles.menuBarItem, styles.hoverable)} onClick={this.props.onClickLogo} > - Text Blocks + Face Sensing Blocks Help Date: Thu, 17 Dec 2020 14:04:02 -0500 Subject: [PATCH 1953/1971] smoothing for face detected boolean and hat blocks --- .../extensions/scratch3_face_sensing/index.js | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index d124dd3b1c..abcf590cfc 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -50,6 +50,13 @@ class Scratch3FaceSensingBlocks { this.cachedSize = 100; this.cachedTilt = 90; + // Array of recent boolean values for whether or not a face was detected + this.isDetectedArrayLength = 5; + this.isDetectedArray = new Array(this.isDetectedArrayLength); + this.isDetectedArray.fill(false, 0, this.isDetectedArrayLength); + // Smoothed value for whether or not a face was detected + this.smoothedIsDetected = false; + this._clearAttachments = this._clearAttachments.bind(this); this.runtime.on('PROJECT_STOP_ALL', this._clearAttachments); } @@ -101,11 +108,29 @@ class Scratch3FaceSensingBlocks { this.runtime.emit('EXTENSION_DATA_LOADING', false); } this.currentFace = faces[0]; + this.updateIsDetected(); } }); } } + updateIsDetected () { + this.isDetectedArray.push(!!this.currentFace); + if (this.isDetectedArray.length > this.isDetectedArrayLength) { + this.isDetectedArray.shift(); + } + // if every recent detection is false, set to false + if (this.isDetectedArray.every(item => item === false)) { + this.smoothedIsDetected = false; + } + // if every recent detection is true, set to true + if (this.isDetectedArray.every(item => item === true)) { + this.smoothedIsDetected = true; + } + + // if there's a mix of true and false values, do not change the result + } + _getFaceSensingState (target) { let faceSensingState = target.getCustomState(Scratch3FaceSensingBlocks.STATE_KEY); if (!faceSensingState) { @@ -390,11 +415,11 @@ class Scratch3FaceSensingBlocks { } whenFaceDetected () { - return this.currentFace; + return this.smoothedIsDetected; } faceIsDetected () { - return !!this.currentFace; + return this.smoothedIsDetected; } numberOfFaces () { From 5f38d640a6ac65bd7ca564548f4c11d223d11db9 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 8 Jan 2021 11:00:54 -0500 Subject: [PATCH 1954/1971] update extension library tile --- .../faceSensing/faceSensing-small.svg | 1 + .../extensions/faceSensing/faceSensing.png | Bin 0 -> 133437 bytes .../src/lib/libraries/extensions/index.jsx | 43 ++++++++++-------- 3 files changed, 24 insertions(+), 20 deletions(-) create mode 100644 packages/scratch-gui/src/lib/libraries/extensions/faceSensing/faceSensing-small.svg create mode 100644 packages/scratch-gui/src/lib/libraries/extensions/faceSensing/faceSensing.png diff --git a/packages/scratch-gui/src/lib/libraries/extensions/faceSensing/faceSensing-small.svg b/packages/scratch-gui/src/lib/libraries/extensions/faceSensing/faceSensing-small.svg new file mode 100644 index 0000000000..2f5cfcd7ab --- /dev/null +++ b/packages/scratch-gui/src/lib/libraries/extensions/faceSensing/faceSensing-small.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/scratch-gui/src/lib/libraries/extensions/faceSensing/faceSensing.png b/packages/scratch-gui/src/lib/libraries/extensions/faceSensing/faceSensing.png new file mode 100644 index 0000000000000000000000000000000000000000..a49ea73a2816bac29ae63eb42d3b046066fc93aa GIT binary patch literal 133437 zcmY(p19Yabw=ewGwr$(CZB6ZVs<&>ZervZ=Ol{km+V<48?J4g3&pG$p@5{6DWM^mZ z-_FijSxI&hsj4i41dk67005BWWF^%B0B~IZ0L%>*`rkj|*fZl_1EjT>k{AF`ACK^E z3iYo}YA&m;1OWV?1^@y>0D!lDtiTfhz?~HUI5PnN_|pIY9AH+5n&7{N5KCP-DA{2!Tb|H}U)*?;*z zI{#flmL9PD68iR03cxeCxZbpvT*;Q>Dg-P zy6Gw@@|!z3Fq>L9nOQP>IRO8o1rYS&|3^Alx|x!CIoLb8@_Px9|Ca>+Kl(px7IM=6 z5^=K=BG*+?C6#b;u_WbU=456i7ltP#B^7kBu;N#jl>Q&}e>EX;8#gx~KMRYerzf)~ z2eXrlH47UbA0G=VI}1BI(?1C&S8qo*Q!ge*SBn1z`M)@lmagV5wm>&qCr8r%;F_8_ zxw{FGlmExi|EB-$r<<+S|Fh)i`af>{bCBgfEi7!ztStYF{ZCc!KU{tl7hB7Jmj8n< z%qI9>lK(&6|JV^^`H%7cPiOwy)BocBb5$5#kmY}eO&DIWpGFS=5CzCdifMX*U39^i z7|OW|`)?)XWH61jY%s06Y;Hw&Z5dAJn~>#)W}w1SQZ9y|qr*bJz@*}T7e_~b3CB*w z28TmcUojh7@9L`m?byJmv#Q2ti<{yf!n-A!Ko>!SYgs!L;fGK_nVTTOjm4*s#%{W`0~Y;2)DNjIi)i_Fr7#i9xL?_6Xiq}RK;)xyN}_^Kyk zLgdW~w>-=EE6#{@P;h(u!Uw*-JfJoCz&5x{A4wPIP7vh%p$tV?jHC(_1fHTEb*42xDs zq~GXFllTc51NNtHoLOipKWe@ea(@RDOGM~VJr>^BL-d9-nlB1g$f!U*GWCr*hLU0#+LA3}0uXJCAA4=zkPFGz`OR4bcS(@OW?Nt;8v^BECj82!>( zpqkf;!dVSE{R<8y>>|nwhNcN==8~E{pWha3*@SUtE)bCns2>MIFGKJq?|btnZ8&a4 zs83d}53R3eMun9MjHFMMJlYG4Rka#!Ex`W0oT@_0fkLVK;ga|$@?>CcwGfT^Rh^*Q z&iO!#KKilH))GWO$X99zpUW-ImdxDSzDeF1#N;CM**2&^o(iF2kEM63Z!>==$xMdK zUtUyqz8tdaYF4Ym8;kIuaU-Zhnm`-HngM@-7$Tdx@MoEKIVIH`E`^C1X1{mL*zZ8$+Tie6mY z#Fdzk{{j`YFp$i7IHsw9? zOOk&Jt|quh_SoWGxX6w{lW&ZyCpNoGbgwbim?@;dfJM1#4GK3T2Xl2#JNk&)H_me% zYn(8-Qv!xW)G95L@bix-@6ygT=Aa)Ee+~XYk|F7Pp!MPS#I4)y2(2%Bdh%|fIXh>Y zN<*1UN(@15ON$sbv!KLxyYnbBb;w382`R<{YsBnaH8FoA;ovQs3Y@Hv!1Ya4%sAR) zdr(RZgz>oGr*Y0(PEq4R87j~UfX;@A$Q2)V51gTiB;WDN)-vp&3LhVI& z0$Y(Dgl~vb_}a%NlII3jJg~d^l0$UH5Crn?7cc90gC-GkBg3Ujx%)Ai--qGS`Bg`F z5n!;}_~Glf_sDI1Xho5Xv9jM&+v=+umoI*w&|$E^c8&Q8D$$c=99{uC-U!}x7UMy; zd5_kqWF3hUGQB)K=ALQXvvJI3d0$A76Hs~@9uxVaoM3tpCv-B!^dKC=lO(`_r?Ne& zp4QGP>>;nKMw0H!W3VA#YRGhsrV-vOY#8K3lPZ$C!|YZokJ{0

QL{EhKLELH@`Y z00RFD$r~=}nN&FmAk1G~12^M)o&3wun1NcQ z|NVp?cgHJqrB5RIU!LBrI|#RmG4b@rORxUS%Y;fZwp8xDhM(#%bL|@k zDRMBPoSONLR;T`##Mf}3$45S{qVV2}bj7?IYE}RAsbNOQ4;hyy^s4UEZt@9jgOkpk zUtWp^-YA`mizcGTby~x@%YH_$#mu2wWt!xVr|;EN13F#(XC`IvV%xael}iQ7 zkiN|yM}j=3HXU#Y6Du6jea}no4A=GT8Ty;EWfOC3K*60&_iaW}b?Qsl#}=sxU;n;w zlXs3^0+6+~lwt^cGWauAb>cuJ?WZ$FL`Ua*M0wc{DCOE!RhTL7#8O{mlx);&Q;ik$ zxr2i?lx=Hmex=wrhhwjF6+uA{M%ISq-~JtEWc%&s^L-#GHxF^lnq zk-V}B<}AYE_U{Y(vm8S0F+o)eXS$lAr-K!tilmh;lG7rS1W!-=O1BzJUq>cGPCwSv zV08Db#AuDBWmuVAcPsctEdAjjtVQ>&bWWH*LeAI88+NURApIO?{)QHR?Y{+`U?9&0 zD+SzG*?+$=j~Oj4P6 z+5AOEL;bgjR23)2XRmzY#F~n_WVd=lkCy}G*#JrFT7WW%^3oVDK9yin@hNy9V~o56<`0UaQ}no17j8opSiu1Fu!oF(mGIg z)C5%=E;LZS5=9!-8$VQTiPDk^-U3-5D*^8GIzXOUwJo+5q>yv3(OX=@NQ zQ5-futvt3CGENz@MSyG-g23dPQ-Z}k>l=6(pn0I-JHeU;PlmtauSDHib0e!x&QENA zNdAG5n>fb#B}>$(#8EdUC((t?oMgEN9KUrj!r63)&?7E#g;rA3x3qSBv=V}kG&1B} z4gNq|!Rdx{oq@J-Y;Bs_)Ah&=_fwQhTfxHjQ)>_siVf*9rd%+u8$Vbw_1mbY!;A!J zd?VZ) z^gM+2U2Aer4euSUj;mY~x4v^Ubj9#jwT~onH_M{FcO6O_$tBmQ)LcEG@$f0~L~bPD zvcWX=d}j%`35r0Th_JKO!oAWB=|C&J`J>b{JK(DJ@X%pDLviB)F9en%UgMV_1*S4V zBf}k~S{HiQOU3~{8Xu~vIBpkKOleP!P(e_^%CKi{j@;2}MTv{#{~?0meb8z`sUPjs zru1y`iGIb2Mfh=T!)K%LXocy&TRE30eDE5^4ht8mCI>5tih#E7MkeVEsK5^@K#D&WSQboj9 zW|UY(G@mf}_ES^?->jGtn)WA>89aq(yJ_O(Pz-NJoARZHj< z>b|n$>Z%aI#}en1{TX4B6uOYYliLY(>0Z1;BpurW6 zYm{G0DC#QPEJTYVZbs`|q*u!t)5XIH9UrP@fWvgG2uj>6PYh?E%s;_2tR*7=lm%M# zxl?ww#HNT-!bCq;X6(xQKJz(#$B~OZR^hngJg`QirPTiTenSAArmK z5fwsiHr_~&7IiY3-sErywk?%mC|ve;?;(sEoHbC%5o%zqKdl^cU_r&T5U*zA8HlDq zXkOn5!y2OS3mpyW2E=C4ohAHj$3ROQO#se`0{4*E6626qAE9+#4+33I5EvoVuPEMo zn0H41H=gMcH3T#6DD^ijo~lwD{8(H;xKZQ;yDoNuvW*GLt$X4=FbyJaPTrVDngR znU|GD3)=SyHpGtn$n6+v4MnazZL#dg*&#CByhuBc(nEoJm3rU!=kA-IMxr~~5REon zh*`QX*W}N%z<%Ibr5zLD^H8I@YON+D6!SD7ao-+t+O%rCGXu+#+VBQhP(vo~6lFlB z(OQi3aA)dj<%4LMmJc)L7vO_H77~J*HDnggH0jWYNM9_LR>~OTJP$q)0_0UNp^xdH z3AhP>ERqp4q^zeT&pVewkV;S;SNH{Z)8&Gd%4!!v&M&i{Mu&`mO3gZPhLzOL5x%h4 zKNG7H*5!gf6PRVdmw+YWHrFNKC(8@Y>ID+s*ITAQ434~TAlWQ6YsU(v0b{AbZ7Lbj z938&%Ed^5Nj{y7H7X0E6`102KvnkbQTFE-qc4|a7p`1hgwn1Ljrb1^mI{ewzT6;x{KaN5s{d}oB8$;EWZ}eMyj#(wno36$ z%!M2nM1(%|=P)7Z|8YYMHt)?-qhG(4LZ z-&-3H(i+elSnxqA*kiWCGe~)G28(*dL#R!lGIpI#qDB+2H^QZRy&H@n^T-xpV=4qP z><*`dH&28YJANdsD8ljJpZd-MK(R9D_Vy#xa*IPr8tvsFoQ5dr3P6I0``ynoc`hOd z!s4#}$YO~)C__`AWE~#60lNN%Kb)&mHJb8B{A-oIr{s5R;v4ZKg4ST*m|VIqt|W=m z!#lku%{)tFRW%~RaOf+#KObZJ}Dwz^3yfZ6n4f5Z< z{nA?sg_;d2C#Y>hi3@^v6vvB1)G+=TB+J^HzBz~d?X~E#BeT#A$0n(yZZYEiMNhYt zPc-h4=!d9(>ZhSM?|v1XkhPpF{N(bm07|47@Y=lW810Q}EmY z!gKacX*uuAlSl&oqE~jCf>|{nXXDg8>3LcnjbF&bQokQzjA-9laI=2#jPEo~t!bN# zIrF(5`hFO%?~%i%iuy}g=JzsbfD_3l!i9C`VBeS;bcl6f`}X>;jE~@|T`_c}D`HYg z0&Lt@noeMmB~yl}hkQHHiBA=6{=enkiE%?l6t-)O6t|!!K!rAcv>jhsCj7oKrM<{N z0{ZaZaq{=9{U$r`tQIqm2~lyMCRIF;x^)xS0w>z!X}O`WRE`_Ke!B!J!eM-1$fJo9 zgKjgywy^2d4@fwq3`LNM&u?`j=UlPL9Kg zmsNKM!0xlfBkSF8wyX1qK(l*BKU#HJO*0KjiA9pepM(yoi*JFb{Wgd=gp1eS*3OS7 zM(L^)!5HOwzu|+^yrCRsO3Nm6Hd0@&{c0oh815Q((lf#1Av!iw$%jUlck=spCJgA( zkGUoq`~8y8{UKnv&#-9sqGF`&BlbS1t~6a(gj03Hv?>_rh_8SQ=2ZRhT^PV&XSJe+ zLU^5l@8mXAlAj6VcYHYe4)z_PR-yS#n}ub=-b6gRzEl-s{GqjrN4%P}Y6O)XoiW7B zt13E-ku1@^fexJl8MkAl{1vN`S%)n1$fOJnJXxdq-V_TdXr{_X^>xwkU}v!}j%dBo zXj|)x>Jd>pOs*`e{tu5XUd+aPr|z!oEUWFjKLKyL1k1k-tHm84A_d7wT=^ox_=YMy zkUfHNZ8idm6xwFm6ikV)xb^rfZ=_K!G@MEnQW~t%*hL&lfI+f)wjs*42~XdT%kjgl zBO-)QWO1MOm9bql(Tg@QMU|Eek4~UwyzDmR#cgf8H;_Z}RmO8UMY;g4xM!-g{WkMT z#Av&^lLkk<5E(Ge$~s#~D6;yllv{d4-?+*VNVn)7LgHbsQ_Em{KF*JBLXdE;GC-|Z zcB2Au*ve)q7DkIC>-X3yCiLrL7>&mtJY31faFWado*)+l1&4lHY3HPKOG7PfvBlrX zUV%ZRPt`(SxYIxoSHX&aQ~iqvvB$UzBmRi~aOZP&3iHhEbD_gY0=whRWf7Inkf9#- z%8KRfW0r`oXd;>_H~|(bS2vPhAhV$Tyt*eh^V!w=??EYP8%iZ2XDdHZ;B@Q}q`!)3 zW-T=)0&hTNr4Cy_d$Dpd+Jlh|*-dIt%)+1C(45;YbL2i+h~N~{5;4kvgbuxFd*jL* zrAY{6Lo~OsM^lVH_ctMq^YQWoeMO;ta`ZoMlSVgJ!GbhM@MPO8drHP4tU4R8UEo$9 zula0w4#ovwjL4L@jHcrx=qyz{(XBE_YI=y84;Jy^{s7yJ%LrEdYE=SAj+d%#-HLn8 z_++TDvrLR~&LolP2{w3_-9fPml@}Tbgcd989Gs(a^n}1MLQ8yzvG(@ADG67KgVSu} z2>xhQ%&&@_nlN?O-K7Gx28Vl$zx{g68h@h|XG|uye>9FraejzXIJ~KcvAEs42d+TD z69VE6y?STXwjmn}i0@`kff;VO5Xyf2Y>YtOKVC%0ynmr$f(V6)H2V15Hvf{WB-P*e zZU^Ce8(bmC;6>Mb3E4V)ddyZnTz6<$Ucna*iIJ$_D5e^?j17>Zc$hVtGI>C<^e?p( zEqW3rF2a|AEn3T_F(+jkP}&RZzu2{dVbqeY-Li;kg)biapBvN?jQUY7nQQ@T`G+o5 zc~ZAYBvFA>RO1!L=7ucA(&!D}Kd%R@?o9(W)O!q4`uD3ulhDnz3jv<2Rd|7%uoPj) zp1U6_W(enD4ua9h5?mrVN2$G4^VM>?yl#^F<#9I_3o(JwBx1#eNX$?*4S-O#Z>dR0 z>t3_mFr$!#u*DpM9_UagL8^jo^$7L}I@z1BlZj*Q+~V%O2poJQ(eiz_cETtZ(Bikf zoHga7ArUAe95;5TQNnU+NqJK6IK60Zay13C*2cz&yfUMWALv9w;%^^zbKTlsIitJr zZ<~ePKf^wRc^B)4Dvgoi)^vBwbL;9LAIv~?+{zY!`7(^lvgoeFLfLsf^c4#j5>#*8 z4Q)i+RO%}tPUjK8Vko1RkiCrm2g$5 z*?8eEG9PaMM9r#1NFOuOX)=30se~OcHE{8*TxLm)Ya}q3PG>}b4}4vqkekR_6{sG# z%r@2A1yFstu($5gN|UN07>ExEiB4reDzuGN&%(n}Ed(zqujW$2VV_{VgVDdpSM`D7 zSb~!fK@giRnXBi_A*LKc&XuF)GcvR-b8UfM`AR5+I*0~Hy-RF=3zsU6|0To; z@D9$;a^jAx%nL2W=JUS(TQgewDS80ch80W)X~IDnv>FM#Q6OI+ZY6rtEgHxA*-p+b zZ>b)QOKI$jV3=(O2Frw~bq8xw?C5Iu6e)t!er|sN)({~&taqTS_KhlwvXn)KJlH(G8ui&zsOz$(ZT`U>6qfgaVC%A(eEmg1Wc8wM1%I2&(7 zu>yXCAqNRETC(*GX9o+%TVlY#Gk#UvYV6VDE|C1RNVk`F0GsF&eRRf0`<&%((p2K0 zg{5=Q#)}wYHMf9ix(+rS*UW3nM;L09n1m+G3+A~zmwJ-!#z5nvsxx_d`hi|R7VF73 ze%AD{24(|`0A3vdTW1?r>FzvDEVyJS;q7a$Qr ziCx-}J1c|&Ger-eQ8Rmw6!{7j`xId!PpnG7lW&rA<9&lDej_o_wXG=W_@Xt14${lh zQdWs!M1`J0PW;j*jb-_==zAGy(S!nA9*#rs(!t$DC(vNu5hYtI!=Bd~_u^1t@{NN> zsj@!lAc=aJrzj8E(2bm}6M1K>hueNn4$jgi_j+PreBzhk*xP#l()tRzx4A( zF&N`!Bi)8K3(3ADEne22BrKSf&kEAF$*sxX-lB-1NTxORZeLns8yv3oA+kat}^@XG-nY_rHU!m322j>G+`XPo%$RZP~+6} z*V56I#ne`4qLj`zXwM)DBJGb*!4PDum=c0=sK)kc^G)>{2mc9GxO`zMLpnXMI7T>G z#AUQ&vQLMrF$<^snPiS({hTP;F&>4Z0pd@4?_sR1zPd-WyniV48t!%@>cG3<&~A3+ z3Y~L-R;Ol8K;z^XG^L+!hbvhyaai#P3Ogi2a2=B38PN1e#}H3v&)d1xY%Dd3en&J> z{&H4qQUZDVUzi0MB@KiSc}Kh|8iF`tzwpmVYC{FshseO2zcZU3)c0y6j626CE*uKS zddsF%kA8%O#U0L9eTy%H3yPWt9H#6MihH*Z8uFQ*R=t3yes-;!f(AtT%70xC2+;SO z0fsZcgN5P?QCS(%ujX3&@$Z_w=+dPWO>t%A1nmIiR??=RYgc^vx?AiqnMXVZQuOE| z;|$OG#I*KC-iUPf%4r&izQrMyK0xDxlGW~L0uTq+55(?WE&{C#GFel4N5X31lN4(yJ zE%t;}!j{QgCF4NbfjRGvT=kviI*8I$Dapzx>JJ}}g-Rxxyw}XV;bVGKJ8fcp0>K%n zJi9J@UzjWbvp!gD-DGIGAd_>jvM%hQaXS_w>PyZV39#3iCSv&A!&M!rL!FXzCfj7% z*bno(N1k;+dXI6>ds~{Omr8}#yX#pT(SrC%M!d!Ta){)F^KXBJOf7>&bWYe^mNbKj zWile_$T}^T0q#xY%6coSw#=FU=!I%%1XTKQ4(%Fc(bjf<_I zVugHKqfstZgwgmPl4|s>8%(+gP2Zxm?T`^g0AYBO18rY^Q>YYs7^9VoNk!| zLfRLFcfY< z-r~(Bxc1&|aJ!$!ky#z9-YkEAc}sMRT0L11P@`kZKS^Rdr7e@K1FgGXi?S702ak3l zzgFUY+RH0UnSJgK_r2Pz;6aAv>MXIdYt7RVpJJKPW(UvXDGV{nm>EKXy9J`w461uY zy)W*dhL9QTd;lV%*C&gyV}iTHw7~FL{y+0^+SWcR+{qxSW^Y(F0V_r92sj!DVEQOg;t@?ojIZB}R0gK*V)4pOF5k=+BoX$memg8 zsiSKAX`(%yOfRilYON6_Wp;Ycs3JtMqO)aMpWzXJHXNS^#7EoME+taLA(pud1t zt^J$Dy6_^dd@T>)?51MIU1T!J|Ee7|ZUb;EcBLs#eKpb`B>JK6PoHG$ElepEj012w zeJCOndW&%RJA2@hAB;nTUyF?V8;$4RGmi-UZ|gt}s*ZaAu=Fx_XE4t;je*{j^@=@I zo-%hB>tuRpIVglNH#zTq-DD3IOy1=Frvr51I=O+bKx$>;1dLN^b+aKbQ`M7VZT5iL zQ+`1IdHjIKC^$Ik=f4}`uvI*e&G2|1dnX=NWwNY}X1V8Ri2mH?N%A)^ArN{kEa~{* zB`?1`iDJGsn+a4fm>`N3_}>zdXpD$Ga23+@?Upvum)=P8Q4cbW`6E$58t)kd%YCx_>_*znzsy<#zd@jMJ2E zMW!(t2uA$W8V_t zI(j&?VT7cuC4`>>$}?REQ;*Er>S+}rzeg^;J+tV0U;c+lMJt3)eli?29>aowk zXp{hVOf%0e?}zv>YdO6U36_cw9=AnNx1ohxLT_ObJ))bngJez*kzQ0U5g0%=>y|Pa z<-?){B?L9p+3s!HJ*$wK#WdiPG_M{~e|GlHpRO!VB2v)v*a&spZqxsr=YFj5;Q`IL z3XL6m zMX95g2mX^^&=%TP$rjnhX7`|*IW*nU4iQoUW2ACom8_F0Zx#7cT*V>SAj;m@?}mJlhcpG@UHndC zss4H!9()gEW#e4auntf;9UIZ26K&GU%umf-O_wl*1sabs+F;ig_qI{ZkPKcS9blo>1& zPRr@=i23u2GU!$(Po^Tp^qTL)4F4Y&*kGfxQ4jm%?&0k2dP>wY$PeAi ztbV#!t@_SzYRs9!XOVT2c)d)N_sbltJIVD_)vjr6v_~W*c56ok;_%BMY?W||sjn}0 z0ZMa4Jfv8{)Z*pGR|ZKLSi$E{8Szx6p*7Fer-*B=-?gc6L%S_>1*|C#e6IS%-T8^r z+9ri?=Wmm+B-LL9j2v+TNsV!``(_v{E+f_ck)gQag`VGDJ#`zuJn~FxkaiAyE?57Z z(~Kz~Q;l-nfG>nWwjQ_$ctx906Jnr1x!F@1=7bAfh-6DA$(PZ{CSRV%^}bo{NDNE= z9nP8Z-9A}44~ctI?;&zoDhL}54H+018wW@_ugmo~EFPs_1V3U&$#50EN?sSbA7IEV zryA%b6I2mmFoqBk2FNN*Sp|<#gw>y^^c)EbHv)jQRX-dugQv4_C~FPE(O9T7DeDv< z(XRLg;H1_`Yrz=6IVN;p9TcQo*ybooJ+c_M+^eK&^BCs^!i*@G;H*cT&{L~<#?_}V zM@E#(MY>^v=(?O%_Zw=^3`4*MeSF`IY_HvjFz)LCDAnrM9e6_~VGJSy=>olw)I}sV z&rESAAKnO*b^2(^w5MQ&{>85tWh386g6k&i0+Ta>|J`=$rq80^Q2o2^AF3?%RTT0) zi(qU&5)C44kPK%+|LHz~He>f!J^9t9v(2dLUH=iU@?ev)%*+$!i4wW~eAB0ArmWO8 zLo&3-_gPyHKwVj_BsU@0iggAEDy?28o#^(vW#)6bVrvrn#kS#vxE;3 z*PPs7?`jK@Xxpo{=FoWP3D@8H(?KJ0`=JJ5tR|xf4LTCx;}8zTCJDqaBse&TG}MM( z5VIrg52s$Z`*VvkDltzzk%Xr>f;hf&P((O=aA4^weK<8ioZ8N{khX|adbP5WmvymC zb#BTJaMHpN?Jf-f6mm{`8aKTtGF-XwGj*kvecHcOrzzIxxaPzvP3cw#VG-ameJ>M} zzzwgt?^~;#II}!2HX?fXmS*}V^b6a}8)RIV_Qx91{PmJIzn%O#r7^f;1)PI*;Natz zywkNZmxWRL&oC`*e=;_Sd#hU$H+LB*paXyd3KEC#j8)--++KaC0y3`0TThLtW8k)n z{I7oq)kHuL?<_~y720W+(!qsiKgk=#?$$(I!IL&_V%?-35z!g%`SzxW$JW~V$B(op zmSedegnW0nim2udVBGYU?6fdyDf^nkx|S@|@o2)6!0tb6}H@V7ekhuS`Ig z>6^mEE_ls?!l&Mo|DF=Kd6$`EoPI{eTAow0oS|WM!zJ5S*4O^Q%gT!Ldv+ih?(S|* z_UPCdPK(@!LVRJdFOfp>7N+bj-kaOwcLei%?C2c+U{*G!y6(M{wJvt{boqhMXOILb zR)-pab8FRk+u~QdtMv<8R+(>(2h*M8o`bHf^A!$|cT1q|9OfeCS`zVa#{Yd>$l-v*YPW7y+yjRSoz1XbO(m&|_FH-ATCvEJC zeyr&SUD%E?IHYaG`(QV_>w+tO)d5$@)Qe$%aJei@=3Z^h$2&$_a`2~6tc^I}V*B7_ zTBm=$?i+V!3(WlEI&OJaUnI{$&$s2hRg{hR69`tBxxO?O&^=ndh@LnOZ_2PPJwLm= z_qQJSf>to7a#2wFYJDtjtC~Un#90BmMzPx@=hX8c_=zD+FY31D@90r+PB1|vtdh|^ zNDPE6jD}nHNrq3&Zip?8+jJZ0t?(c`p?;xbe!8@KP;rqSnZ#+4;GyAfOuvq5kA@MF zhCM{ITCjF)8D7D+-#`ngDOL;W1i;-a%mR4eR5(O;BmavfOtyL0BcNuZyG9v5#18z2 zEmi52SH!@!3k5k!m+=8eMS@?=dgB_CR8Tr!W#~~RIS+CoP6iLXl2v8Acs>T|x5N^* z)VqV17}US`C;3mIILj21MTGACJo9B}b93US{*T`C`&_K9odSaN{%`f?y>+j_a|zfp zO>56Wx|iGMlOt+96JqMx@S2m}<`-Y2v2Sgos4av(7N}iP{4Q8E3I^TD5L%;IXV9BX zLCRt-UH6OIHLed3;9l@nPxt-XUi}X!bZPEA%Lrr( zdC>_V3_^o#e_AE}8RK8qWkh|a2$Yev8p#5r^4}pA)yEVDg`~Z@9Kk7ZmN;5kp}yx! z2dmMH)P;A3DPb)xYA|>jgCBt`;EmvIOU690aG*}43ldXMV|0g~U!TR)`^)4pWEcaL z(}OAPd^qsOVk35~fZ)`jzf)JK+Soh61914F2O(C*wWeRqk-1^=J2n==cu8ctykf8} zqTYAmd*qw!Fv&$jGYYYi%H2$a*5*S_vG3@v0DhP+AdPR6vXyQ0`Orx!il-&ARV-SZG45>BB#U!`; ztlD&F>2vbMm{+#<4j7GWSR7MgWbru%jO^1Qs(Fr^K{@JNx)4^I2==rqOg6VCN(|6v zt~!^&7U|JHzQCBT-Y%omut`V7TpWU@6eFon6V%^oq@eneB?j;ud4IS5amVNG&s;VWAWK+kUyowq2Ip)B%@9EqPrYT3*R?pYq)_X7FI{w-D%( zPI^fwksc`sfaDqQuj449#&1P9{nSE&+h<*;mGx!t8)3|tUkG4>b}OnU20LcuxggeC z!aX+acJjqZ30)mKbKE~r1ujm|678UINiK$KIMzR$n2S$6!?As}l0#MXGy+~Mh;5m@ z5jQO}>|ahfK2~?*z?RqA5u0gz_}9 z`1*pz<2Mj`?D-AyCJpl-I{NfplN)Y3Afp9?A#>2b!{CHLAbg*0b88!fTCf z50A$HGD`2(OgQ?FH~iA;x5-@WP_u|A@-kKcJjCOOIiri^KD9;KQV%eoU=U9rh{7=!h>WnGD^yfTz3q*+No=Vt1mAUs2guoX_3lvCPYh z+*wxbNKzDSMr;cGssUnIQ<+fw;b02fOCD(rT!=J@f!{ZwWmEjISRR31n1W5%obM57 zQdF3q#q~r;I^{#BE&Ptq;l~=e=hj*>cG2A(brtw7F7h@IE4|niz%+mVm7fS)ok@V# zo>xgo9AAYsPzPZ_9>vH;ym2a^secKeJ1!+7@Dp$3O zcG+n+_X5WZG$+mVruIo|q;U^udUzHm%SfIl*;2KeK2EicqYvv1R~0Vmi?DDjkSE#OVHpSZ@{MUtJ>Po=QAeD04ZqA@&iaI0l2H`j`{pH!o!n2 z$t!;+5zV1!(ku1s-3jOKfH6b3(S|u&7ov=<1@j7P1h_eFK`1Q6-)`1dZr*HX7_Rz< z7J}NT*~YS&Y7@0`>w%jQX+E2RKea!#YI~`xILTwj`)Ckz`H& z^mXExJN#MJoNxGh{qKQkrPsLdk$8?>5Y2_=+1?7dIH2`kC`4JO_QJe-kYaDGt71Zz ztaM~0iFr;d{z<$rh*z!SPqE5g#s&u}!NwvI^weXhSq2+QP2S2K11l_IR>+msC=5LZ zVwHQeOT+J>4^j2#N4+)**>ZRIaJ-;h1Jc49dIEPE&Pgy^`_56H-!E<}^g@**x<~4N z=q;zkf=4)}ybeR?-_8X5zM_bDYg@w8m6qRJ>m`rTiXs}^=bAWd zRJvpnFB6qfbR52FNqI<-X)+$)j^SCcq+`c4V{nHxb^4YhOMbj?mBMJS)h5OBMZ@#; zAEY)CP7z>>-sV=T3Y)8Sx*Qy5b`D2WWK=6MF;)R~^;*T*!X>@HOM=DHkfZt;mnfl2 z=0j^@)GL#oY9heo=6rJGTuO^1d1z;L#pYix6Dz{RyD9dAM9V1E)F=3 zbeURd(%+YZJ+g4Ri0&K3B1nB$PdMHT5zM(t4*@lU;26NaI5<<~SFwhKj1$R8;&a=XO zjqpZS5Ch0O6NWr|Q? ze-GKQ(zg$-Qw+QBFDk>0V5HmWjNlfn)MZa2FZlHNGL)#tH9W|BHhxT;;_6r zVuMd%)~2c~=^Vwen~U9uCNgjlJ)0sjfm~sqSy6R(4{e-FAuj3kybgnxXK$j-ZHl4k zL_=c#<{Ug^(){ocbMpE$y4|F~^o>A7@38C&;+P&bYj?E75YP66I4)7-qm?PWbVaAq zono1?e6A>&4W#{AOwA*62?pS3G*I20UK)uVYx7&pzLV{}i=a<5aL0T)NuW%xPw>?0 z+=vxF*<0fWZ@$&huc`MN6;@H-_Ew8ou_BdOXK*g(V187+>WK-Q$BXX3{N45btqAslyEZ#Q0L<}tudP?^cgqfxBelky z#=WZ07EiDot;5RQe&AY9MBrvZ2Os+nfNuDY{tkk|?C)9d7_$9u?PmnKVfWBqa+9V$ z;MK5Z{OTf1SiG^x{l;jtvGuA>-r_i7gr|Npc!^v2IbtPdqC>hb`Fjc`3^Thb?HmJS zd6eky*FMDD)*FQAu+x(Dv28(hK2+VSm%k7uF;PLcu1R&wSq2R0=1M<_GAQ`ksL*c~ zUXK)^pWO>GCoG+bsIdvCaf*vQ#+nuxr7dbGf_I<~VZ;G>K5CsXg-~gtEdka`X7&f?Yn8s5;H2 zo5QYxJB4v^pPLe1xqy%2&-;WqM8Z>Igan*>*m5&xaZGpprhj9vEB~k7>2*|)oIB5?RMV!UP z_qjt==4O$hZo>^Wc3cA7bV5kG>|~S66H9!(n1uG+PBxH~RxqJQ&BOO#jLBFmr%6>5 zdK*qq%co+@p3NvF&SXe0EaPIIw_k$O=c6=efb#6gy=@!dP39F2bj*)a1MT~*_of-f zvD{?{Z;)yH1`gQ4Px55GJ{_{WQ@|X_S7-cO{NYjW*w!|Ywa1E8%;(PSffjg2nVQM+ zXrl|ms5;0J0INc}oPY;?%d%jcKQktCEIK1=+GF{?wnG%q8JDW^85@sMw%#y*$_5*) z)^__~ZUOTuXM$mk#4WS%T zM#^y`3=B6KO~%{$wO~>VqYIe`JOmcwgzYqFqx|oStVs*x2`|2f8Si*;JJ@LK^5|f8 zR)=WZraaxSrUm&{^4j)STL;Q&d;OLzUJ>f`Wnsi_7QYkTA&y>_C&b;>2^?O^Sh*OV z`x&X>t&9onpi~+Z0{0Gx5-~rxH?MS@XPueOU?W+Q!qlGu96c=7YtKTElQIb!J`QobditzXCWn?^@-uz( zQWw_v21nl*f5`(VTB%fsZ8#9ZxdMX3#tjGL)=Uboj;bQw3y5AXW_Lc6;w^c=Ka0UgtG7Q*0kT)L7F> z0(T--d`v=rBy@C8FvjIaPcTxtQ();d+W!GYK)Jsd#I&4mddi8&>q$m_R*>%KEohy! zU@UCoum7W8%H%h(sxtt6xSX2@T>Smi8)N`0D~nYKqc2zp%eVzjo;V6R7ZN%w{PB?i z`78O1w68!}i!g#EIpvg2Ps)*_kI9E7{}|UOQAWjsusrG5C;Ja0OMd2LoLaaIjSkEE zG9L$nB~-WG*l1(WgfzbI9vT1Cc^DWqO5t`jtNBg>v3% zQ@BsM6{EWlIC7#-K2i5&S+N3Ldx?}+-8UM>`yiz-whc+4(3t}eO5HFpFIiV%5Z-p< z{h^@&`DF47GIvg!%$wIA~*M09HyaS0f+nX)8?t06+jqL_t)Td`C%46;>gcjJ;x;Td*!H1O|>Dk-8ij z0PbeC2)dA1o^x;Sz4FuP*ObImj7?^@0U54? zCP;s`4uVx@#aMdCHedy6Odp&2V{~UQpOgqS z7{xuDwRg)=s9NVOw0aM)D-VDwucgd7SQ4g|4^O^L4jy_yJGoDkE`uIHgPjc8w~Qwc zs%276ZxGE`8vSGFmkGOH>pachm{S5tFqWoA`oI2MCcdJXlgK!(sga+h-E>U+miH)S zoxvx^wL}d@wo@}G6*$tAX^vRpLlz^sU0GZrUPAXtCYdwWrVnB4G&W*QywMnR1Ot!} z$45sna;D)0IWdciYgZaS0OryM;hBCxM5TVSMFuv$#AB=)j5Z$qe0bN$dAuT!Xvn}0 z<&2b+IOf104@qfAnBRF1%Zphmc4$*P*IKTC*}CjCt)-5mz9Rg8vKg`%u{U& zmirJ0%PZsYF9^d!g~rZ9$p6?lEbJC=49pgflAKU}c72?4wLm<^in|$=6PlD>YL%iU zJfG-VMt5OKWgaZ($K(+Df0KA5R@SuITQaJhTA-kYK`u+Fr_*&pEeug~HIC5{JMovz z4U#4yORpM|#TSZfcoi;ggQ9LieSMQm70fM~A-s1=t0*|p@3-%fKJ5O(1}f?4sRDEj z>eM(tEf{EU=&1DWL*7g4=?m}&yS`w6AoYU&&NsKu#|4&UqQ{vvrxYWz@pNPGRL*Lu*62_5JbL6& zsn?SRDC7ExR7`_{7jzIPBMw~-DFSU~Hk5|4FoSqn# zvrxtY)$+Phc;vEuFd3bkH!4Sux>GX!!ny*Yu_?%u2pp1! zRS2c#mgW8t>TvdvK(3t6mJTwT*YH~A%#%e4`)E!hur>~hy-J4lK`tapX36v#5;A`+--}f0$}gMk#EBfU>(eRe zXHqibj&xECwM)Ct{1c67Uh_(yp zBCqj$XZ26@t9l+ky1$UvMu?6V_$zDavh&r_936!DptNn8+Y;GGba?YQZ^2^oQ@xX^ ziZRF}RzNh&U7n9SNs~%Fqz;Xkbu07kz84NG*Zr}x9Zq|c#Z;gNcqu7U2a4rWCsXxj zO~(N&z8lQrUAX*0WXP>3e|_b{m@ngFr}Cc;Q!P{CKTHBljC12x69H}5tbRgc4;3L;K3x}LTyb- zI%}Nnux(-ZY(ImK8BE3R`IgpAcN3zzPKLW_-7T7}RkYEu0cqqq$RaQyy#^B``05WE@H9T(mHN7mCzoB-Zus&w$eb|Nu78&k zhAV;_mm)}^z8gT|eR4G|r}m77@QgARs4vTwT<&EMZ?9T@P*Ny&nAACim7p9wO#~P# zOdrw6r>9W(2xv}@BRc_NF5ZK#IwF$mwd#3UtSIm#PbyU}A5DD$^MIZXX~SYF*Yqzo z(Y=A`Frb=zBqT1%?a*TFgJ!;VIz^ zcdY*jS$K}0SYh!wFg!Khv@?oEw`7tzbTn0{f-E69k=9g$lVC{b~_)`66 zrB<18oRG&yILl$g((}68Kyx1V^!$)Aw!aeR;6pd#CK;{{_5l!5LYb?Keg2{5hCrxF zKbd#hK{&TW1ycRt9mhB(TaUreh|C-CmNpQ>U_R{?W?Bd{MKB&$UUCE#29%E9T6eoI zHIBBHY>74-1o5qTaZU~&hVeHKIeB--F4=ReFl|cQOvZ0iDmKlNN&Ca+lQCWvB!*>a zyT1om{Sqkc1l}e=X2>%cq_dCXoL~jNG+E^1;ALXJt4D<}q>G?<6DUZj1P7)~X5#F( zvV)kJ!FwGLd0wSq!HPjuFIT$M=;r zJCKS$o!-6oN<)4+4_(3hS7S5DJN?q0^}Oa6`1?;LrTp43nGo#WzPtLDvcKQUw>jW& zA~KGBayjph{0$8r*PqtsB!)d7XuH{`LYz8v63c%E9frzqPShypb}-3psQ&Ie*ZA?C z#FX2e@VoaCh#%CUHbZgKO$?Yh#^TMYYB_~b7;A=-(zxY5>D`Zl7?8$_@f+uUJKtD> ziZ`W{p(`3OFv+QvvmV+h|1eVZ=ax@Ayj)4e?%lJ(1(~V!1 z)|SQ2r~8{+z84xlyIZ3?W(Z~z#gMMRa-xU+2&YVMg6;&radhW-oy?3J{a*Qo)dKv; zt+IcF6vh@F86UsvGUzEMWbWKHUx;L4T3)^9I_yj#1sveyYIBhl0t~4nmK(=>Ts`Nc zylx4$)tioZ??3*utbE~+Z{KYoi2KQI1R~2;L?GS$WAGcKu*qO-7+>vamz|l_3Wqk2 z*zr5ALoh{Te51y~&|{cH`uyCl%aXBNXJ`ICbXKav*8@n ztMVaZy;PsEN;RrcnPd&4(eL>Q&Kz5ISwY5Ge-9j;mamOoqu9$fEfS1mW2z&Wr2N;$ z+hiK{svryu9FVzZACZ<-L7y_dQJ$wUAHA}@uq^%9F!J7sKa*u`ZRq}^I}A=g1tqna zo3+W~@}3nBN_`65wE^6IR)7A(k}7WP@M2BjRV(t_gH6s07WXZY>&EPA3L=#E3G+h3 ziG7IcNr$+IRJc-^ijmlNyO14gmOcMHA}cYXvYH!UWBHH1xpGg-4U#qH!E}5+xjdKC zL%Kq!oX<^8$d}gKDU(^)Yb4@ay&%L+b*o< z`pDFKW%1lO(qJ)$GE8u#gT{b!W#seqpO+;oE{Bs}Y%<=VL-)$sm+H_ZJTJoF-iLO{k|pP3*lcHnEX)Hn*tGd? zL!0I2+4V9X!)q>WKGJ_oPM}RJflcaF^A5=K797Cx7c9p~gf*0ea1%%SR%CyX&OsHQ zNOt#sV6Jh+W!8qGnCBRF_KdwuewyBZd}pKyQ$Sqd=_u=tI{B|dd?H7`U+wn(74$Ac8aiM7sPLgU!NyD6V zI9s9>%o)}|AgKp><<#I&wdU%XW-3L7+}#^39Dx$W9SM}v{lz&x2e~?S+bS3~`>--# zk+ij~wzhg(9Ywi~V$a>s&_1jN%}V<=gd{(jKx(l5sD|-*jZ2=VK8pojtFS`xRfq%K zh~HRhEvqnsVuka@jt9{-ZQhT2a)8t{;sZs`|9}t260&g7SyEru1jNCF;Zz2Y*$y4L zUs~4SB905oKrkaw(D_>#EAE|x)~j=iMP#4T(&S^Yx9Bh%QRur-e^c;LepR_HnJV7 z5x%cD{^U|Qa^STxa_1K?*NZ%&!PM3!(cN`mHAFpDSkxg&FfP*OMu|yGV~w8Li>WVk z_1F}Pk&84Sx%M}H9z)M}yNC0F!=7p21rl{o9RP)lMM7~7s2I?q?yJsX5#S%8SB1t# zb`<31!axb@@q^Dp{%*w?1gMbVm6AT*B-6vSr~?G7n~>W1!#F7#cIY{$P&(Lg*pMk~ zc>3ix z>lk7*pmndub8o!Z2@FcMx{XZfP3(Tjr3BP4cT4klCqkE*S;xu#OGborp%jcA$A$^< zITJw8cr?Q~uCw=R(X&Av6XFmDVM=8xCQ;_4)%o0I(1o!wj)SREdDr+a-RGi0ccU`) zcok7Bwz>iZdej9sL*lVZ4AKWR*?${O-g#j$OrHOyhn#ZotTFOw*+5Z@^ml)xYz!4g zq&VeKp%(BnRve`|1<6q=#F2uz*%-V+{9pxK&H?If4p*BF3OS~^{b#3 zpa%#6uPdBFVKc`=f@xm%MFEXuxn=5WCBliZrI>5g6UE2dpxEhc89Uzbb$aBbU zhQ>{*HOryirUx=jn@*`v&P7bYG`7Obz_ek@sS5Dwxb6<1LYcOLQ0H_x?^Qu=AfZIu z>gM@@-@yd`9foIxuQ)XsM8OFq-*OQZsqhjvoceM{)Rv=)d|?6~`Q26l_rgyqtmQyG zj4zb{g%z)~J@7A}&)AV?e{Ggzlv z8Qm~U<{-Id0$4}-LQI-Py1>XaC64%*FYuhLe z()Xi;I;~*Ad7qPo7rY0)UL2JZteWqc~^)R0PFS@RzJbfUT7}_6B984ry;K8=YYe&Yw zW9*u#1fpVHsA;-F*1h~rM*@=nW*m?R^+UovXX3}pOIdTXDd>m1_pw4qmK2}*2bo~{ z1o9uzg_VR9fl~_$W2B*vQBRo-ref-CFF=`CDT`&}O045sg@F)_kP4uL;a;FzwikbU z@wXq}{qUK>pHY$c%8cHnVkmvfd7I&1q<{bANCVHzQ%+j!5|x@!V$vxz;3_Z`Q)Tlj z4cH3PU@ZQzJ5YB7FDMO1Fmx>@wbQVOo3QBhpcR-GON%AJ3Y=kRdxet#j;&U_f_iFa zZ$>~dhtZ~vU|>bwvuOozR!gE*V2q&v>Q=?mHZH27kCB}Sf=o8Q7HY$Hiw3s_sB1@l zo2*`?IU*mhi81 z7y^gyRRJQ$`Z4Z2x(L?qwk+&c*moRU#!~e0zRT%^-YnrZ@}Ri zy&j(aZmdu4baojhm&5%q{&YUPV5PmWg#dWVd?}#Rm@=5rOoJ2MlRf_WgMW1xi+V{V zU^11$qZz9Lf+yBNncX9D_J4t=sFp7 z?pv-1(y+vvP)S~RRWQ$t*nB%G4Ty+KGm=`6cRd^+B^o=h?K91;zy+Xlz?CZ8I8Y3@?=Ov(&Z&)j zmx6f1_v8BDOI=fjtNbf;haf>v5e&Za^XQ2%NSJBZOdS%5)2xysQ!&9wk;fsW2wX&% z>4T*M`rbKT0;8SlmIr=QFuQcaU+tGyOBz%Z-w)>s$&?eWe#ml$F&EvNHu$2K^pQpy z)1YAy@D}=&!&qt=g{rM0$%J@kppQ)TI-Wr^RC)>2S8R|tQ)D%mV)Y+-zWu+zn)u&D zCDg*r)jK?2;|(}FUFHqLZ-lPsm0j}IU9gHj347Do@lYk^qw)qst^~7j#i<7BfRnrN z6(}2v!fB}6&j*7(>VytstV-s4OW+aKKzYDLWEJ(Hh|Ys`!{BHaTJTmRf!38n#b}^K ze&attZ+a2@sEdKf+f&d@XqJGf0Ivmv*KzYtP@QK#ZNZb3LNqf47yhe*c_)}EZaO(I z6>H|6c9d}q{!U=^DWu-!Upcsf8)BE;=t!(-FqKXN#q&!oIF!8l*GUc;P_`-k73b+> zLyyAZ#9$b5Jix?u`?@UEm}<6jOzu zvO1?-qb^%|0=mph00m3SMc7sJuvg)wrjy$T=0}6+s&OuRXMCQ`wd`XqCKUe651|aG zO3hR{4bnwJxsr-|)8Z}!)oJ1BWTl8AW`xHpa$CmwoB|x`hA`LFB!CXc4WQH;E_nh; zLTa&VyyWLj`nMw6cP`Pn*D*?aa+3si$`CHO+j&;=my+<>jDzu^>#Bem2*F#@-HdJ$ zdEfY9k;{7EunKenZFYY#JPoyz24PACP76);mKsZicX^ythzaF#zKP!Kt8GlB!a^c( zZju`)wqgWACc6i%pE?lkltevu}dfqjnZXweZj=hq+(Vwt+eLWq9CR?-;)R8*V+6xL)6~| ztZ4q-XrT%8^)m%~Uhn8@#Lu-&95F6z;AgHH9eD~niO^loWFt`d z3j8en6pozv3+xiV1-7y41D^auKDiAn3fPnAqDF;_=~tYwNXSZ+^{``+Yz5&+O07Rf zC2}P6i~!Ol!*Ixd<_=iLuL{WpGTZZ0oC)~~m^qds&Y_TSv*3IIr2D+hICcp{B@Kh1 zHnXOJu1+~8_QIVNT|FqySd}X4VNAs`6fjmh6!raZM>o<9a{%o*rwuT($d}4AD*=pO z<;noU%0L;FkvE>-&a9fiEC=h7-Coru`pv`&lQ(=pNOe?^;Z*7?k5UX9t`;yAkar%y z4?Mb7pJq(Og0p5I#gsVUli#Nq@FX2(OKk?J^Jh-cTN52mj{0VZshDt(uf5EaYx~d` zCVHS#9noLKpNl`>gt>!R>Mb7rxe$dE;5;Oy6=6L_x>J0<_b+8 zHPLt&E;G<7tF&0(%&trwgD@E?Bfn_5wdisyT|VN=OxzEo&5E8tBjOB0wP;OL&p~_& z%m9nWPrTY84L)MIk{Bd##}!ZRQ^wzCe_=W7DTElSA_^l#xR6fu(95f_tlE|gFIUfS ztzL=1nGm%>XcjAS3fcixPc2x~vkK{Jd?k|L|9o&ww<=UYaa!uc!a@czp+C)N+?<|- z0OFA-JdOkB94qrOc{#Gv)5yZoes;K91eQsi3JO8`@FamcNcc!XR**)eQK{MRELpVx zs8VFM_`sir=BI`*q^rv`RU)xUv6{GSMfktsrs-IfcJ0=64d$xq(3$2($W=jJSR*vs z|Jnm+irvf>fd!({ibf(#>^8{YR)XQ;s0nT*@WGp&)CNN{&yL z*hG&bYEVpzz;X*uq6f|Y`EiUU(CJVS5Ud;ymfd(!hIGI4mw>9R)|ZpvY-Mw~nd&!f zW;|j5H^dj%pj2LL@ipB_c>)>JBhDJEa^(t>slBBjn1!$$mS9N}&(IfI`#B$pCnxFE zu}Yk!(0^Q$bPDdU->E4vJWe-I^@mKAQib)aph&U&&p~Ar0P!JH9w@^T*L%~0IJFa|r z7~2D|E^_!E&>3p(i2KlwK|A|1nKIl1N&z>|g$VsRLpTaIZQ<{$6Nt;g`Y zVHv);eA5OxBA)8iNb@*^+JO&Y13?#XL8DOzPGJ&brh)me0`jI#zo4z^IVeg2c5>6` ze5MZ;4QdPU>=am(#JoS(;A30iPER*vnnR^hZed7cdyhKdF0|E@SX7(@!X*_g*WxbX zA3|Van3?4`EF zi3(&>T5rVBciW)ZZNUe^(YC5IHdely*DM;q-mhSp)%zhl&V=)m@S6c(a$!(lJ_!r> zf5qLcAKG(ESvOaNJkLIX#g(X>*;C9~q;#i^w_ADToN-YtVZ|c}GU(N=H~?YU?$QtW zQUU;iVCXSyBg1eH%8}Hrh5O#qhI(E`DAb_&r6G_~5NzlC61%7_LtY_C^srOE_kLUC z={GWAkv?bR*uH2U;FfS^Nd@E2Z09dtAx~d{ z#dE#WSkv^Z0k@LX2Fyw;8$DV%2PZ%?muo;b9tKNc$;LABIL^t$P>;rLb$!OOhM?8U zD9m262AWsL+x<9y^=Gip!G@Q^Fu5EkyPCD|a1m%c+Bu&WIs_ecVExPo_GC;xO8y0b zEa8o)&z9HIOJL=V=_K^Cg}}&b62{{C)z!;$sY^9zce)N5nOgtkGU*e4f`dW+9c1i- z`*WZuz>6E$5q6${)`NGg0iU>ASt6y**W_n;4{?*4Z zll2Qw@ifpF)J=Y{OH>IY`I47|{r`+52p>h%GKV)FCg+V> z8?(VRV$bABU%`A;`#T3s>)fA1a9!aE{ii-4a`gKscTc?3id0uTB+rE^N&(ysRb_$2 z;RtFk4Fg_=f(a@%ozzbWwaSA$H+&g~mtTXZHC{O=@VYSkQaC;5S$nHr?F|2BB0*}H zan2J`!$1DG27MkSeOn7`R8`7U0t+CQ|2fV*!kQ*c=?b90x0 zE-cSyuOXeOjun9t*t-a(=e+Y2Ak%@c#u~R60!wo$B60~mr#1P8Q4Ew zx7KSjCsM3-9gT6C!AeB)4nv~1`=pUjCOKO}N@uF9z+P`LGj>n#{qHE{KV zk6Eq>UNLyyPq3!uLZ{LA^2@v+k2gbj_%sUU6@I*$kmm&1^2`RvtvqpoN%6#SQ^SiJ zx72WIsN54CE3p=bHUA^-%e>yBN$_5#@YwNul(2dwhDU$ng})gz+p^2qH>jn)(mK2# zzkY8hbFK;ZK0JZ?Z%6USTj04{f;?v_IeF$eD3b!9Gtc?rxp}ts>tZc__;#7emlq98 zk-*%1XOWtJ{4JOP`wq&5p=YV9$BP|{&!n5yv8cg}?~YE2`MKgCDsq{F zbG#2X2we&~9gCMntWXU&5&q?{7vAKBrm#YFNTM0}8}Vx$BM;(Q1PV_qZ2ffw-VoXP zo1n?RcqQzj@QQ#UY#R~t|E?El7NSkrh;sS}EM5@EVcxcwwZ8i^nAL$MR?>|EV7Rai z(@eI~>o#l@wz+9RrTJe!gJIFJ%QYB&CmK;sWboK6B2T>o@6dXx1zZi!OM=e*oQF{h zE6)XQtDxom9ZqU^5%S|^_*WmsI;QU;KflJqMD!^>^Thqn$LTn?!jY9@ftc(IBD#^? z2z)3f{P2P*E;3aMP0?!vZPq`5nk-50>c6xzZc4eC?EsxKT zz+#3mKIJfEa-YbGzi{j(iokAR;!^l1Z48((tGyU8Fw3SGDTD~L5%IvjAXH_sXAr67 zJ1D4h5L=PKhjJ}6$OIUd!m9ThSf&1#a6GDGRsKw@!D{$F#AQw&^l+&mA2Sp}L$=dS zbS!GMkd@aF4zR$Kd<`nu2;rA?vIGQ&;)Op!|c3cSw(*z1sz2N; zT@B4jpMl@^J#4G_Gi3FU5vamfo^!SGBO+_w<{^r07lWYU@iD3q8+5-N44IveeEu2^ z6)C8%HF)JQOhBCceqo(3h%u|>5%+X?L%of`3_;m7Yk^1`W$de})CtVrO~6 z@>hUBoPK#G00iQ7n>){hbca1AuTIZKUScpuka2PgaLsuauHHP`$>6c=?lADlDrOin zjvM{~od@hzqz8u*w?TkoBvKk6P8Nby&FGR5-NGo1YbAK|Kxyb^Gjxu5#h-ehp4_bG z+Y^hU+LjxRd7DPK>mj+a3=g4w@6}N1cj6s(SAq`Oum&F>Cez5{as3(YbbzqYMpg5M z@^eQ)74LN@nMJEwo@W9XwYnJxq%j6NlV)HuJ>5`}ZX@BSis9IUXBJ3q)hF={yV7t! zG(u>D%D9b67YH)02TC9y|B_Q^GZ?EdKKlYbb>qmgp%5t?62bDW!Yu3$F$)V#m%<7M z`^)VRU}bRvgcZ~6hE~O${915rcGG;~1DG87S7c)k0?iWSkf9ofwz=^XWbs1D9-k|j zldU+=66GarNE+r_(S~`2Ly$~i*?n+xzz4J0VZd}WAWJH3CWQJ*)a9gR@wEC&HljWo z@RvcR7|x8R<5&Bswv%?kDCb;K%M!v4iSjEdiZBp!*UDAN|@ClAqaH*uEHUq zo1LmYSQlh4{!s`G?#8Hd8QO1)WQQ8C3BCb!hd^u`ni2Jrac2U8_|Zsn5MT-XC0AW6 z$+KQ8HOp2?asljP_pW#ODHy4ojKMCMo)XyxLCYbx-Y#@vJI>m=#u2RLp!LN;K1oZq z?ZH;;j|Z)fVBstU5Dyn*F=mRczI@Ekht5A(DVIilwPvJdX^y01YpCz6tzPO6>~305^pg>SZh$oSMiK)z(_BzpdS9F=v% z(N;)-~}P(T7@z!)17Y;rONj4{{-gZ;B{z}VQtpK-te8*JcjegtQ*NeUo=B&3ss z5(;-ZUCz6Ex3_uX|MTtl-t3#%3GdC$Y&c!r&A!y3y1Ki%s=B&cA*Nbct+dHx2ilR4 z;!L1{pp_o&=V$I8)fXhnrx}P^a@1Ll`bwGD3e;0?fbboy;LD-d@|{1(g0O_o*0%NeUQ>jm_5S$Q5^QvI|4vmZVeUERhDu7Q2Du07}A~= zlRbfs#0Y2KyL77%yUDLt{Z22Y=KmVl|tiNIMNB;*`x~vVK zn?MA&>63>#oS;d?aT0$9{Dtr5QxVg0;Wz^jl5Y_}XYC5o5F(S-tWQuU4ZD_>i6!k| zY8(B`1~C5&OE_uCi6xvyag57X%tW+^);qC{41Jwfh{!`aVzD^Xwy^vm%SWxpbgnqS7^WjY2qm zDUiq;C<-s^_l5c=!XVT7JXCdyE^Q{ry49eCQ5$dFChF9Pp|(mGofVKldY9B45)dOQ zHTYVYSIH@|0%Q>3faC(gX~=5q;}b(>Z1jYgni{eVI;zuQY@*W%=P64b-?Jzd*C-t$ zNf4Em(r`_fk$k^7%5ON|ZH9CGJolpDCd?%2&m?j6bJ9?Fb)-CY@;o7&Uk7j7Nv$n9 zYPuprrawAhmU8WhjDY7UTl^U0REJBYrpyF~&qk~Yo#yHRm?3s|b*(WyJ!@>)ye|v! z3d^DN(0_c$jE)X+=+8-eV2=g>=jT+q=thGfr06f|Ww>G!Dk;=-Z8^1Pzw@#X&b?7M zr6r214wW9(YpzahY7$Wpqj$yQJ*K6l({!}=aNh!6E>6>&&k5Y*zxhe>)no*NrqLEg zjRfnqgDot|=oR;Zy2&5I*ikj|;{5G>AyT)wKZG#DQm@ztwl%qD=S@O1#tJ@R- zIQ1=CRrD&%gK!%-b_l#m(k)3O5s#am-ldj-soRc=4B3=fF}~NlB==3TB6h%}WT2;k ziA=qHTj^{x#03H>-_fH#u@bB9#W-4E7RU1&&BNKP=Hc8XGss%NNz0NXA4Q+7a86xIkn%MmD^#?;Y`J@$ad4s zC$*L|*hIARt8iDxRvH|GwxnaF>F8K)l1ZdpkK@^|jr@apc>WINLzvNBq)>%nRVxwH zPqRZn(E$m()AE|@d`^Qm^G@s0DgsrmqrFQ(Tbyk82+KgORc%gxm2P|QYTog^7SJlE z9$A8s98q2^6v&TlH|-yC9>zaNr|e*AKLBJhljiW@N9^E1=KRYe2^sSr={K6k^DALQ z%;J!a8l;<5fK-cvB$F*>NmqErkjbRYsgn#QwSW%NzjS3ysmU@bI$+<)C(N7U|70>S zFO{ml{~XiWiXYoNK`J$Fjvf1nWyq2$Z_d2bd@FmFSpw}v_eOyv=Kfg>9KvIC#(c8xw1!@;j|+|p~DT1ueC zNqX`_ISI>thXv>W&U3#E(fg6sK1oZsl`k_JfCTt?Mqj^x&vkEMUk^b&V^oSl(-=ojiFOP8;%$xI3f!+Zvh9)y?sn0evj z%$v~Z`;s^`Hex=W`>^d~5+K&Bxgt28zI5upb?P8Or<(+eKbU&I>9p9?pJKI`?Mu2~ z%uE#20YRi()y0y|ZqwS$ zfr_+~$Gg6515SL1ifeNQ=X?f+brlSYSBnwqV^|Rw$l4DYv+_ndzn>x0Ab~{Shku_v zoW2O%nGF!nD;V5s#0~nHG&6@=O=cIrv=~-0Xw0H;qoV_;etSSMX`aenV7`>O&PrBA zfg1F%q^rlO0zs0?a;oz1iGd(N3t>9DWnd3a2X(-a;S*?#6XwF`0rS$t{~+N^&JojM z(3u=HgM&|6JKcMy-itIPi&gV_S{_ww>B_aJYT;zIQ^VNLunmb`)u5+OYG$xO#!F{p zGG$&j^+mHGx{s2lO>ggJ(;iesx8{%HvRH<7H4JM}f#^!dX30~1469IBh@X4Hc{=Al z3?3NFOX0wOv%T@lWb=f%!RSnh`eHp|r51{NH2_FPGlA(RJ78Fo#SAO8-6URpk4b+E zra3i2mJ-fq!ua?JbK=A<+fm*}M`@cGWL9r9%C@w$nU>ZzJ4>hMAfRwV14n9hjMB8E z;snh@o$kco(Xw`wHe{Ut)ADzwqjR08>WmUrG!LWig zxq8uUOg!P(wi#(q8j%Cx*u594aD{tEQSP zz+o84(DB2enN?5UvcP#{@KPTnk4i@04Cd`SGOsYFh7X386oOI62_-4y$dO0soH_G> zso$IvX4TfdBrvlQmIKF7$!kbdg)$Sa9%iNVCnv}4Al1$`GM`}Zd?0g!$>v(kp+gT8 zlAm$It`PBS7-)a%9us@+`w$kGC0|US!-r)mlE=J{S*ZxqtZUWG5oq zr%(&pMMpJ|+AuXWVdAWH`(pa#2KQ4eR*k6xFJ}!B?H!#)OiFYLI%p`q+!7pS6@0Ak z>Cr9)!C~|iMtjuo(!L<8>o>9&n)c{1bGCWRjExVQ_O`B>j5D7$nL9piGT;Ag1O?gv zWh{c=RZ$eJc@H2I!DP|8ku1D6;$4e#Z6L!^!jl|-eihP95BcsZ&Z*Xuh&Zv+!FM)W z6RYentb8_yBybqDFl{u6tuHN!46&J38f@h__LRy`omy-gj#k3t45KhnDJAc9T4sCSvS{BW&G;3C0 zZYwK`bL8>kJ78eT4CqGNH?vL2}H)R#X;O-N{+^%7ieado6i&J5!ZALL$4L=2=}$ibZDUsVZ|e!c!% zs`F{ds!|Tq?N|BmlF@q*JJ{#Wo~IHBgz#gCrbDZ}q}B zK2|C0ML{7!HNSVs{j9;n8L^7|dGz>$X7kG`1c4E4d~wd~{_m%7<+uQMi<#3JGWvHf z-DciC_&??!Q?H^HDob)fahGFmEqYc;YjR=?DR-pOXVQ(rtX5FX<=SJ#XoU~MK^%Vz zp^*zPJui*kXF5A7FP}Pf>IgQj1Lmw(5;3HkhA*pG-qK)Zxg~Nb0<&aTAT+ffg3#!> zU2s7ANvy>^fuG%y1c%azyRKkL_!4vAz;?5C?M(e&Od#5}!n_Rc;~(HjU4K_`M)V-| zi!{t?q=Z=op=Ho1_?c-%n0Y$H>u{A8<2bVCtr#`g3LR4_*S@{?m~F2j>1u@I+a@+_ z*zwgz&DwR(wjI6{M=b|hcj8{Nf|GfUv%a!ER^X&6Mj~IdK!CJkjovVF`D4|Z=x`(} ztstmY;7U2?S$`@uQ=ZqsK++XP3)6*Ry{}i!#^vJSHnVxZS+R+4^`KQQ$*}M2k$c|k zWLYG`rf9>LiWtEXQRV2kEUAFGapE)%pj`78NWUF$1 zhY22zS)IT)<&*5mpDEu%Z1x9tKVr7Mn%f|qgfM5p*!J&2Vk_v3)1^@PR^e;(&cr{# zuxitBI_Au~pmwWFCS&9};z^ygn3ZzUOk;h_iUlH5Q)VorN-p*IRf*e8o|)`&RUuj) zK760)Y0QNCOk!4hBR0^w6{6sd&?HxiU_DZeI}2m-nAGG`ZhKz@jH;Bt_=>h)`5>xa z4{OsNFY8aFbhCo#fh~Ep%#uTJrFTN_doyE!8!QtBUGCzcec_G=NmIiIvY$3+v-%Tl8_W zY#Dw^Wk8bp=AYw3n0hFaj`L~_m(>-&7JU$tvVKzmSxQml;14B3mzpM3$2J`%gFPkZ zSxjBV6mC!sj>TMujIqRY)d{mEf3X=H+RGZPbwwE1hQ9nF^TtD8G#^R5AG7bw+7~JM zmk0MCFUr7uP(Eq4da1=soDtk<>>VB5rloDB-KSjolB~u?Pt{DnTwL#+wy?Q0al~A@ zTqF8SIw+h3q-_5aCLRneFKNpOTbJg{Kyo*;f<1QTar&ckx1^)roICoM*_=IQ_T-i! zyp?Z+9a^8qfFYWO1=0UR9V-aBP&0$%r%9CIp<@oAT;yuidATxZ(f8})J6R6f&fuZ? zC>CY4>w{T$)R>NQ=)25jX4r^8{WvT{)*Ext*WE3ih&)A_cf-8Ok$x^u2tqYte-T%; zlMj?O^^%&GhPXPQ(IObW0feQ`oQG!$D!zUv9VB5#J9W5Hh~d|?f-OHbNAbHU`KU(P z8(`;yl4wPbYOq6tm6zEl4@qx*OL4_Of^+Uy24+@D|Kw0OpP?cIc~H6gIraJN>vmOP zRuSxh_nBoSiyTVRs`yOvT6-hA5$*kPg&=ve9QTwr#lJ*joyjmIooZVi%`66C9_2ET zyF%EwVvs>3H|?fjCAV^otQ;~`-ZAu;tKO`Vr*H{Z;2{ujAZ7R?LvtzDi2Y;H{vk>OE>2$3T5Vr1*b%+LdOf9?NG4G+ z0~(1dAR%W{xkc0qa&^3@Wx%|14FgyOqm-D%o}KrYv)@Dz#<$skgtr|mj@Y>6N~A3+ zrT|7jxxWNNv!e&@H6Kd73p<)*4QCh;BM-}L{7GBS^`i$W8rLPov{2VcmQ}?t6U0&Z zgXSlkgX~e}$EVW0_Vs#K^4n%j$FMcFmxfDCjhl{Zm_KYQQJP8vN-&WYRSERz8rn^~ z%NIf~si0diceJg=KGp8~*63U6HO=DwOqpeTWHtX=BsaReVbWvyPIhpaIq?W;7Iuz! ze5S1<%9QoN1NWHsvjCUn)YiBj)QIaxMTT5CfojC91+(vjY*E7YGhuCG9$sQr?_Bz* zxuA0xDYSg!R%hJv)V=1MHxPIxfANwEb=8*MyS~AByBo?49!g3K;BoWW@t2vs?8@3{ zcRvue^@F6)oV$^pKNT^hP+R`^BfIy`5*rFNOORFQxv`z*zn&hQ002M$Nkl6f7 z6<)+sah*sN9LPL?q`JBEQ*q832xNaQ2NA6FX_%SLU9^LNGa$?edN=Q5tY*oVXDBnm z2YoTQn>1_pjb}E)5;38F>fwvb-klGd_3Ihk%lfQD%)X=dn!jxSq)Ak;<5a2_jnF4p z_7W7dSlQb@JZPMva4?dg<41T^+^CDfRCyTSY`p(I>sDMBDo-A$DerlDyThzzYH3wc zuki_#brj>C?^a`4NX2YB^WgD>`9kJ}sOHN9OFaJEt@Lx89a?SY^nZbJ)LH+sHN32k zhBh@Ao>YFP^B1Qd=1Dfh=fw7y=O@0)wmuhH+)CpQ9XnvwytW&uq{7HSo3MV8UPSSQ zp4YLt*^4<$_cfpxlIc@6Yq-=%HE=V_g}=j$#ix+Uf~tt%BULYT07TT9g46jlWLPc% zL<8OGv}YYZWcRnBcj=^J&3NjYM=a`Vw12 zB{yYEO2vuR^F%pVlwfvY^If~{Qu;;t&ZIIQ-hZ$8-PF6yvffoJfvKSifygn3`?~{E7#5tMS6EoA`q8Tnsbv~ z=ExmOXsAh+?og=}AW8@N`@zf1-uPPjYiSE9jwJ7BF_B6jwL^_t5|t#Yu#m-XX!s=h z{wzS=>sPi9m|tA^IJTqeBVgy-cVP(}RGcG^1O zTIncTZ(lWTE%0u~mm{WU=|+}ORo?9&!>$o~4n1gopRIs4t*9(*1zq~0Er2?^dp#^d zUmL}&PBFm#Maw76vXvL%&(~5G52^fzhwm{PUxGngbW9=1Yi6)|H9FO@>Ld7+`~owI zsFCtFl)5-$Aa7>^*E|n)B=;|Wks`d<_z85lH_eD;GDv$r zpqvGv219Nfo$t9Ww^Juk?s`1+IJ?olg!?_y=@9Sf|D+4R_sXqGm<~waU^|vwM;|pG z660Dv&HP0lIm-qyl*^#rh2w7+Q2MH8xsV9ztq(6x4T5h=-fXtE97fs~GjVC<1#qv* zXU`67h;QVDJutO=xRPiU9@U@DC9B7bxxPj^_gzeWVxXgh*T6*ONWDulKv|}z#!(}U z3Rnbry@(i=ONcg%gGD_iNHJ@LA#J&HkZyY47IW~Cy||#InK`_?YC~7G%Qx#~1>WSniIR{Wry&|h*Lbaeuxzqt8_Cc$W0S@m;bNu|}g%gjW^ zYzL!48{&a)|?thwkr)gcXoCB+)cB#$Ga4d0Q&}eH@zN$wTuhM{3 zp2ei+*OOAkIw_Y+t#?$O=x`kq1?)#Q z18(Jec@2F=&}O|@1Pn_=qLCu?G>vm-G(ylT?8v~(;m~q3_~TZ4oY$&V7bB8aHb8q7 zqiOc^)FJc1gD)^cESr`Dw{+9O-$>fy%iWy`3M!>ii1gt@`K(9O5ZzgEB>XU?D{ui5pC6~XHh4~ib3^3 zO#RfzpxJX`!hAISTK3h-GRNz1(^i;Ndk0K*7NGP8G1}Kj7@M;O5awachm(J9`a0L+ zz+C%OBb~o^a&WiV_8RK3q0Gu7hU{D$uyZ})y>PA%BK;2EN%KMFoJa66ESJ0bk+Jow zKS3I~KVpOKixyBeD9zGjG9xgn!=-O+JJdqy{rF+gHqE+qm8RNfM%6iWk=!ZsHB`*6 zrOq?UB5Bjrzp8G@%9F@6gE?|)sOp|fVo*ofOzQp1cbn&R?=xwRkB@4vmH?o9jvae~ zomxjs{{=Y5N|;?p;5Ne@UjZq`kkzZjxHOp9hFTF*m1}(blsP`|g!!lJ+sy84H#^aC z<`e3l)_c|?UDdKrRzgOzekZ{9car~VRwqxGu_ z&XiO|;7s2mYt5-0?MzYcunl|d+RH13uKXcll97~oc(~Pked-FcFS)_AM|1+6GI)r#w=1M@Eh@VX@oXlY5EzN`?&^ zp+PJAMkR3))tXQlv@0>PAQG9ft~0j9=b<`|`6#Dl2+1%)vQ9LX-gfLJ2JM_>t%vtU5aA{qBY+_K8?4B?+ zvfGSI44Z-M+2+ao8uM6wojH>4W!r2qAD!^5eNY`gdyy)^5TPvw+o-GaBD>79qeo0H zdsX$taSHD_&$Q_j9_yMRWP^kDbXWao1;I8Z^Jpfj^k+PTWLVH^;wkX46Bdx>GgIsk`Vc;4l9F@Nn;;hQ4V&ia7d|Az zqVbI*VKtI~a!t@?-^~U&*iAm&xLhDbX8$H;6_=S~Ll4_6jiqWx)Q`twNMGBm=%2H# zSq*vaKzQg}zhQQr93MMI2cW``e4;pAt-B#_64+G(gZRu%@}+T&+&llun0>#2A?4R$ zQh!Mwh1+TPKe64I2d;oy5Hz}mKjfKbV@ItCyLuPTz+GG5S=6;$A?$Y&&mXZ;gxUDl z#%y^TuUq-%>-b*!`~MjqhY#?L@U?)f#}V=l+zcuRI7&{{(8nHVOP2K7L8ev`&HgU+ zTTZxxgF9_qMPi+}ERC2=KMRSRiOXiAu=vuCb%#7ibym_z8TwQ_roK$rx?jXXltwEB>KD)y}OG_tV;?{m76xEPt z0!oPbFzM)OT_-qMW^!_jjSi*H}j+kW^(P>+Gs3}Fw%(d`F zs$6!TG3S31Y3iq)-tWg#+m8$(aUT6VlF(h`#k2CWI2Tf}JW2wWoicB~mXpKZz;+NX zKzCZ@1hV!LaXc69gcfO>IN5cyZV(Re$dNsu2a5STOP^R=p3fYz^^r)nu{YW>PM_(w z-tr#(MQ2$TRR*W1q?JgG!}GiYHA2P<_6D;J?7-*!&@ZpDUW zIg&3&AuaHFvGe5o6{C}r)!q-n=)Pgh#?Rt?{Z{AO_rcw&iqbh70i-AU8WN@lxrg3o zhJN^EGrIq7XiVSqrgdikiU~mhJclNoBgS>!PWmw{PW- zhS?^9RFts>tFD#Mby|1H*Nw(p$cWkd92zgUJo-)~kpF~yK8CMlFon1Q>1yLt2^%5b z<#hOSet#OD+sMQxv`l}P2R`d-PYj@MtRwv+9)3!z;aCCOsCXL zQ=V{aK3!-_kqv~~%a$gDvAul6nA>|Ak8^>^Dr<1r3v(2sELAaus#h<%`tNxxJnLSd z@B&@EpZDy=)pIF`4_}Amh?-*)y&EYrHOf6dFU`_8r~9+?_m=gPvN8c`Gpsa3%?wmc zBoG~iL%U{#DkBppomzo%bcn*yV6_6z@_VHQslIvjK4+9r3%`foq=r^?)@B1LRFa>6 zf$M!ghf(J)NRJrNXozfLiPQ#TuJ|75Q*bfpEKFzr7qE5x0c{87r7DL6_R41yKM!f} z!$=n}yu6J01mLVa^F8kELiGmIN%>!|KQ8S39Lx%)F8kShKm;3K-!G$LLm$Buc>D-= znTUh}7kub?uTH-!jY1N5h=Y@>Ff0<_OdYX8y=^RLfpzE48*>Q^%7cgBZp_JVgU$i(&D>wJWO<|hxU{$fx^Uj3HEMa- zG@#3QNiE209KX2)?Ik2{iI*=71Z>nfgCcy>B`ffq+)3HhG3FbPq?W$aMS{YQKGAsf zCP)$E(ByVT@^ij`ns_4}_k5%bjB9rx#bi*q^2dUt5-tpfcG&j!#0!m~*j631J2*(X zR6_tK4o5Y4?4edgbYC@tSt+~c`~)c%rsPZUEC1pM@-XA z?dASdxzKa-&W`A6EndS0uV&e{=B7jya-m$2bqtW(SXy$66GnsBXwb< zIeUSkSi;hNI$3r620@a>GZ&DBK?JGSrsKkBzUjBAAXFjohVlI{E2*gS$gEUmX&ZN7 z)NQ+j9Tad)5ZplH5zTI+r^GRq*W+n!6IvjDWEV2nfUezJjL;n_JRN31v=2}bL-DK@ zQjc{smiloK!zwO9?I$%h5@=oT1f4qUNN$Azs&)!Z36Y@9#a**N1DKU?bQqQvDrryU zh5`*G#%R)P@N6Q@&ZjhewGn?Oi1f3i3O1t6zcC&19vYDP%R&Gdn8&{bJ8=g*tDr?D?8fKl~aW6ZO@T#WU7FQ>c@x$mZmzs>6t4ac?{AD2pQ zfWgS%F7aGI7<}`J8<_nN?2pvoMwxVie;)(kw!RTpKuxoTj6Cbdz^>_bcf%U!4AS3m+5Hj|=Wjs!0yjdzm`$tQ19UM~6Aixe*+~$qhs2DvE~9 z2Aca9lm4#;MAFAUNm|+ZtjH%KlNsqFE+XS2r*T; za|sYx9b8VP>p#W}9)^kiE&i6DaU_CBFW-ohA0j0r zw$Px8AimPcQ!nfzW;do=!yvHXP3{bHnMC*fKVx>j9$Znyo8ezpCd-DuiXcU8`*W5k z{j@`0Nm}2BX|=-ute1bt93@#zAXzn{ZCA~j45HMTywT@~QF-VzhheF$7foM!*)6my zgqLGun$bHnP+?2ozye@8t_9So+Zb*5zqkRKh(TTNF|3?<@{24n!dBd|>Ahyy0m$54h@8B*gzOk zV$U+>dG|RH*(vYouOw$4d57ID!m^rN=1IG`oLz&LI2R}TO{Q(>ZUZTbk3`kS4SWU(``0VOzsynfS8$VcR!yL%b(`&Dp7-Fkf9Ofrb1Xd)Cs|v25fY zK$3zE-ojeQS8y)_H|7zvz5~bYmpQ7@7&i4Dv+Z>Q4$-tt(D#|ma0Ro4Ov!M#t!L8a z--f5E`3o5l{)%{iyVPQJ}^>B-S8R(IyrMFq}3MXz*eHMUJQt+}cFQ zmxgFsmNyfD5NU&Ja<%K^StDAG@9mqW10DP{l{)>nsPD9#rT=A45mZk@k8saBcTsLT z!qQpmd?kc&Od^;bqyJ1I^k`KQC#PFx#%%*2i|KK2IvovwqW@{fu=W{q-xbrR3axx0 zw!)vKpqu$YN$V7fjccwTv6eQC*<(RLem=Sx#HT@9$YDQXpCJ1}tJahA%)EkZB&hS- z&i-rK5GtVxa*&Y_RmkKYcgnQb|LTbPu4lP}3S2?w1+pi3MUw^0qZM=1dQ34M6m0Zf zJyYc}XN|+y(p*y{m*Fag<^_qvhm!h{eLEf%Z0+TIB#?wElc%%Fg+Vb0WAYjy^x-su z1nvL8=b1Tt6UBr8t3S)8g}+T%>#?!cNtfhWOacpuQPBXbz@wIe2Uh-j6`Nw6C9XG` z8X~&9U5-$18ncY2*~9da>WQe2+@#JUy9_wng{$ga8uk|>fVSqz%{yr z8_M@XepxuuyxNR5;d1f+*skYtQ1Z=c$nLoFQ>f#A;cGXju8S6+d6M#7r9qsInnL&f zAtQmBSF@qWE4|s7)k?pZN%=!BK!zoKnNBk2K%Uls39%}I@Z$URUKp-g+T!a&6{F7A zhsaV;##owpngIc+r0p6OLSMw_{cfiHmVrBxtp%0IrMsQ6D!^k5gtCuRsT3aB7jsALcx2`XW_6}q4{0v zLE}ZUAkJWw=xJ8!A=0Is)`l#JKy_Ij77)V%!MWIiraT>xE@;Shk#c2X+0k8+l{~Bk z^KJkZgMkS}U_9AF{CMnnt+P9W)C%vgQK_$f$;9H0L})$4d6-1y82C0elTSc{pYWb{ zv8|NvDTQ3P5CO*wI3}6FIv@OdQDtWU^=WlfznB6&#&#o_eUnLrJJ>(@-<>|f3?(nP zALA-w&I4Ep*VhJ{!my~|n54PY6wsH~oEH~7QnWp-1r7)cgJFqgT4C>v@Bxu;STOkp zjXha8dpa>Eyi_&naIFBhvb}I^j+}>dgM-7C2M#6{F@Y)nRV(b-?dH5ByP0|3AaXXH%mI*yD;_`vkBDo{?|F?GD*_%P`8qr7uO5ih$~E0M_XHW8Kn>Rx`hFf zCe|3fLfP!B{pLm`SYM&#)vxm*e54MVP~7fSr4cwwV@@3i8n1ETR4-bwK5qck0x>Hg z2(o?ys0c3n>ma;RD+^jDmwtlT(=EoF_f?k6NNRvlN=@@5qN021?#S0L{v$1%{1=!K z!DDL(kNrbVoc@+h`!^fj&L2RX*Gf>NeL5Jgo z5eYrZN~X|>6NZdTc0C_unbixJbhwxYm`4YL(?;rJ6H==MUuA1cqy2wNx*xLHnfO}r zF3QA>6XpUEb_;Pg&J>b|HGq##w=tnmFW9wyN?Tm6D2Km82QvJ{WVmY4>FSCLF{Knh57p_I^ctx+4ok| zH&nJLqkNP~wzy0IvP<&-;!st6t~^^je?W;74IdPrd_b=}rVIR6$$O zuJO29(t*FNgJJzAu2qsEfPuS=dF*Z6?68lE?+swkV01XFq3)|eslmP4CW~IPsg~Y+xhUw+8Zj!j zNmoNL?6;W=D+N)rCrBpB|!j!`Y-gO*o=7DsF{DEsH3Zg>n)X9_<0M)F5w zUrZ1|ifLQRriAxRV{P3qrXT0_jz$pKME}`^r1VWDKYn_;&b_+7dn3|UVkWKg#JwCR z-S%@xgZrI-!RrA=nf%EX{f~@^s`<6z7ZM z`|+@+>L3x7BdTZ}1jJLcW2ZNS+;tI+@Nu4@pKK!;RISTV;pBkn2kjJb|4rD;wqu*< z1ce|{nLmLB-siD%UH(1CX5!fuZl$Bd8<r;stBg7osN+g# zeLEr5W}va)=(mt;U+g{-ru(Oix%j`GZ(&q*KX1_eoZ5q-yL`p03CTgd*?*@QmbPE9 z)A~)w((akFm4yA&Z9+a(lZ3d{4yth5eAOfhAKOuB5qtexuxLi=f-m8v{JKIYdn4)P zf#<--WDhw~{B6FUB81cnf5gIq?G)7hhNCSI&op@|VFN%V2O715PWFt~Uws z#Gf*o_ZjflK(v5CO!m?rVMn}z8`_JcE;;Pp%;5D?zP@q08e@!~A_M?MO)>z*Isyc4 zKoAKw6Hgb$>jtD4I0+cm+4M_RBaie!`cL zd#lR4YYU@4Bd1{?Sm+>^iSxfhd2Vtr730!w2|sM*`7G{t%x?+k*QW1{?vAc%RBnas z{u~R0VOD1M{Nfij_XusH{}s?2gL)}E@gAfv$`y7YeR2bXjM`hX0GbDI>}*!4fKL(x81r<1!q$zvlUcPT;6VH3N z3eQCfk&ToOv%#hR8CGg8ez zP#SR$(Gh}q49u1h>2c_O)W|28IC!fg6eyVbj7MAVYQytK-}RH!B!Qs+xA|eqS#cE) z%12O=*C>qZ{_{z56`vMP=W%dtz+bhi?&m}6&pU~!T}>R|_^%BPErVtuvRPlNpb;vC zQ~po!QI)4e7Y;O*VJ(E&KTW9yb$~7HOqJ@zZUc;g_ZqMAs8G%Dl^z^WDgL5=4(!d;_(SY7!m#VD*7k8fD)z7fz zRsDm~*8hXip&s$IRz+#_znVtTvYHzO=Ebcs8W6W<;0y)kk^*Xr*B}AKU|?2lM&!Q@ z8zarJDu}`P?OAeLn%ui6nUuerAsH5(H;(Nhp(XJ11dZ^ft;Vo$F_~WRPp#`XsXZTr zFYcPB0-wqD8BYOCHuS%t*bTz^YDt2sqDIUAQCq!w_QBJDkkgK%{=$T{2l?X$xo@O& zW{vDp2{okD0hR&x)e*5lQ8XJjxtqkqMe8hsAC+p%*dOl9eM88-u*vAQDK#tE!mJ4g z`<4~Y8qn6}q3~st911D@wpq|fW+fcuPA~=W>*=Vfv&1|yEA0uC^IRbs(17ZpL9^<4 zF{JDE=f!|(Z5+!KCe}m$z>8j6)Y#crFO>$gH7}_-zRqM=(sdg(+Lsol(acKei}v+) zRJ$W*nTjsOyP?nI$N79FbqKucMZ63`4NR&`>Zk9kxvu_(P|~Yx8o$^6WnkCrpk}k} z?y|mu6)~%ZATV!<%w$-#oGnA63gOf-TelIj8Jyj#v9M`_Q(-%h(1ofa0{PkXntO;V znkb2|I4e#WuxXWff96#%3W$CUs*1`P2C1YHB=lGm4Yf}0G;#ycRcfAGM=X@YZGM5= zeCPJ!mDZQ5rX?B(aSZf{Wh7Wi&<5sVYqgH_gB+UL2sG_l#@#*|)PZXyJc=50r9fxi z&npE~-8%i+`tC8q=lNE7+{o6U{_wL*2u37lwkX}RsHqe^xJ4@W&i!=ST_R;B>NR~T z-=<lK3Bh{5|DNHhW^Je9ouk+sQpN4Vh|GI=B711+C4n+<>JK3$N$8&wdF=mQYM|QkXPYmHn~=gD?H@%7ss*H?j)%f{3dq#aHt%; z@n2v_5mX*An>=ecHJR+9WICWv`*q;Djvr=$#7eF!_|ALnV=u;@<(C`AtN#JY@LkKd zYDhy2wXoU`^v@jl{QyWu55zVVW`L%>qA}p9svLES) ztwj%RuaG@Zdl*?qz%so=8wT2q3CnzI7!AZ+&Z6-^*Cf|*c2M`r(AKo zl=ZFS9MJI?wD;l6wI&6$7nhybQ(LG;Zp~XJm#tVTwHay?l^cH`xk!yOS~V!l=$ogi6VtXkP^l9Bq4dTI~HaA zku6X^CP_RS7;i0~j^U^J2D9`8gN>s;FE91f}@c{J=@Gvrm59m_vVn>bRMJ zd-yd;%CTi2`AX1A{)Htnl>$AJ-&0@mHn1CQs?n%T)go$ndM!eRMPVoRP+l5rorNw> ziaC0#da|>TnpF!}Y1#u!b=)Y3u;+IMiS7JvarncFKm$Aq42A~ftxg!c6?Kf?iH|y{ zTE+n5%41)$hsFlPzbf7;m>48BF+0-;v&uZ}qwhLxeSq)|T>9k?B{ zjgA!tq~qHqM6<7K6F~|}9Wmyde=}w+%%A{DTlfJ3cNw$mxtvbTy%yQ({KdRf?*~wh7%tu(eJ&|1V5x8{g>oIv;PLK?GL`&01(qBJJ)xpnRnHBw zpf73!*voP-s5p%E!XF`ZT{+FLJlsz`LisL&32)`wQ{LOzT`MsxFV%WhOn7k@{8e-p z0ib(|wwcpUgM|0K`2|=c3=4wPI>EX5v=7I`dLK4TLP)LJHmjGDuZgqIEuucjXI9$_gEKeVV=be~nxQ7iB)@R*aR&`!)q+mOp>uEEnv=gOS-^H9D;z_1|9J zw(Tr{wz9w(7DSvxl$UK{0T3%#n*--)C$mO$jJk1Fxt|P{U1=zLu<0RVuA`-SHXV(M zS{idFt^$!wyx(0K>oj{t1y5jjzw#gwB$Eaf+@AN7_c5{XRo?JzHlXB!LgAz-n3#ny zk%MMZEgbEs0B+uP*%VTf&M4G!D=j+{*r56|*ErhTYyMfl3=5)JHE`h)uPk*NaSD&! zPiuGCbUP ziTLO-_(N3UYoNz0+EzXNS#h*ZRX%2Rx05)}NaA*vr*a9Fd*{|^DOtSNl9H^X1Ou{H zF(gzv?S7zvOW=hqJe14Feq3;W7B0iG=MOG8dR0fLuyJAaU^Tz$)FMp}2Oi~Ufsu`@ z@q3*y=Y5!XKG=RKjGvDCOg&dIvX!zUrC%#X4Rg>eq9ji(X8(RSyaJ|Sead_^3vxTF zKJIZ067kaUUKR58UU*l1@B(|+Lg497dR2&0xbemH#aV>6o{YM1P?xAGFZRU-EgKm* z@mrcT&=|FSO`&ntN{~hw*~acaZ<}UTR)uDy{T|;Y_K?)BT8W&#-@Vzv+mJ3#-tClA zOe@B$=uJo=QibI2HK!nk<1!ub6hB`n;NgXlImG*U?+SsYZ@hGh((=N)c}<62w08@L zVL@qm#ZK<2P=O1S#%c?48niP|4Zu;xcE68Qtt2BGm>GLJ&Y&M~LVD7}tw;`U$i3=}bjFcHs$oec#R0K=l@<-#v}Rkp#N$+MwM zkhp5=ZKxb}DNB$DwZcG)Ff)TX!o24uW6pZNldzbT7KK|?N3Srdo&g^Pn2b371!L~J z+TVi zU~dbG1huS=Ik^`BK>k(OtSSN$gyms#ZDXj2(DVnprAUKYtbAX}8#Jc#_3pCxP+hst zd9pSu>|S^DH;`F1xNIKY3*l-VWl;fd_m`1|CZ?5v=?ATYg;17f7!Gs9u-t|rk6F$B zNusi|U{$CsMz!wujCme5m~%hx*d40`pVLp)d%e*)LZhI|aNzgmdZ~Mso72CDdm9FS z?a0g_%fRDj*ELz_dg=%6ub?KjYGMVPgKw#7b4d#8wj-mfLx>=epEB z&HUN@aJ_LmF5A^olOw!%@sj~zSAST=!@&a zHBA0s4h#fd)IfpH1PV_92&`2j>UT_RHIQK`VUp9vYTEi#Ms!J2_FxCz*@~^gr2HjG z?Pepl%bfJ|-Ws(@yZve&Kz`0aW#iX&4h=eB8N%Y2rt^3Vs8Za>CUzV@KDEbHZR$yvI666{_bufkZ~kMxuPFbm zX>V0lypvnEkweg7H!L#HQagIql8|z!(%K$IAjWz0TTE`8Xx; z86=j#s8uhgD~Nul?q?^Oe{f2u!D;m?DYpjA?$fo@3%Fd+U(n8gp_8M0{i24ayBpDq zU{`I+&7Z0B!rCK+X{6MF%5(PL;al_zG!`#^VNV{CLhhjBz(1;ki<5_zO~-qv9<%r> z>BzsTWh1ZaiC>&r#V0MH(1|$JK)muA${$CC5ooeP&X95Eu_1q@{@tjI{B(CyzLj^4 z%&Q>?H#m_z%Xu!lheIXKf&?&Y!EKZ~ru!O$RTG!tCg?R% zDn<(;wJBGQ857+LywCQAgbL3y6Llig`8P+WTm`e5&cIEG3?-^tQ0Ariy6@D`v=f2W z&1s#Mo$klLstv|M0qNNTBG#A5D^xaHn5GQ zJxCQE-6g?AWOb*B=>FOPcejIb2%aYIBGkh@=VH4P=XTrI)q9Qk(S`z9PLq-@++x^` zF129YrGE*gQ9F%0xzDvZVRstG)j$UmWa!{pFeyHuUjfP|RQNpG8?}|<42x6j`%ONh z*Or3z)ow#=lj;^{#!PCQo$e=K9I-yRj+>%_l&0W&CR~&>TIPUR2`AZ&DpZg~Dsu>_ z^ABA7p#L6Jm&it*-NA&rS%dbIy72EwhV(_Z-G5`uU5s?X&kh1GOKq)eHqSb%1)YF~ z@H-{KtF7?U-d3xiedIJR_k351*owAeDp3JT(Tdu0!Tj6ds-7$;1^wm_BFmj+CKa;; zpLH(+Ek;5MUZdIqb6LY1Jy*b4Osvv3o0j*7H0<^;O0Pm%UeH-z$t$J3nS^%*(5&EP z4}uCzMEGbM+Na;a?Vbt|>Ub@W`DMQj8c12fMGWfcUxShIeiw{OAhmkc&tubiA@^&Y zcc<&$EbZywze8iU{?GnnXz}#vTH1Lom)rI=3#i$^8W72Uuj#$A1`7Vq8;p4tvok$1 zcG79e0*&ZazUi~X$ApZcZo?`Y-uH3}NkyBJRSXaDbVPlN4Eq7o^d%qemb@%Jqm;Eu zUaG^Zecv?&pK0MG(r=?cyzmBzwl2;0e%6@1NEI?h=VA2Lu5%uEx{d1xT=Sj$MjC~| z!b?ZwxB&?^0wsb z^^(F?RQAX$DnH&21DuP-NM2J19Mw)`Dc2TN4!Li1{tSLlpPMd~ov~5`;x!7y6mOJq zT{b_`-S;J=t3Tm>M{y2T3iI;SHD$~#82u#yp0o; zqM9`EDxL_6<2|Z9N0I6zc}TjGnpEkKR=Zgrl6DUJqqZa}TIN;-$C|j!L4Z2 ziwcqSuq!PxFs^n4Mbf6JU?ROfO$8-t}wIp*f zi0jjCW2L;ZsqA_mwAxn^a2Ag`b8zV|v5(fLrW3gZgvv3Y$WnOPfHBWi?*dc@ntAcDw*TK=^R-7T{U_|V&tbpMy~A1+^9Ur;B2k8yb4Gly--(kLqnT$T zsg1YOVIuVE2Vcd^=6}#>F5w*&RncFDnwvywP)GAaVH(x{vKjZ}U&^eMjcrgR`N;_0 z2lKw*uckjVL%M<=n&(Tpq8>z+U4IT^{3JI=_>}^PM%EeAeWhb()MpJedrDegn26&Dsh5 zyl>erEM|Iai{Zh=O%20qbt)!T{p`%aExS|g8f{vVPq(N9aT zjNQjz9;59Z{|rT?-IYRO4Rny?1R?F<=!;hxb}paAxa=XsYH;&_ld_cO`YapC955{G zLYW~NIqHm-JM=O_7eJ*b*JBF-0^XFQGr_2T>~2mnej~}BOB#|Q0Qs1C^;PfTrcO#B z{*vL;K?FY-P`QROqxcNnZI@we|Iu_FmDIH95aI7iJK+-_fM$OOiZb%og4IYJpTNcB zbxuG#A@DG>Xh^xIBh%7gyYf9S>ECkO5B8#48R%v(cPcZ1c|q#lf&{fiq-U*EDc2U8 zg|lT=+em#zV-KBUbDNj^b(X!>S^0lHsz%S{B?!*G08WA>>9lR)w3Ig(^BgF3)@KQV z2rpt;zD6t4URIS1nI#y`?vXqz+07~?oTsrr>+5_4SuuixU!@Z)s}#Dv_fm7t`ZDQ@ zAOi9^^bwweFe`<}J>=>4G9Y|Y573fk)O`^X1V65^Y_DK?T+nFTazF*{d zMM-&}r(LyB@_NBMf}dP=Gy~cI<`Jk^YGT^xvO)PZQNp5fb6|U;FrH_4_!W5aBVAeJ zym<6n;N&qYsSQ=~bNhfpAv!{Af<3@|;TvwdBEu}{`|IW5cXB`jOfnB63I#|L=ll!X z(LCSj%wn*kPa3nGnchk^o>1vGDX_IOxrC33*8IGBa35{Xy0;(n5h-i)$FXI-%6_sE z^AG9x`sDreMfa@?h+aB24SfLgZ9xEt*i)(e$Ta~mCEq}1B@CO$u#jq^jO5oo+5}33 zNK&2X5R$;bKcx|&n$8K(AmqWG9U-?GjiRU#Bg{lRdB52tWcY8I_z@>w&m)gmP z3gJ|kQwNYkTDQ$x5*_sw?arF`$`scOb1T%Tsw_(+q2$F4m0{SxWMU604jV6Ec5aU>`yd(o!x)f8m=~2TrJk z^;fuoVm?j1Q*Y7koQW+qRrAcI|GX5vDquzm^b{ zVL{9iHlxOU6(WYf4i~wh42+46b0TSS!yPn*D13+pVSj}2&}eWQ@nKTjNtKV78>4r$ z<)n#T_^T$i=?W9waDj>3j|%GXL@`a_I2(x2P?x=iO$O!5B~W^#tM!lBbv*93Z|nZ! z;b>_M>c=;l%%Lu%r#6RSi?;gBvKV#aH`|CF?%=Qbiomd9D>9ae#d?v98HfrD%q8ut zvP?6u*4il~R0b3vMw?2m3>7O(;5lGeE;>biSgY~FVv2Y*Gl+$S#;UD4LAE0KVeS7Y zcJ-TSwCfq2m<{p!Vbz)RjA&{o>M%$&`LP(9PXgDn7HqJ*lt_=zLj)?(@H(5-Kn~~ur^?T zKibMX=)orelCweV0WD3a6lrGJskxA_%9WH}Z9Uy}MJ6gA?xY>O8l$- z6A|H&=xWb$UkWV8Gwxmt0chV%* z?8UY+=9Eu1F-z}&1(hivdcd9SR2;Wn_kRIz`d=(OmL-{$q6jZlPkr(DN|QMZlNxTd zYE#)b&$O2=IrAB$FlKxsU8s`%NMf>yErE$il@uMTGBLhS?IX-2)C*XzwdgD(yX_FQ z0tWpKjye)BjeS)|;PE(j5>`bVlVE{C^sFy2bNrHup^84J^VGAJuqcr-4_@DbFsk(Y zc$5wUgE|0%Vm2wF5nbQ(Yfv4R!D9YjdF7K@5?4nUHL<=!*jTWysod%aB7#RRi?RFQ zz0+wlStbx}kZ2%|@50#A#@x-|l+ZgRL5{=$jcG9;7@HXG4Hj ze$Bv8ftsDk?ni<;=Bju}-ivv8;X))`JE#&>C3sATY-mjSig}Sf-2CtC*NW~BP<`Z! zbPf!=VE`tTN3F{oPnxV$ya_tH7?m*{MF+@BHAz@fe(UO(YYhCE0Hh$xf3@aNmMt z3yxx9p`Z|)8!;*m3h%uay82$Klh}^p7TzDM$@kL?+R59adoGuy{7j-)8cgFFp$$4e zF?pCHmhQRKf)d*&##7HyVqniK#4dtjTdz4f@TMgmb z!Td5TRd@`aBM*f8;jgfX-zDCgk}Kg)+(`#Hhxh8J0RX0ydK8c5!xUBNtHfd!UgaqX zw;pv{AD||waT7V6NtvmsF*7!H1jaN%CvZ}9q?~ESpq)pif%o=7FO>>Xh$JTvjq-!# z@$5N|a%-RT7d6WLVRq$NH1U24FT9+Tz0R4K~{>?j3!0dq*Y+7g}D(vLms^txhue|1G02wJS(Omm;rPbcN zaPBu7CmelH3EdVwBg1eB6{1HvX%T7|0oAqAY0wO6p|?p~AyMDWV;^9>+BeCgRHE>O z@ouq8O?(gTFT&MX951PvS0~Xd3~FR_z)VgaHR%*y%XEHcBrnb|52wtV7T8J}7L&5c zdGb-t^q3>LJ~Nc>F(>oAW+dNd#@I7`62vD_xhA;c5^7nO$4ZnM?vXZCEJvnYzv!fC zj|`h$_842L9}b}{<~aE|EYP+en@)#Vl~T7)UfJyUL5Ewh;v$$B^XCg3L}y8_!zVR3 z48W8esX+|Ise|c(K-9t%4K1i6=GQNxVv9tMJn*iyj+Hfpn7P59wKL$1K=_Hv+bK=AlR2Kh$UK#6H9Ogn;YhCAv@uqQ@!;!D*u3fOT}FCX zrK~FAls^dS3aO4TaO|M&NJ(8s#da87nkybGrVR$QHG0BqjZB(lT$h_$&1%q9H_yS^ zoWwruWu7?Z#io zqU|coEvUZ@?hr1nYBzppPMNBl{Oln>#u*rh9L#juv-jGMo4toIMB%L0x`^IJz-oJ6 zD^A(nK52NHKZeKjiwWS*On9L8Zr?(>B}~rdU-C!N)vYi@ujP85?RkZsXzINTc9ptu zY;X$q7c#YCy;6?(V~Gd95$bhil zbVc^s;UEJRSj&j-3<4Sq#2gc-lwDoxsF!vt**0sCQ2AS*bDyep(Lxo5Razz}D_u2( z1fwTdo#~5kLJ!Y5240&(|Fq%QJrzj&v2_}(+ML2HkZa8LUliG z;-H$zFCmnae$5sm#UYaS^lUL59sMXcVzRR9fcuJv?9qYlLLu=Vd=m0|m z#`f{=_!t%^L$|!?3nsdBoyk0Lhe?0)4@suBolWY;(W8%GAJI520Y8DHls5NfUueFV zIiIeAWW`%&^scXQS!S{nx@ou#0qVq~r%uv|Yuod>dzP9Q?hds;Qp2&SlzI2WUznva z$%mK+fVFF{gy{vKyA9pgKPu;;L-*SbE&7kbd_SIg2X|5f9i^*LImQ_@x)?m_hlm+G ze#olmrD;g*SD%p;ZHj@d1$Tq%^Lx$N=KE$N2YkqMkfTfrk;5F*-@gsU#WpyUw=S64 zMSt7|iQIaV31Y=5lUPUJm9N_(g!&kkEhwGK9;!g6m!VzTsoYpX*zXc1DXF1?l-0VG zxQ<|5Ogqv$`ybiG@>$Mz5nP@5Zo&p}XFwes{T@C@c29H{Cm3UN{c}z7yGSY<};b=7}1%9DhWTE$NpujLN&~Zi7_)ida7*4 zD8)k^z7K{NwBd^?+Ck##lixI#$M1ypS<~9mM+e$ensn2jh5O+{511^r7tNdAnR&kX z-|U4}y>^td#-h+8JEFv#I$`+afYD5BHBddYbu2L*?VZ+kmPR!nW%6dp=wb7^$hXX@ z=q_uCgOED`(i&{XonSHIyrb?Xl0qSgS-(6<%W9PFb`T!sNxcb8RZU+bV zU|^Tyro*iMI`vED%b9DiM>UpNC6a766Jivd7#}qgV

    m5S5aj@l6-ebFsSIaHK6CiJE>pG`QDMm8h>Tr+>lRnmr#GgxR>_+HD9Ps^@3`DYQ~% zQWoVe{=F!o0X`s9sxLQ@(lZ97BUg12Zd?U(#EBAiGN5{Orh=%;xA0m{`iJ zSu=O4p=M*Hj!WtO@AON|!x+??L4S`n@}KIaF2aJOz=^R@Gd|8hTLoI%I!t>9+CvRc z3rpiiw)w<%^NN=Lu=Y^-%dJ~?8DE05KlO1QwV z@)PwHOVI_gTOB+06Yk`8V9f7My@%N*8Bl%YQNRX(7)q2G!XTiIaq`6Rz|6`?6Iq0K z^?-Q&37F$&(k~)<3L`KxZ>ie$vU>6=jpNx4WwY9uyTm+{+t3vH`}1vS)tq8D(1gd} z@xwKlm0VSVFe~M*VLpyO+@WPxnm!jAP+wDU z;?CD!5hSsKGHtDsVI?obnDfU0jMmD@g*0o1?n5HeDa#i4rhTkD?(e>B$VFnWFY7{@ za%{5)T9*4*2xVsk)nUZ;{wnFVtdk+Kt1>SE{84d#!ge$@_~8h4H{QyLh`c%augb_LF|u>oD~s`CuvtXHJ{ z)hUW#%Q*4xj!%?!lYIQ$W$=XB@_5}2bGwBwcfzPfu|?r|T>#9)@$+fJuGGJDRiK$WuN=WT zhZowpwzlFX5&~qilf)p&!IR9ytdHXCI)d9qhV_tQ=FJ2dn}-IOekYSJq~6Q~M|C51 zBXCh!WnT&kvR8>l!2loofGU4!dOF~!yKk-er_`G)J*Fm4&eE1x(l1IgTu9FnF6>H} z>3W&I3yr8|2jP10vlxzXVYu7nhJ|!^zM0N4evmM{w4V`COBsnyL~bOgK5%KsmkcHO zfjdp|#lK+^m%YUGt}n&cK|;1GwOjTPHGkXSk=;sE{VPllPKj;pR7MGoRmniuOyCaU zq$H^!*%DmM5q`rb1`3SIkB|JG{x$t-su8CS*5x%5J>w~TxYi4F@gxdZkhJpFtGM6=UWVZ&_SrjqMi0+k4-t$2rh4fa7 zxvh+3%ux*BQ%Iqnx)?P5py5;plk6nXidXm#a?6{_t~BE+N71FrSJy1Nhrh}_i4R;* z5KY6Vno3uBjN`T<2UI^SgBshj*Ym2y3=LJ$Ozctg{+PadwLx{!g$UZU)T)4$k!u{o z(h9D@Ux(Uml4G}%Yf#$*RGdO}Yy{I{CQBZkTGlY5V&wN__hPC<5L6KcFf~W^)T|UJ z^m{ehpfg0360gNf{P83)XjIqw#Go)TTtgBjA;xQF>tG_*2B?-F6oQAPubRo!DRW8# zk6nQmG>ljX`VZy7P2aX-GOtCbcJmmj9a|srfDg5Ht zg`<+h-Fl^&_!C?$KHR}1`{gBsS%BP1D79=rvC)KFb^K&qY7j_hy#yosTaj0HBhty` zLF!cy07-2}kwR5VE(;Q&au}6Y+xSwMDVEb{*Ug}iWv5SxqEBV7GaSDenBCS&UyLIR z!zoQqqit#9Ee)gR1lyT(EMXgBoDjq0=WSqDD-StqKAzoRe#YEYF8ea?B4%Xd7*qbo zV4h3Z{b2)ml?hhzNmUE(ilHZNcqVO5alf zE9D^Bn{RrrHqP?8V%xAR7o%XoLx1L(I7$`XWu{xOL?=Xa_s92 zkq`0^lv#Py0r4z`%qfY%!AE2-w*RU+OvIDytbo&@Dk&hS0morfk1;bSX`)fk4il*x zz<;8KL!v5OYXv*Id+oB4upMz6r^g_45=BOF@`y$?&ef*R(9m8O);^XtvD@m@akGE_ zcJis+C&Mbw%542;!D!l-7Y38btFQoWkmI~=L1 zG;XkG7UHt6S5T0Yl}y6WYluoJ_t@A`li&!dzO(Uh>LVs==0-*a0Pyjy-BtTpBU!pKnaxx$O3E9jCZGFK14i+{H|3b=pb@OE!Au7~Ge1VbrY?R2M-Q zyT{1ItU9}bPkLxe8n69TLs7G}PqQPfIAE=uEt`u9O;R!vYz5ifmnYl#i4{#hk#i*Xnqy5S!$Q>@{|eGrP~Kg)II-nGsIQ z&LcQ)LlS6bhkw8#6(Y5PS}`(D7^ca+n!1_#1U@uMr22 zrACDVYXzSue>yVe*OR`N#@Hx}%L9U(aaXerw6(3Ls%n(w#Gg!h%5-lfVTr146?Lp; zU`=Xov7@G|Q?^R4E~gQY;Fe5untsw?lZb|aW(4K1TQ99LY1w{e;_FAy+nPpo=j0Qt z1u48-MRW}UDRxLz%C*CbQH`6yp~r1~iCN7bsBHY~abUsdQV!R~F)VGX+;DIUrqMRV zETUR-p-w&FP+|D#V!z?B%wDzjR9uYjt*xDA{rb!8mb$~kdl~s3M&1XtJb^)1{AA@+jvWlzpjxa!L0l6erZT`FVCSo^m z5b)zt^F9V`k_a8Pqt*;vF@V?WD+UPm0cuR`2^O^OP$iAu4&CZGZi6m#44ygZw(ZhK zsT`+x;&Yvh=-N3`?P6l0yi}pi5@l-jTn=w&1Yy*0wRKET(Aquu-VC$AxfZi>(sacA zpy`)})9wO6nU&(EkUgK>djN`8=2%iw@^NNok#dWel@fsVark#jXVi?1msfSw(K_h- z-DW3yuQzTy4ZBWI=z5_2tt6pU0^uw>Vm+X^VP_t-6}OswtcpQuXh@SCER7@F3MLFU zzXI&$i&-Vtz^vpo05Y?LM)XTIQ;?Cpz%gctQUjv1_&L%tVOlR|b$b+71^H%ypBTvM z)fby#>&;$1pXC3~-h04DmQ?rt)oQ6*8c7p1Q4=(?voo`S&4F1qFWH#n1)B%v0S1%& z2ZQ;82@eAf*v|nQumKyd`D4sFV3KVxhRr$7<}^u@qb6uZ8s+|fe|2xQZnrwz&Mi%! zkFNTL3a3t;I(6#QsVWWzI3prY_|nUxA&3|GvF3M9|q7(v0 z5JuhY9L$;{QeWS8v+{ZZWNY8N@4j}z^z}WTl{T6%X(5xvO$gL7Fu7{pf_5!EGjha?K}Q{!n+;B$*l!kJGGW%}_oWLgEne0+cV%EKhg4a9CA6XwruDK-rtPu^O;e|4RJ6cVYvGP(HH3F? zoci*nJJ_uq--r@HM&2-ttRX=$5O2#qB*>RuKaQedgsDqM)OdC5pc!q-u1RQeVT>Kj ztvxlHRO3A+Dx*`xbZ%fK5Y3`(i_A-++o`c^31y0z`JKvHNGdiw<0uId&o3%>Gsz`5 zGGjFPHmr}Gi=lcrA3<(7>08H^Wr7jbdM>^gL4{(Iwd2SXv7GNDtmI$CspRE^+%=Ie zC)t*Uw&x2~yYJd&Mm;^;8b9kP)4W7>2%Q|W3a8S{jU^gw;gmocQ;9V#rVr(`O1NT{ z-KbH=V&SLFT_p%FEiD-hF@^5p=vN15y?*Y)2;j`xfbcnSqR(`{1eVGiWHLFF-ensj z9F?|*cbP@W+}TUbmC;`?49)y~GO_%3h3x1$f&K8v;REK-f!(aF%&l1|XYHXCrc=s< zrL<}79$N@#1;(oALzo(7lW%4<_E_IOGv`c%bm^ok1T{abGUb}J-uo>gg+Vjx+w%ybE}$mn-1T=pB`8B(6W$@ zSxj7Ype#D$v~3+ZS0?`=1Qtk*_IXD3!f0J}@=eJQBYf-(mWkkMFu-z=Ow59!z?Lkt z%sQANtVodITC;(jv*%h*>IDnKANU|`Clel1a8G+-jmSbaAJ8Dw2(_1%*1;9SM>-Dl zml>W-;9Z>!WQgy&gyi#SS1tkMTGs^_^0PQU%A>qFzSO)f+KZ__L^)l$Q+`>!ki*vF zf~yXWvW-h2b)k+%cFJNv`J7-TxKPk}({jCOo0ZweYZxKebsi0~6p|H+lnqawnh9)O z#Z%=nFF;_42ymJFqQ*3|nufs9M$JohnDG%juj30cs?=h_cw4u*Ji0gkOd(-)B-DJC zd&nQE1+74g7*=Ca*g}T{qPo(b71CLB2*bLxxKiL0BIA`^k73hcESCk29NcH-uPbaA zvS0&~4F`8;Nm1wXEie}~|A58l*^SFtBl{oZycwoG@||>(DIaMwg$TG{8g{Wc^Z&Q?ubXl-Q~4oZV#_k@Uc zP{;$#UCg8`$P=hMpR)@rd7EhlaA+Gl7ot^#ICQi;C{;KWTpU`~?y)-?^dH;oZK#)) zWlKw&na@UbeW|i6C6o-N@dDP0r9}20Mhft5cJb8^=gyaxcz&>({;3{I0PW*`WBW7o z>ePTy`f3ayf`f+qFk& zg*c%`c&YIhvzu$!Sz4v9RSViSrVsa((NXNbMS%R^Rx~|h9cwdncH^Ymbv6lGQf28~ zLgkAa!mKNdIpd2sFus}?IhHhOO<>EsP^p$9P=E{l0%{-jVRl;3v0TI3DLlgf`zbfW zlmnTnq=6O?op-<_MmboO^Vu_1yxvV{XfK7E*>V!$IZY^3hRXg&f&6 zIM81RIvKEA*8V+7F7)p0f8J{t*enO2N&|UmbT>$+#@b?x4_g`Y9w1i=>f@JM;2$s4oP02`D$@Y^CG!-v{zQP2xtZd=a-;d=< zKRtY==ON1e!R1eqKb=|@IC}JeS#~+)m?}%M#7KLMtXgy; z@Yv z3)$*f>o?Ou+qlG*r@zhoJi+!*ORu6n8LwL)y{@bAtz*<>fjWrdJlrB1R~10=N%nJF z^~qztG8D23xYRO>NXH3AyX@XRax{zJXfo|+)P{x7>7d@VRLHd@v33k?Wv9wgV6ecQ zG1K#s?P^1FpJ~f+Je3;cu|qrXTzAZ!o=S*k7;*M$PU|_cC+l#jHQr$sMh@W2D@|+? z8YhY+ikjwmgK2v$#M6x=6lS%xhpLWsb7sbj%}=S zbtg1T|KdMurxUwMAzJ`HoOVd*JP*Eiohjk93$U$X-pO;_&IV@5r!23@lgSusJ5Srv z++kIgY^GHr*aM^@UD^zb@%7%QRk=>Fw%la`jgxPaS zn{kmKkzJ+~IF^W-kF9uw4HvR?X3TaC95PE^f*mls0u~yD**$0@Cl3*RYJQ^myoKkQ z55;~+yP0I!Ssme)KIKAU=v3}K+C|G*?Q_`8759~{C1yUhvaoQVyP zCc)adj0j4-H80<4I@*_-lP9%H>QsnfL%N9NA;$xh(>Z@(de^C`O}>$XVz#c( zx-4YvCMVO#bvKSMECok7YTnqnlNr?PYZaT=qwL83?d*BQU?l@%<-%{uH+t_S7bnb- zL))`DVz%PUyR>N+8#H^vHeRD$E;!4)5@wOn zQ;nn(f23kSwv1zJ8yM}B4;X37`!EW`f#QGwI!Po)0vq@>GY!wS&!<} zQCgTRriLL?*7BNIdlQ{UW~XG)e$kSP%tvE?gUHfQFx*ZUQ!|@HYd93o;?-{fUq)&@WCtJFC-%}UMubbe!z0AIGiZ7ehoc0yw<RoD+gXuTr2N->6KWbi=Mc%oJbm zWM(L2tDtP@$d>rZ=(A=Vn^JQ#Qav4L?ELQ~`T5N%^5w>y=`815g zk=k5xi!tjy?S0Pnf5~gv!SPmBd?Pr=@hC`0+m)?V^T|bkj@`4vKPFs4_6euAz=BFG z%X`h7*(>d7!xc4KaU23H`tIyNMO=e*I3AahsgyCvIXGoIY z&<-y0FAz=xfhEx$yn+IK`O77UdIG+}Up?Mu2YVBdR@wVXiPS@F^dTpW6{rm9A} zXxYW)bFnX*-F^9w#R?`QR}w9Y(PoL+cKKBLI!mw=P(2j0HZeU7PC!M8m zN+)=NavZ1gz!;wX$tK229pqw#m7=f0W#Oa7TtoeC_&CZD&(0>6oE5YYv;PZ*i{Xpa z!!YtvgyqUFl72qAfzL=E!@tb|Z7+LPnZC?ifu%Ml0Tac!t$DF`=_qUwT;CjwMNIQD z%<9lGSFgIT>k9Ldp+``DgOOb#nSb-mKm}tdZ|$vA45)!fkf@D!S&cH}8-C4?8Rz3Z+pF zoH%ZUm4sHVdX|=(%Rg=si|lvBEV;^*+5qFV7e{V2OBb)_^Hjc5eH=T;&aG&rsQ}${ zI=Am^nfx~1OONnMl0v|!9b}J&aLAwe@zxf)WoWRCV7uxr#b^H@xLrMsVXf;XRWyBW0yZ)D2} zTggd!N5XbOItloSfI91!CCm^m0lCiIo5jws=XUg&YZ#6UkIG*6)c zgehi2#`JXLu>9%R-7-d1btJh-Fft$_i zo1ZXqFe=WOol2X@G!y3N;k~BoT=`Z(Pba8`cT5IJze{W}X8T8sdEgbs?ER-Sp_Qqv zq$i-~KsX7#L)f#PU|9J4jeJ8mSpXlM^DrR6iGP@dKs^6VF+bV4p_tTa5l4bbmxNZ3 zn4xfih+1MWG0QZs+0Sx|+4eZFY*DEO)7IK;K45;tU2f8O2b3ztR1iaJ#?h@2EapTP z@0`4;X@`9y%nqO}{p;F0?T!_>>{iki_wIYd%sqorYu+KfSQ;L-Vb56j4o;4EFWvrX z7?yY1o@5Fz?4|r>*~g4o`&D)X;F`lQC2dE5MHlmGSO>Dopqrkdm#FBtc*(iu?XBN6 zT|>Lf$Y8(sA@Et~xsFtB343_WDb=HD^qHhPAiL))hiVs?+aJaN^&G}E~CFO9i}c=yvW zZTmRs-Afh07#e*HBcL=C9mAGtt1Vxeg;6V#xUJw;URVy52rU}K>CL0`>g7ORgdW{) zPTqN;*}H#J)^iJ!ML{^;+23P+mbf6JvY~LtjvTUfrDPocMvZJi*V1flFg~6 z#*6f3SEG>4>Sn81vA0%E8sfsxw~ZlMpIQ5B5I$YIlCa{e!Hn?SXOW8E;?pkgvw~gl z2

    C{;s~$pQm|xZ|wZX=bmW z0t!QUYKjqju*Mj&o&5uh@~@xzU2oLM^fR1%nx3TP5;}P*M}|yI6^}w+E$Oi!WJHb1 zKyEdmGI^GeD#Q?TYX!PJI;c#9k#+{%TUVaISuk!6AKsQ5M=I*t+1t#Eo3>2xK=FE7 ze6F%Az0npZr81cE&>ro@fLs^aqa$cmx6wFozRbg10|=@?Y*%Nz5n4+)rGsrOzTgq_ zCVd?Uzu^lo_d#Mv*w7m<`f#eS8a+muFn-@{giA}9bUA0ibgoF4!<<~4YXP=8fkjI% zGw*Kyp}An>Su?<)r%rebp?3b!|!{9mMFj!baj#*ZoPbT?^xD7s}{`WX!NGf^DelHD2t)w;A)~ z^^@so2~eiA3BBYBV@_ah*}ePGoSLtMPuHRi=50neSv*72lO4;Zg;pyjF;K9M7xJP zj)ryF-Kn`tus`#8>JuNV1Vg?QsH}iW$%=>RF(edCT6=s0N_r8>@H|4}8plXTgC5(v z%ItsgINst%I4L^wk@l%M%ID^=`O2rIH&G%CCNPUv;O(AC)HnR*# z#`V8h^{|=Wj0biW9D}(?L)^RXQPaWbbtyt4M3i83dR-o^07IiR&{v>$@hchY(I5(F z=ju;3e-HN)wp3oW?DDJ~g3W_}L)embAAHJuX5=Gg9%WB=gOyvvyspJX2`ZO|evcg5 zSF*s8uxdtJeJb`fvvBG8rkVY}a)E54dv`o;*1wfdnft|D(er$xQgp(_^%;2hR=(No zkjkEE9-LA?yFM}XZ9P4Sb~=#JT6-&im{bS(hNwmAkct)CP1oGDHluy}o(Q4pvx8~# z0`t)|JIuRg-Huw{l+jnqu1hNe`s&&3={F(LQe!xQpt`Q(koi7C!1>J_D-}ZWNJEsc zV!57KihKzXWhyq7v-HmwRtllV(FY0=R*I%7TKQ6~&5M}*`ybC+C#@_{KV$jD=F4qg zGG{mKO%YfsTRDW4PCF`9yV4$4m$OIvrx8|5x-ZHrtW@{=4?J$Vuc1o(QMl++KJW$A zsm)~I1QYLMkhh=QyYl5&XiAo?3XN3N@D)0cFC&#`XH`0IXam#cDfnhcRHHOGfUb%UnD4B; z!`!%Vv-PnGlrlpsz0v~j3A1MphH-{KNF(x8*<0tJ-ci z%h2Q_vd!iIE3B9hYK})t$MTY8W%iuptF@0#CD|sR^O47K{~^;l0DnlR%14Cy0yc?P zZk|X6tJsbFs-_@S#@xj8nu47I7`1!Xn!}q|$4#wfA0pY#@`G0YH`7>lA3JQm zaOf2#(L@8GrOch5X^KC8dJ3V&CqW|Hm1exMHCAa+lee&3T zv+h?jbpk47xd0-|W_4Yl^6$NXVf@o%KrdEfKwveqK{<8??MNsoFa28}l~6NXV;C04 zk9+T``M=V_N@dY>>=?GIObi1Vh917y?Ax;m8x>pe&RxR_{%}G|NvpbJ*iW|iC(PFd zt~V!UEi!W=GlW5iF8rV|>pz2|>0cYO=Mx#i)aF$rasRpyybXnPCShd;&QnWX z4PhlFx92QQ%|7`?LhIhk&2C>s?(AG{X3xe7U4S^y(2&D<%qbij?-{(od}m^Vna{>^ zE#&X{-6*xG94IgOqel){?MnD4guB$wjc|1QF-}K*$GkqX5f_UU@j-;4w%*H>no`IN z(iV;$+k+;#$5!u|Zzi1D|5TJRCs2#G)XSbbshh(HW{kw$%P%dUN|+=;lk7jT$!tBf z-28CjrDn%Omucnf#Wro!$615g7*I+^rbMOf_8r^!0=-$|o%j*@`q9!t6h9{!tvHp4H*~rD_p|N&zm?{4` zShzbd`is%kSPn4*HOA8^?vF97>x>MWbJ+25J%Z`-*?Y_xZO2Rt+k}iIILMVu3fHL+ zY?NouUc8=(9ch{{YjDFU&1U6FTZLb$=D@?aJnW?8tfgCw?@|kUWQ7NhVuL64nf?>I z%nPII&E1J>%%c+>rUQDnqkctjLutW)Z_^bk&_QUKk2-~P;Ipl}aWtuebS4*|OlUHaL!!G zikizwtt10G@gvwA$LQ2lI3XB4gI}ip?M82Uw}CvK^)xshO_WBgBd8MF^i(*I2fG59 zL)e6sUq@#*L-+22I}_ETb_YGhdc@(AFPKBj3+$h`!aO;#&}>euG6M)8IfJMJk&A@u z9MrkQQAb7kMLLyAPKIiM)xS1p7~6$5~ipVm6xUj7y7e z*Zq{1dm#>n2%-t>o=2ZCW(!VSkes$|%$c_tGk-O{SSxv;RL4<~4nJy4?|W?$rfC_% zE8Q`%nV?J&YIu?fAk*j|rj{R*1(HKU#3f7cXJYH?V!;Y9>U3myfE5LUoC>|)OpGzl zh(Nas7(m_D3IC#a=r=ddGg05kQ6K3(^bFq80 zmql$tA2N)jixAQ8^kh|wq#FAXJtuI#!sv87SdN&_gj>d%B$HkjCtBaW= zE4W>P`UTVpjUv2Suofq6pe{p&;kzEkkvh-*A*#(EF7>cZr6=4ykZ{tKZT8lM%l^yx0u-RHZwHz$ zr;>yJ(v4`okhcM;6evt!=>*o{ovA65^hD*;JK0&vlF|tgSsbVBE*!swh>|e~@kjTp zG^0BfnSB^h629AjhG`4f##U$O72CUJ%&*9ova_fW$YcG`p)KrKz)=RshH+5}@fsG^hRQvjV%g+HvUoYrJn(nDI{awD$+byzoZWBK`+!b6&%D;L-Q9hK={xG%bZ8 z`pI2Hzf1j}Rv@r9JF~M37Oc|Ox&%8sA2@=rVqGc=Jtu|}=8QK`6co#nd9^M}TpCh^ z39M0E971hF5f&+0P}^Dpi=itCGy)A}LvFs87ZV97h91M4E-~7>`>?eI@s4uHT;a_o zb}?0sL0nyQRl(B+P7dglo)^5F5ds?i%|D;zd@te^Bl}Q|L96&X-(<{%U-r`Jf08wT z=Rr#zuYjIyt0^+&laP7heGKo=f@GJn`-Sm-Vn0K~-(?qoKR|o>JlfOeXo>DSX(SGN z5>yJ@SvXkDW412^9PLQ|4lZe9`3!A}La^Ppp-dw;n;2S6P*-h?d*{vTMowyy!lj#% zSydk-s800v*|nNYtdvO5w&u_j7F_}w$n{Y5Q&o&>nHhC2KI6>90t6P3wC_&28lZTl zT1Iv|SpfHzn^7Uj_>qNX^o1UCg4S{DNUt|6fEhKTq;}lW(qRQyET*&Vyy^0C*t{J3 z$lf9&uzZXL29L7a6K5MyLog8Sg|hL!epk z<8S{OW4#X`ghB`_Kc@W=>NNb_cOtlui_sQOaH&pYdz>|EzFml3s6jlKMIra94+vz> zD%67VQ4DWQO&mMVEOBhb)n?_TKTayCg`_+4!-|K{*8Kxq3lLbS4ue}fek%u+;xMpw z{j4;wl*0_~s9-J-6yMwNzC~tibB`Gs95P4xdok2vE24GiQqz+FllH|a^a!p67--vB zCeqG*yA^DXunM0FvX8hoo~>jQC(q%L0i)$1V=TNL8^wM?d(bghrsXU%e)uWVJU_;% z%p;~v?Z4C>TN>GdcN=rwSI9)_PyiCBTet?8ga3e^!A}uD535~z@+yfp&nlKKwc99K zTGw?93EzW#s+;GzgeNe=YA*mkeRmnN>1u|QXnA%R7)*@sVSzlw0(_wg;>x|qXs=TaB5ll9=y+oQEt9YQ=8cy(m!TZQD$*|mm;PnbbA798d<2o@?Z z)YB|49m#Oq#Fwr$%RhXpiOy!&e4FAapCZed3%%+5t%dq;o|B|Yc!5>6;UJX~#gkTy zSPd9ND-<783X*AaLxaaWK;l~=hXBHej^9M31;d@)?As^ zsDwZ1C@@6kB znRq{+`HA_+O}qR>nJQ32V5Qcy=+_1>Xs@~eq&@{cq}+<2TB1`9PzA-7l9Dj0B!IIV zUHPdFP#*pw2;!DCi%rXo3B0GrO~e z)xg*(o1fK`R2P0RG9h6F80GUyh_(y)H5{EFi5B|i_`-x?Wfu;h#HPi`J1A+2b97Pq zO}X>Ql=sYg2Ngv?>{XeF9!|s3js~ODYOY!fOSyQD#~ES67aMMPx5qK%z&RHV9+w6} z&~RJeR2@#Kyrr4mwUBo_(ir3&FfoF2=;(YiF@R0$R11QP8LbiA5J$A?OtE14<}rru zBMf1NS?0;rffsy?VN9Hs5Tgedi18IcI7F5)#QGqDRzU78QJ7>{7f;My} zc~PiMzrxJm4=5|h#L2AnMKnyV=xXn~kQ}2v#-3z&PP}2lh!)K~d_PX(MX0{Gmw4E_ zlwKw9hHQo{YLc+!i;NQyyoS6HA{sW!pKJIS0CYf$zq0cXLJYgt=m6xE_KFO3!(5vJ z|E!#`bmyM-rAzDlQI7|P1?ar<7MkbDjWrbVGAE~;azNJ@e<;Y&ZgI6 z6T6SFWn59A(|Oh+oR+?dJR~UrZAhm@oThcrC(0w>vkqfm2c+JL9gKb2dv&UsFx@@p>#VKl>*jqo|5P5Gn-6ko~f zO4kVKOS`HWK++&pHHJ&4pM;JTJO$7~Y2*@CDzDbkJ@z&Qw$tPqhPxsnb?4XP4=B4_UC#1WKXo}dG! zyN;H7;m;T@B7`Iyf(=~v!fEcS7*gKhX`wN+4OQ6Ae?&|BAon}D$_Z=n#D9VWeTgD1 zZu47tWYkbA7=uR_UPZAXpgNYFuK}MewZf*h`RX)wyWv4NG=c+_sWC^`Oj z%lk%*+aiN(iRt9aX!HmnB&{l(kMK}78KHv;Kwzn1Dv(aMX`)9>AULW4@V~i#r7_p~ zG-zfx+VgdWYhMJ19p%SBLhEs~m$_W$|AUu)M-Kw&w#mG-_}LD{q*?jkvU@MGGCsoELfNi4QkJ2Tl4&`>P2+ux4MgviZX&u!v!^#nTGJp7G_BoBAoB zy^6iF0E8S@BU8#HLRDr3!E^Kwh?W80Qi`kTp}C8_6eR4A-OfFBtZIT55pVl4q!2B@ zVfB0Yk+ABl5Ejt=`2Tozp(ox<13WmHkHsMdE@{^P!MzA71pBQBu~U!pea~c^a@~o4 zU;Ln-nrMM!*!5}hs+F)3D%kfHzGo7r0kp03R!Ei2c+iB66;_3nG>e7>#ZED|6db#Gh88m(puL8{+L@)uI>^kv z;}}wlOfZ$^AgHZI%92)U$|+CMY7*Fr>H#F6<7hY%5`q)|$v?HqDuc4Tlc6ZSSr+tj z#SVnYs;ND?7otr6hbTJ2N^)YP1;O=q-1xMR2a#af2;Ja-Y+TY-1pyBz^ z`%$nYDSdiWD(igdYKC|gLm{D|Ve;{(`4#`>i8Ipz3f%7(&Rw~fdv6H z%jS5jB-A0KwiT%~L5Fvhgk@PH(ETnje6Lc6fJJ-JCn`YfT(h`aH5DtN# z44C#^ZAyxTPR<_ZDu=B(J&STaYs;WAoYqe5rh&GPal}mfRPbXH2Ixu)D^-+S2kHSL z3sLvq>0zQ?z1B09vI`A7gWcx4-~@w`hBRI zc4+Kg)bLkpFP*tSXe4BJ1?M10*4q8yay3N0MQLAgCJ@@!qq+E6TQHq^xK~-Ua5?Ek zTN+ZS0!y1EX+1%?z^Z@cN-Rq{WArk)>NQKWio8xR6j}K`m{1G|QU@QSqSjR;tI&xi z&_1{RHpc%4z)Rc%tl_!)J=~mWA7{)zk^S=R2q)Q0eWuVdqS2K+V?V10h#+JvfB48W zft&0)rGV-+3hzm9`Bhv>a%GDvT3!cWu#u}1vKiA7U%>^jCwJe+Ajl0a7jS8w9AlWaY^`{MFkE)rey(YU}fFa z0EF3;Y>KLDGb{5y8Qkjv;%u4nwna1)uVdy8kUxEhNe#WE_7{w3lUY;eS>kZ_>aq!z7R?@(#Z zB2H*zw03UJjRxglg`pVtyGIVT>OAqJQ!~K+AnUqVXdX>J_AUJdTgjj(_wp zbPZ%9xNrCyuK`(S#(FCNb*;6U9Gq%a{2yimeZF=0AH4_7YGr+DR!LH#eQO9CJQs86 zM<~})KLlmuY|4__vD*Gk4#N1AIu}+x%FSr8z5fP}NH{_3vpz>BvCsd=a}i5c{K))} zrk#kId{gl|xm4KEMa6Ss!;;&|tzJQ}7J3UiRI7XeQgV=?TTY~QixZz2QUa9mLCK3b z)Z066a=qI24R+A{bNH|hW(S2&KG4?3+L7bM+pq(zP7N&=mpjpDn$}XD?R=AJP;F43 ziruZ54&Xv?oFyEgxo9t2p|q=exgSFaB(_!?x#IKFTv2dC!3Qp6qTmT$Ql>a%Dc03t zjA63fVMR2m0>pdZ57${&KEDcRSRn0G^vdI(nuA@Z1Vd|TSDV*6Lq@XhVC-^&cKJN> zJAo8ow(au6nkK?Zm}~80GrtdA$}kb!gP_@_Ya!nGKPC)zBH3DW+B&<{fjSBXCAH3limFMEy=?}P9(TWvT65^T# zcJ$${q9EgX6@`_`QW*`)y5Z9^*9AyO!39#2HIp}fh(|Gr|GpF))qsP&5b(B#1gL&Xd3z(wOK3LqK*PLomZzx$^QXI?`%-mANl(2hV?yM0bE8W_SRGLQHO zDb1EC37K$u{s+a1;i(1Niv4?jbiTlS*exf}4y&@nXfo@v-l?oX?R?WaaCxBIF^0Vb z3mxjpp1moi6ul}4N?xZA)j%;(-&%gK0-^lL7MG%5dD0eiwJ?*R3gvVY!x34oO%|NC z8fwqSNQy?RnKrElJNV_KQde+#^$ymIQaUU&;_hvtK&RfupZNv2108OnuO)wq) zPUTHI1>sm^;6y*_eor#;NdH#kQ^HJ)$4%ekpEHLad9Urr=WO_0)4A%GOh@-6g!P1s zjnss85bvPD1%!;%uEY-le(kM$_TEm6bL`gMCr$tMe>DA@|JIsDBGF~OlBTr$@X5Hr z^u08u-+#)`ypGvgcxUBXy&|svLx#J54oP_*2B4eWdn`=YQo@Mb)jFv?2c8uWSmY+A z5+Td9LOTckW%xu*w4vh4+dnicXS0KUuv&pF>;Zbj<_$bQ7dnNy4yXexeE~5idc^GG z@Wg1-DDU=JWlA>TlRLg`PV|0{QNmP2zMw4+3|ho;y%M%Tx@jS55{Y+4#^dqYECC|c#t zzEp_m4+%8<&1mPV-sY7h630{gdW6s(o<)^x01fQn#+WrK4ML+1gq4a}fgxdldaY~8 z!E#vOWu4w~lz3<-T1Go5#`e2lo(Yu`yUTP372#ThlRI(uP8tx#roD&8O+TX)4nj^< zo|5I2K;fhHM?*-d;Uy1abhH`g-6-*~_i5;C*MaI?cDl)oweD@4(dZ8q&0S{`T%C(5 zVWt2xuyM2j!M~jm35zXG{T*zthEB?1?jgPI}$+gKW zgFMSOrWC4(v9u&Xt7cp$((^z}8T4r%xdG z?ga$Y5?}btT7f)%#2YTA7ea=0%?4Tx`1BH1B89rX$^rak7uw23q%~f$ZP}R&eb7}$ zkn~i~gHxoPmiwE2AQ-0qafDT#IMeB~Z36+euyWXM82l_*!0Meup zBeOY;`I*W7uKg^i8JkWN4S_2?p5(oTc&=lg`w0l!7KjU#M{vop!b-3GIthd*`)wU* zR<3LT&UCNJ{p%A}2X1PY01;d#Ne|IWj-Mj4in4pT3qyLS8cW{~zfdk+yZ)OZEU!R5 zHA>P}SD;Spv;CKX&>WQJwI~AXDEb1{d`lCO@KVU2f|e4gh=&Mk2ZxXCAf}H)$Hip8 z4C73LDDE8le?+N7d&;`(HT-Dk%(G51SDUe?SXH{<${(mZVFg}n2VEJXyI154|9z&j zb%JU;vdar2C$tJHdl`^Pdu5v%?|dB7z9t_>5xBGP<+AmPyg&XaqRgNMlwSPw=OiIK zQfX*7A5REfs=^`qpp*V}3YRMhFOjMcA?EsmD4Ofs&So>mojRls9hZ~Z45Bxsv5;V= zjNU(HQTMxegkp2Apn)=pBXfhm@>{OT@Pd{8pFHYLSfS?2yaZHPU}YiaAjs6kEQGis z4^wD}q|-kfpg5XX0X{P>US0koapEeV06J(V58F_@H+laPwG~@?oU4l=qXm+YoEbuR z4b%8W>8EsQ6^cN~OPIx&WsJd%t&H|!TsB+%lTB=F2QR=)3B{qkT{NuNGYJ?I>O^P< zDz640)o!8MB~)~-3xEMe5#-tp#fNH>!{wX$D~{EoX!Xk{rX4=uOAfEZVgHMgLAkH$Z6j%2?vugY2o+O2h_Dja)~Wk^d}%|M{!f}!-3Tix-#T@ta)wWt(#u*} z3LqzYR=vAGU;s94zvUHGjcFM5D!Qt)qSrh&lL*@TO65@mi zET~_1Wls~FhI{?bgw|r)OYij7;GOwb9^S&eF8A-sUaE{=)EWn{Rmqn_(7u*(RcZKJ zH^V^cSUXN6{UTMllTarD%SXsn)Fgh@y-TG{hP>(WI#ziMrIt3h6Nai%@0S6a!d?ks z#$D2)w53d937QL!8qO~L7>BGf19d%RIp;f*$&T<%r@Yh@7(Z%Z^s&c0EP_0;$ zCUG@Ft?S*My(hAUaMV*lW7(P(a5D3z)r_5wOg_(gE%&S(s3eHz+t%?fFY{Lti^;Ty zwos2~_E|tRTR&j!6g3h6+RZ_2Iv2=MZ_Y|T1c8tLg8MMOTe%Xg@V5wR{usSB%WF*Q zWe6x5z~Xl>wRbhoOTEk`1cKVF_7#&md*5CBQs!FovQ9>RoLMs+=ic)*yu#m?N;EBM zKkN6{Q~z_gnLKCAQ@@H<_JlW*iegM_LO>hrAlgs8o*_axMux!r_7gYdHSiQTi^1vzWUa==<90)k z0ABOQOp$Lyxl*GIf-9$fgOVEIb@*OmcA|Bi+{!ok2Gs)~tmN?4`vuNf#I1yptXcmc zvuT%l$hjhPBA~B$d=lXkXlBRgvHSj->p#4rYFwc$Z8Y3UsC)?JdI7g!T=>}CE?mLN zj9|S8E43wg?Q65h5JjyYfn|>cbB(CXm6JC)C^aT~O^$&2rWvc?;jqIDqpZhYj(8RQ zq?RWeM;ASFFFi72y!jtwY8-8(^Bmsu-D*#n3g*9)f85D^2!hYD+r&RZ2corTh_Iha zLcE>;&5!kr)=_r|ZRGJ^fzfv{r2U97XM6^{=TnDiW$ieRVLZ`RyuvRCJjPBC<`BZ` zARSKs?_oFMQ;fLM)Dnkzpq^{4R)T^AmdaD^W})=#t_%Fu5~Rrw(kZ6bSq_})NMLE7 zs#<}DL^<*fev5Y#(0?&}lKXOVQw}`zNrr#fpT=ImR0H0New~&6pG4yjUO@rn<7gRP zw{RsgQD)E9{7RRZ%Vk;nXWQu*7x`mHH(U zZ5F6up0uC&XQM6rUk;17YBEDRly>;*H-Oj(8cE;tC`_MYPxL$dI0&Yr2&)!^STp5G zY@bYP+TBrS2|A7_L*BWs^RSn2+WB?1t47;g@Gh>mVtYi$w?n%mIC`nhxz|x=OJ40= zHq0nm*aK`(_~-oo(9`Ogmhwe5OLbHg04-ZfIQ8rC{@G1mrhQdfYaRlv^ zp(2$tkjk~0@)TE~W=jYWq-Fr(aWO)TK#Y-VIG9cwjkWDCj1O)HfwH4c+FG+X!4PEv zVY>Letb^f-FY^n07kWpsAHT;7XYLI=Dw+SJ2^gp@oLt}at8t_?)X8gsJ|4nFEze-i2*^Aba z3P~-DN6j>LZKPxPfgbpVFxhz%0_=~FR|u?l5ASyKLA^+sK!P_mYcerS99Zws$yX)a z#aB_x8tN!I9A>E3lDgP(G}~DMHGmO_uLNxaC^or57e)4>WX z|7A4BO{PHUkwQL4u%k>I2NmjLDVo9RPth>w`PCa)9F-3D$vkO+r*^Ob+`nNc{{+my zCk^kcp`3ELQK0teYC01w3yoowz7wzeZ3O!X_cH`E72b+~a?t-3$Rq+lOU)1>!|Wy6 zjXc88?*#-9f_-ee_kQ+!5MY-xK>=N&aRL+0UOv6m&{w`2`u!&FN%QE_#yn3Q4U(31 zU`1=NdGS*$B=%9?^Cn{t)1lZ6?)ecqLZw)UEu)+&S1mv^rz0I_7^rfT0FlJ~>n6!y z6Fo+9ldnohJ6zP7I;=sxT)T}Y6~^&Xnzj zb$CYmFLUzDHz_0K8}B#f4ruBG_7qyAzkoK8E}~MW(l{mKr08^Gm|t=sT2HqZ!PbLr z#Tb6w=<&eqY{GY)XRD7AHo@?{7#Jf`aNBL22qg9hLZJGBue0APjRkaMbcVHpEKZ=s zpd&7xgcudCN~spaSCLZ{^Gh$6-7O;hrT;9RoC2N5rfrin>VTP3wg8zDA;$}~rXDW1 z^h|nsG3o{Ms9MYAz>#JcVf5VZ7;`&F-QK~p{zr%gCo2izX@@4#j&lLRioXv!UADagcIo%5|Hx|!6| z8ljdAo&9^>ZnSIujC$sKWL-!sh}%i}4rHpB+B%a(biuO!g0tKrVu4|D_P}NSLf%@m zJltCVzTr?vBZ(@&uk>*7iwmX3V%-CZi(ZykI%%X(NFYS|e+Y>no5<8g?=`+XTq@R~ zlc1efBc%F_*+|c$oh2;2a?!)FG5=Pf96|+JyvIFWQQ3vBzvd%e@V{-3J}} z?w<_JqlM-F%Ukcb>Qh#Uwhq0Mp{CwRBa@S_+J?%q>s!>*vZ>)hzagF0)r1!A)dUEj zA)rhg6@WBO6}2H&rJ;=;s_KLc7K(C?WU(`d1X%&w$be%`0%{07nl7Vdp1b4}Ci-TpD#A2radq5hU2t@9>6=&;KqorH+HJ^6d=wq4O+hfIY!4srMtj zXza8L)w?>x{b*>}9CsBa7l>tQVH?&=sx}(5ZM&R&J6YJN0;@9l_&Gx97J8BtC&o!+ zs&+RbTSbOi&+sZ0CpB{3BxGdw3Dr4=dyMfJp~qwDmtbSL>P!?2o;a9LQG^@>qoSTr`&I zu@IVaU_J0PV{V;g%%*pNgWr=`KI7XZKc{o}Ru;t%{+Xr9Z{;Gaw8N9V{1#CT@s@QA zbxg){#Q~g0;UT%r2z}Q!Dv)ogz|x-UnHq9gGNvR=Z8XXn_vUgLunn}x3YFVHKN^vu zOG8=o1#Tp?0-#wdIvaEDcZ|7;fI8s9f}f{O`~3yMcNl?L)T^$eN; zT9z9|ZQNNE#k#B|i>zV@brT)nLXuT0;d)>tKYpiB*37~rM&0U6M^)#EpD;OsP3ox+ zdl8)`J^yNBu0sP;RcpmU63_@&e8aB~(mDPDCKLE97pTaYpj9+TZPuBBmG2#h=%jFY ztycj8B_rce8e+wyBNY)}QNi@XP)o$!8YW=&F!Ghz{z;gEw8R)azX8ei2ExNI9WbV5 z{pN2Byyx}+y{udbwtdF4g_xtflw_+i$l>(?hK9mlR~z-R?6cgjsxl%)W+yy5HsBNW zPH={v;@NFq_3Z83?Z)vUk&%1jcS7ME8fAg>Vxpl zy^A8LE#?6gJqbKCBfPa9vRa_dUv5EGn^c)950ida8h<8FHL%$H0zU%Cc40X)iknmD6V(dYgWuE@pJ^-zs~Zsdb1(N&bHe~J<-_hYej?xa2zHy2E0={fA9GBiMx=FqTE=nwN4%c~!b;_k3qTWoVYiDDxl|6pir>I* z48w2>jB4dD+Ceoz25I#%OA-DuQtLnlfri>z=zNQWl{ArQ2rb}zJ6gJC)*QV>dcS%If0L0-+!>aPi6)^_`+4U(+woF?!;E8)6lKZqI`2V;spD@p+OHH3j}v4 zae>kD2A3&C$Jx~hL=v}^S*l1z)x(USw5ohA41OCZs4moKuc3!;M&oem{9ZJw7k-6j zsRYv;Bm=tt7(?(UyoTh;Xp4mp(cYip*6|sJ2hUJmUPjO2pEL!zXcSof2m)RLaJ(1U zGlJX826hbn9u4;#uM@Ct03<6&@csUC5oYuj(|W@}uQnrEwq%*8Nu5}V1JGVEP7nOOPm$}h0$;@Eizq*dbi>Mq~+bUI73x)!&Hra6<$ zwe`=9*{$hnddissnk5|F&!oZ~Y+{FIl?3%h0iq2)$1sL+AHIoU@ITQ(@L4*$g1C#@ zBq)0Dl3?ok92bJB17T#r<>7vLx%n%st@{w21U8?@(QeY4-6ZtaWNryt{K4~b_5L#>Itvdjux;Ip7KZ1 z&;K8sLH|FPN49_V`t@M4u3#PDo6gXO2+662T z*8M|i`0tda?llbA@+Wu%e36Fitr4tu_k|#d#Rp*_5ogiz1FR8#(PYDV_9Nb2--+I^ zsZ7&w2oGso!)Q|T-o|b;H?kb*wUostpfeiV;aed@6*(l>B&;L|1>7I_0sDP1yq316 zWn5bBa=L&zMe7Ccga!1u1!G+yCd1eYgj`$U6qCJoXh*VI3s0arG0GsYVA5LeXlQ8# z9?8w^U4`c$VyL!7pvZD#w6{_Bo4EHQZT|*Cls}{~?aGnC4E$tw7@av`8KXvcTrEcsuIsQ=U(PBtwd1xYJrTu(bd&_tM(bzbl43rXD;NF53o32k;4fI~^>&`A0&y9tFg6yT8jshbBSxIQ2@`38`TE8MC4CVd(z`F3gUB2{Z=(jo2D=#pd{!XL_({?W zApoO7sXZ3uC`85=mo5(bTFnZ(EVo5eseUq>>G}QAXX9Eim zR%yv(e3V*#_7C|aYp0|e*Bug87~dPvpg=V!pq6>h%1L1*4ga!d5i}v)7PR|HI@m)Y zo+k}C6HZSkLSWH&<22|{W8_K^g~}U%@RfiFs&ova@6y*zmhl%`vI^>*$Td+)OTr;UzbQ%nNSn&lntC9YnUxr0yUQ~8?$hlG?i5<^Yx zsa>H<^}w|imV>b4e7hcjD3HN(a0j~=tnn+_d)@fopg`3q5P|nLe4TA*UJ4KT%o}EC zXh%WJF4{%QJN>xRS5P^6Ben|wHQ<^FD=JGzUnP5q0`LwSG}6P86lP>Bke_()1<|w=O ziO|FQyx9dS>8B9^V#}F6Z>Jdbh%?+In}Rk)tBD~LJDobAnhx>vGG7G(s1+jwv~%Xz z0VXaH1WtpgCqA*lrUJ!pB-x-qJ&MCdt>mGA69`nEt3SIMrVZ~Eya=LPavV=O<14%i zm?!DR=dx2kvtao_TBvrx7$5XIp?a?{pQ*H3s70$ggm1m#3|{h_&|Yq1Y&%qKh@*Vk z##pcPDHlO}!6ao?CsK_c@mRtQ7VPIbqJfWYxk}$ir$K=!6!695l=qDnRir?$q5Ja3 ziJ{~tER$NIFU3jgG&GGQxur!d8W`v7-&|rU>!D({Pa#kzL+w&B&KA>2kSzi z0Fu#Cg!C!+Vr>z#jZe|4|HYkH#T&sh*{C{ECB`a?JcOQy+jd{x|ZN=?fx28Aib zm(uiGSfXJ4tNizMsRV`~ShX9J$fcvb66{~4(bBY}n=h=icmFus(~L3gH-Q<@zV^Q* zNuNcKCBN73CLPrpqSDB^L4l-BP?t?cjm4M$y44_22jy75BY*15DnRz50`RB%5TNq9 zptkJU2$1c4h&-mHoDEa1MwnCzz(`~LA@^NvVJ)y0erqjd)s-x9ywA}3i87U@efpy) zeYQ0?W_Q1-#he~bl+iApua>!2v?9-O)_1E;OFpxpid5EUQAKI9ZPUzh;R3kVvB)wpd? z01D7ICDwru~vrsA3M4q27=VfzNcrIYhA< zL1rig{H9jj(vhhYBMqcua{bVBo4}X()wcM1nK%A7D3C@0H4gfyQO%$n^uO^=ui@2@ z3YHhxGI{Gt8_`VQ=Q7SdFXLE1+_r{3@8|9Gri2qhGWFt^uIWCzI>Mpm0{b}VXQqMZ zCk3MF0Bywx6V&4=t`-DYvyQw8(!3f-S#I?5waOIQzcw3BU||y~8Lg((#<2`^aK$V` zQjH`U6sRf%9Pd`V*^!YHtCodab19-SR;CrtxT@rAWHy~C;Pd3D{CrWW#ILrGa%Gz2 zRj0+cFCl*Y$THD3F;&;X1_i1_fedV3)||CiDb`K9$4R`QSe2Eujl@c!0E{o4 zMw}TQWNl={KTEYlwyI(6rOht6E8%dX2wx8#!zZbn=&-qq(nA**uw*fnX;Aiof%Gc={qq(hYyhD*M6Bfb1q%fHUYsjhxR3-OFD zx4KGGKJb>k5JQ${m?ls7!pqS%+b=PbpyVB7y4O1^lNdNe=)i5~tq9bOy~LNaL%Y$h z>PeR}b{Dq|n52N{>FQb8bws|BI*`!lNxM|{QKA$6( zS8#H`Dt!L1G_`c)-dVb>LL`fZu3X@hBmdVbZL~27Nv$r`f(Y%{ zFKej=HnP@^aH)=TmiwvAT!#>-z2J}OU&#>EUSb1XO=s{c<&crAk$VJ#hai9ObXtID zArO7a0aAcl7Lk$?E?1)EK&+9*=|KUXq3vQmrAYQ2OwGI@h2cd3PEn)SLRm$!eCj8RMl+7|VN(ViDl<-oZ`&%KE=0Ly8I&e4_SWUTLNJ{ zlc$DN<{(XTQ!&F=63_spnFUl*vPw%VL;8(Z4GLtVfOwC_YgeQr(~?{K_~FIdB|-sb z5tf)!Bf3ukRVpKCd-fpHXEUY)o~{+_3wSjqS*0ZClPgwLrb2rU!jGUhYg%+#g0vA? zq`D|N9p2~;k40y#H>UF~#&n%)%=~K!q7G3+)8#`@*-ZcD!Z><5cLMi5Io<4DRQNZD ztFp5A+M6sHQ?Egsr{F7X6Iu-IHRi}27`L{efZfSy@jstbz@n=OqQx6nzBGM4(-f~l zn~J40G!tF5z$PXOsOZ>$1_jDY0kv1RBAxxIj83OXwm*2#$)$2*?mt47Wh>0XRV z{BKyWDnZqZX;Sez&v9f|#;=W+m85_gof;fJa-`MDSFKjEFUpaxItdi#kzRhK3DhxI z%M|!c9QO1C$YGXz7t7Q|#Q%6zM67;-YD)(TM(7$dbw$^wV6Mbu`z#)2JLhUEc1OASvJy*GqS=U#DToDo1{H zXBzJ9d(yJXkf!N1oyR3m7QsvJW^r}=8rQ2mKe9Fyf>|r!CHW1dgVN=lMZ@9Z-nnN# z&Z+Ij9Nlb8A2d7sw_dTO5Jr}A$?0F#u4x7;dqDtAR1fiPPtmf(D=2|R;M0}@YESTv z7E{Tua)S1vUqM<^^Ilf>e?!5l7QIL5&_)@h+EH)85~x~O&Y*n!hNlumna+|@RS_Q# zbq+LWt6bG7`P5lYpgd?KH~Xc=EdPKpi(i3`A*}(hN(MZcRCer3?|t$eip~i9dy)ad zp_@?%e!}z9o<>b+po!{6c#>~4C zWfgz4_BALBC}B;~V11DCoup^roo<8ghxQn=_Xoxt{5JG{2>wMGjcUpk<<(}WOzRwq zMKQLVvdVX_*aYgFE)DdGpa3#f{iZBZcAAd?m}Hsu;nYA(tRiu5|J4`YG!!h=OT~a% z*Zx++JIJBFD!yVxu4%$*WzboqwoASj(!tRgS_PsKh>@RFu6FWEL$4RmioOJ$`#5;T zKx+9nj9L16I(*;SGNq#z&A5V{{NQ$D_WhWdtGh7nU|o?>Nt@70Sv7rx$GT#zOm$Rl zFsdCiku|0#Z3AIfQ}Q#0GAi=uRmb~`S#X&#i(ckUL-NW(80B^ZdiULJ%&tE`;d z+hF?Vhf|a~hK7Pw$;ufyh>&HZ+ptU#`}$-gj7b(0qaUBMXql_Crf z5=XgjC{i9_iusR{ZGHebT$Kkg@4APf%80nxAaaX<=NF8`Dm4~ z00V*vM(7j!ys5@{Z#8DcZ&9D;pg2jn0MadvDbHwF2dTpye@R{bly(pC!_|c)`_syuFqBX*C2npT5uP?jJRq6kuy-Lwld{M=Iefr738HJ z7AYMsuY%7fpEG9rH&CqTh$Z7%*6@7?H@?90QfU0IDFwva%;`q3+>CDbIv^>|D?Z57 z;gOz? zIB6HSKmMpOFMJvc#(zUIDWZf)pb$1-W>7%Z%L%NPGCaP$j`Hdt?pMMBAADqf^}Q zS(Hl@t!y3W2pe;5{TfcspNCe2i=Y77Gx92lXCv*}QvhqO!NFa-?azA+%GMW9x-uzS zYD?lPK*#+&_hD9t{s61iO88_qFX@P@1+Fu%N`8Ksoh)&AyVs(Lwr)x8j~3-VsE9d* zOyLxXXI;sA6+++iD@fjOPi9W>vEPEv?p`0Dy;Y3dufDl^)r&4!mHgjicwQ4U1%lb9 z;G$6Jfm32O9x6nEhJqD}u;N^pT>Aj2lTBgdPzf{wg`$86D?xw|v#SBsfz*LukkB2& z5qVUdk;9SONwNIPYyBz>N7yrk)$ZC^L0mz9Xk3}Up=s)5I= z1$EG@CyVOSX7UAhwL&lxE<&tU_@oe}@lYTI8VXicf{D>#YTL2y^X@C%xn{*>rhkCe z>Ik%FReS|~fgD)tMW6^b%<3cCDuj~HXHKzN@h8Td`5|b)oS^FrRsHT>t8wJ4-uqEH zM*MRkngTdF(;1{uzEBi!ytIn(;0`-ZF>Ahyll+^BjdjNHkWEYNOW}b7KSxo2Ip3L5 z(=kD&-@dNszj|rj6)j{V@zb3G4FxNO1AGBB&ggCwYk*crSaWbg8l;e^Y!A>|ij`IZ zYDJ(G?sZ7(k)7}YJW&aVtbx~1-j;p}AFIzmgEn#z4gAi}b&6xhjd}Kitib;#-(i_J z4QNmx9|h#IHL{i6<$oRiV!=aPR;{2#lmx7m3w}F-kKa?5!|Pa0b(B=4_tmPyrk;v*N7-%029W9YRC6m|p=Y}_{}5RL+oCGgvR zZ?VMLo8Y?(;3fFk4j}y`+;iXk@Z3feEtGxhpL8Nz9>s(zW0NW-`$n7w1u98_(@epl zlT<&crKqw_jnR2(iv{IykoQeL=1mr{eIk&q)cd7Se&{mgk862s~Eg+Jx2uuo%% zSjG1p8N!@quVVOpRDH+w5^1dIyHR*3$v-8eC#%6Y>2+R*)#`h6RP*5@@g`ORf$P}z z{TF^#uYp#}c!>{S`hGPETQdNs3HIaa`faQpul2Av^aCtxUxCk8^4u6u)U0Ys%2>RCW_)Pf58>iQG`KrjVrZVmeVk9fi-tT71+p zhv#XdV1c|8C>!PA`1aCVvSf&mnvKGGCf`jqDa(42Y zJX$2J1U{XHb{O;c2H1qDn<$MlfihM%fD)+orp0BKvIRh^I#+mHplMC{h;2eC9Jmj8 z!r;R&z-Ykqqkt@vvcR{)ujk!DJ+JiYJvj(~2Xue@33&B<#2Fok4g%NlDCL0ZBqGXD z-bNZThXUf==D8?U3uqIX(wbyue+XU6vZ_ONm+$~NIh~I&Vj97@c$h{%w3W{1Ij-lu z`{7>XI8zfERi!9be*8cI^`>-)BT&GRAG7K=Kvzlw0wOKY4At2=;-osHI>*8-pk;n@~nZJH3uAp}|5u zK@4OhG~oO59%0qlMN}*8)D0klsJiyh|D-XGokuvV8+I?HTJdBMxyF6r6p(ybgSdi6 z&-oc_@N%z>+cv=`SKk8+dLKIr9%l5eL#3^yzVO^qVl-GLg&d8CdZB>YgqV}xmHu-4m3PZcIEZ5Pil0F=1XjVIL_}YH zbZZqmI=_f6;iftOKOK#d;&aC9iUP=3#R@A`9RHlU+8nf)f>R#x?np0@eVP}XjFb{q zT**J>MfVxA;Kg43IAz=^SH0hWCs4eaSMptocd_{uj9|zJ!iRcIFaSEAa?GO~Ne0Z8 zgkwTfMEKdwK@q6aUa<;LXBcHi z7S)2mueS;4(#DAXqT4-_v8^Y4Q+@F}c8n1}omKzOsn;dE-d}J|Rf(W!zOgNo<5B-0 zVIO~2jv@H#!4+6@Z^gQC9p!MHu8Qj4J8RYiQ;1rrmXT+xQkq64rBWbrCj79OZ5@7W z%#vTGD&dEuHOs$0cnjN#ycE7Jb|4u*QtZ}%1_f$H0c5p!ya@w_ot2^gEmD3(61Vy> z)emF@_u+Kp`ex{g648Q1;^KXDp6V2Rfs&C{X)j%8v*pVyCj2l;NjCvVD+NSf(iC}{ zq)?$HH@EA-d(Z!(V1#L|F*-J9aV#p4U?iXEqB!lxq*y_lBAG6jYVGeuf0Tlez5EOm zhmQPEMp4d|;FogFACU(2XlWywr70W<6b{2QWZhYgm0Y5NpsBJuo0kKa58WgA%1=I)IeHu})ERPqD zNF;Gvi76DHcm2nVtfwP+ose)PC8_cP5h_8jr0m;@|=vIMm- ztyEUuC3WmrAcuGJi{-2&P+F8bI)|x1@U)%qOpt&o7s|uP0ThK3e!Uh^E_FPkbUZ7W zA~_QUE65}6e)so`dHzk*GnRk^mVmkhDMs*O;v@>wT_{dha_=jLt|J{%2g)E}L<+W) zL;=qey(pIdjWz2PY329hYoW?xzv8Vlw9`!eHfH1BbfZ9=`bCkB9yaDe+>P{R7Yx2ihzbE!7I6;W&B*^Mj2mls#ER8I$6QvWDh(%D8-|&J(zH}jRxF?Kn&*Kt z8ojklq~$R!A5|_XR?tS3TCFO3WEJV`U<(EmsBRR9^v*_MUif`uc72xmhK7|s z+fDfu3vOEQ!bjlwi%7-iTlXtodRWRNpB&9xUJ+C}BDu&o25|>Sm8;hobHSJC5TslQit2?u0i>OhFSwn`+=l<~`IS|! zgsPawPBZGE5D?=LUW(N$grjA?V#-}ep4AAea?$C_s&@+iB25R{VihCFiD>;&b}*Ip zb5FTa+BQKyJvoR!{vI}*{VWOx9a7yYSIywP8hSvJ3-0rjD=DBVi!0O6PApfygfgku zC{va}_;HXxxncll21(;i6v^#hC7cs2(8DQY3tw)`wLCBXD2f`&Rb-7zq47JdD1dU+ zyq2vT7*uS=a%<~1Pyq4Gn)lwqTUw1;D8PQz1!d^2JUOe0XS?!5wVw-)8xpTD zd7su{0!*dj+Gb&amAgx+?l?JU+OS}y5Qixe!{~dA4&|KxVjn9;^Q5e=wOcDM8|sx>C-tCr3mH0!TQTEH8W0ru7k@N1>96tSz5HW(To9=OBO59LQ0hy znd%|{dKDTJcs2?NbUpVM_@vxGa&#!7x8oIU_3#+V#7V3{Q4|te$DuQ*VnCIp4g3aC zt{%dn9e%WqRZ@<8e$}+#rfw8dMtKp+oZK6U+l+vwm0&;;Il)xpgD7m5@=RK;6L$CG zN3mvo0;|^F!Ec!66Xer?X-0uo+SCwr{01zS-$9JIfvAe%^UtB%nKHbNI!2i;W<_aQ zQ!Si8u5tSb1=Dk%?8>khJlev=fJ4vrSNHBY&?ZulaEYY6Ebt+Sl%n!1nPpEVVIZPu zSG&lJL7P^J3v9Ofmmb%CZTw52fTI)j8ea~d!nakT+i9*~#bHzkE`O!5T@W9-um3p% zu9y1NBjznf1M-uzKYp6(gtz?#{?32MTROSu1^m*{*;Q<%HX3kNqqII)GCPC-Sy|-R zrw(vNfM_7aY7d??<;qz|6>EBHY%9~oC?xZwkfia5>h)nPl%l=0;`jp5)4Z6 zicdU-RivBu)K>$h5d|2KPW0ivcLQzWW@HG8xKq+y9rxWs9bX9#_3#aza6YKq<%KEY zDA-v2k10)%?^Y>7VUi0*giDKuG>BDuw0!K}EAynho03sp)T$pqW#bZEtpY~F)S>Wh z!);3k(Pui8<^N~zJ-{r>jylhHKE2N@Z@Rj=yQ-_JUA0%^9o&K<%U_0kBjy zMF@d#PjjB`ZCri7#q}tYXDPmS7sz8$co?xoMaS5woC8JT8QicU$HF=7q|y^FZ4iV}(RSXHu@m82DYVPNsZT zhro~&UPW^CB$BHqymCr+*omon_B&uqkle(&`HqyPAp?>Iov9f^BK6=Yn5%pF&7g2n zIR8HL3T=Vu$^oE=B)i~KMZw3hV29=IpTK74SIJ9d`(-G<;CbpLCgEc+ro4+GK`p|l zZig)b0ZBn&Vp8DK!#BAPQ15`g`S*!u!Ka7me@PG2$$DRA5WN;UN%A24ad*a}7^x_i z?hA~P@~j|lMZX<_8@o&#IcT0vqLB2fY}$4^0HCxDG+g+g4K-2)Itk_G;KXgjU}>Pi zkjNKNp2<~%Qe(OKd%ab3*8!L)CdJfF;tBIgurDC;rG2!a&(v86p+T|`_^m-Q&BQbD zJoocV+@E0!^yj^{#b`$qqhn`vEXHeDkR&C`KwH9K_=u+!U6QmLC@Ni6yE^<-+h9J| zQU$J7Y6&W=97PdemevTdY?Fp9l{}s z^xNQYkZ;qkVRLmCul4O*I8_Q5twXY(WjS3YKc!6_^k|iEWIL1r0l|%+lTtE+ z+~=3g1K#J`?=S5Z4U>tBHrlvFv2bwn!fn7{A(Ad20cuBGv;UX9BRvvYwk&q1j1=l|(&fPwPCL+Wo_XAFJET+vTOp**F8w2se1Gxs3 z7%Lc@(p`b&7U0vOHKRvRT}jn=?60uPd+)7LbCakA!M}N=TW+;PeHt>LE|Gmy3cPuU z_B8GGaZ5lDA>uv>gYzs5j?okqN9}<#ENqd028?rXA9|90eGByYFppw)`T>?$HSr!W`38R4Mc%w9zqE%x%toSZ!{Muyq-%NS`MP?RXzz=|5yu z#2|EoH~?;NZwx?(Uke6_o0t4{4*o4WdfowoJ;eK2=oYIMIU1GWseF5Eg8n)DHkal| zyB%ZYGm=Xrb*1sjcwP%tv|BI(<8c$%!o1|EQ*5Q^q@o77(0|5aIW>#hl>)E8PlT+h-7QTX03lgiw zLyRrTV8s9|QUVz2Zg5Xbj!(YU$#N)IB$wKk9;A4I7a`_-zFfpa~vOpMk`>o|^oJ%Swo3NAA zf9C48G5oDiTHy`&sjd}MA{>t}xw^X_RnPhkcy$#ENV;W5RY)>t(I*9PCL3k1OpH^v zh@9s+Pd~Ujad|HCY)V(~GA`;~R6fd^JapiqUF1-h6- zQSNan!xf3?nL2a1ds91i@+6LDuTFfI#=k3LS1$QWRT(_1Tm_Z89e@CUaONY7xh)G5 z^)DT)1DBJ~-3Cwvu7+AZ=CAg7`~5Zq8U+HP1`t=NKIQ5nAl8nOR#-<7;Dr!8>&~Ec zKXr$BFTh-Ny&LB0m%Z4IxzeMb=KP0nGXQgyl4^w2dXSYD`_{iA`bz={+&e#^t5*;y zxRW4K^1LK{89xF~^L-!0XV-s%7Kyo1ekxm&E)(K)U!tHRBFeIjDWlwSq6F?MWJG`Z zdvV)&(>ThC0aues5^PGWc%JNMNQ(kfC^BfG4Z~$98ht@nU=BfNw z0f)*dD}YOIRnpH(P^&DKFLt(BJ zrl}Lne~T4oz|C1PQs6F(lHxf$bMZ;9dk{*W{j7sex9z>O&1z)O?$Wy$G=`Mm7HQ^! z`HHqNKE;H=lsSeBrfgv^M$kt=9znj#%i0n^rP4(RVdfHu1#)efK)qA@p_xt8xU?f0 zw*gz1!CHVRb{X47?%>Npc-2}xUnP3#RJTYiH_ziXneW{@wGr?P@P z^&A90g8Rg?UrbP($4~Q8(ZrnT4s#YwrkNOE%DAE}S?*CL517FHKVy-5f01}n*(?j^ zU{KEeAFKx1{aC!d?Bizs6r+Vrr4AQ9`0vdQ{S)esgBu z%SIp$V6P4n?71RZ2(`j|b?AZ-Lo%jCkz4#oa;7{)enodm$#7}jd~S?Dr503$s$hUu zjZwSBT2MC;KiP~r$=l+Wt?+akFjxVYnFs?n4YHdNzKGFX;-=woIAA7|C9^DXmJseB zzm2?8S+_FRNDcz;3P>-OxM}!o`EDA5Y#QyZeo@evcFpx)IQtF2kd9hmGR9<6u;LUd zjSS3FMt9kQFzJZUDcyo(Na-~Qxormzt_}_WqzJsQRV!xCbLSJ|A=1ev2;7xK80ceF!r) z^*Q3P;3#6Z+U2S)J3*J+#Fs~d@;)cU=j(`WETt+i-S4vISp<@i#(~OU&jrs z>SlE3ZBeHBH3%&Dob4HR$AG0^!gNV3*$VU3ma}L(Qt70R55X8y`h^8xkQw*Vyh~n( z{wN}vVS`RqrVRn2hF(hT8Ag)K+4}@s^4e;HgRx!w%$BQl1)avKeO*VR5beGxSRfOnAqA*>ps3qI7 z+>wYik8y_>L+e~z#ShX1>Fdpa$&Z$fu+oN>dq3uTo~LkEu8$}Iqa~H@Yac>u_^*-f z?I8}ooj)zba}`&us|@6!EL@R1=pJ0Ghw{^X;B*%bwv*sDj8r!6F;Kz8>e7*>)@oE) z`|V^K0DyKi9u%1|B5=p0N97#(dsfSD zT`yq`-~ACH@vM%@8Tl3JfJ!9x0C{3Nr8B1*9iakz7gH3dIxwR3Buz-5CSah(#Xzx2 zmvOBoZl~%Xp@@f%i##=aQ(Yau9{;#L@^MuwZC2@GO3i zEG~HQ*ZfOaYPu>ONTSp(r_Pf^6)DtYixtM&kVat>CBtz^U=?Iz7ciTy{od>CzuOSF z1p-1DUBWe8!MM>6(MfPlDoELed1^^9VnmW!QpWm!Tk}WdtJ2_9}g@jJTT= zCCR)j_d>35n^cFDij^cl^hGvof}zg()fu;LD=s(J4?$-@CvR^F)=Cgs>74)qmL?B^ zyxkz}vu1}s?VU*&tcq7kR?$0u6l~OC&lT0pkE|*NK_JnjBDm;1lZCcx7pGvJCSaa) zmqbcen#hhA5YeUak}36e2p}LJ1_Lj6x5e*4bJR4p>pPDM+H&8Nb1D(tqb)FDs34;) z5TUsAl!{DH+917q!5{xq@N>y`eG4vq(B_03(kYz_x$9q8uK-lceHu%i|8+rfM#V{;XZcZ3ZhqpZEX-TH;8S>M~ZzW6#n4k5Nbb7PMN%Ts4GX zMLql^#<*`Gzt@o&1b>~*siDM`lLojD;E(Qs>Vj{@S(qlN@VI-76qBY@c)`Tt;`><# zc?5Z`T;djc3(*SNOqcPIs9_} zRjw`>rU!RD``oIEP?ih9|K;8<{jv~cvmbnTEmYi9lb5=SS$B7uubq_%#+-If>MjYH zCJHtx6mz=3MeXt=Ebf%kFPEW%pVM8svqq)zRKeuq3ScJEaZ?;|RLqVk5o7#5%4*|* zZ-TYn1&mlQ7W7WRedO*J<=$=o*@i$PK|nYPDho$b56t4b6?~UOHNf5FbrnrZS!ece= zsqkXhi6CQkesPGxr02N}Ag<?3rx=4PT-5edX-v^_9`b7j^ZoT_m>)&qq?!dYZY=g309wiwq`AtP4 zbXF7zO)Qq=N_kg2X>mP6s3K7hCSum2QSWj%?6*fSYM72LY3J6%y<56@i@zxCjMbX5 z4ic}HZ2Nuq5TLt?nF!LXv?cCD;<8%TWH3$9VbG3aCrlaE-bokVq$R9x6f| zY9N0%A!y;nCEHW%qufz)QuPT84&B`!euM{?Z3asmy}0I;b}2-VXl0%~b*1Oak%=5yZyVxOlRt)9c{KvxZA7YI90-Q|Z9Fi)sEW|0)kBL!gKNnh{nR`M$z zauxhsL%T^LB=$j=U&_z~41t)YY;MkS*?G%mGnUI|VVLHufIi$YRQ{??Sp?}l=1X~& zHoo+^vg&&&m0u7_pM!V0m%d+K-qPRnSZTrvpG(ULp2cK(dFrN1@POBX`O>`^rmW2p zFlEVPlckzEV9YSg_JJ0pt*|27(QAf)`i)HJ2d^jnzd^SyeU9G zqtTdm#aIwT6Ff^Ysc$Z0?^%zFFM zyC_vxBZ5xzZ)zWPt44YiG)MzRFwjbR-oKDY6;bVh^Delk`s2s--q!7}Cws43_Kln| zKo$i{>|&Nd-z&~`Ip#^y6hDrq>-VzL@Foa=8qsx3(4p(JPaUmU?CM95Abo*y9v}g$ zi?D)l1PN7PIBA*F_|Jiv(pF&?f+}*?w&8_P^0@GB(;qw{t4UoGKo$v8CYyzMO2a%& za!nw067i=ke!GJEEq&$>95E0<5UZwm-rbuFJVr*q(7;1YuNnp+ zc-B3*7Qd->et8S6Q7qx07dslL!u&)+&pd=EFL*sowPE*?(t;D^%R8pFC` zu_mq-i^W@z%r;px7ULe{og}v&qgINWU$=__0TjZNA5Bu(HEoJI_i zDy8vzm*}Iz(H_Z5dnq!A;EP~6i4$=(0Nk@|qjZTP6PC0u_yOJpk1kSxo9;(l{~+e5 zF#m;$1`m~HP;Pn3UwOmS6b89?jA6v;ik1CRErCqbd9tmF)0yhHr9I$h&U?`i;p)x* z52uYkwn{uddg(1-u4Z3lr{M3C{T+No&l^@y6JV&)&*GGjyOU4_%$3ThSGUwq|6mf4 zv`G`le0tL6=SE5*5=0U%6s4kVK zTBp)g8*Lo&F7G)>Z95{<*2&d{JAw|Ix1MMQH=Q|4L}rLP%>9OA+)|_!1E(M}r$RAK zJ8{g)KMyz6YOkt-bv~cBsj160Iic~vP+>yr=sajGEnPH6jHA+Rhe{wI{7}d<2IkYV zHaC0I(&;H+=`A3Fyu5hC_2Ub1mf*TuL4b>ln<%{~taW`y2CfADEHeHq0!Xdjd7J|l zz>)%oZ5LhNC%7{WFdhnEi!e@004+C_lh^Ye`BnM4qSRFrM>N5ig5Uw$4ZthmDFrxM zs#M?-6JUgeLiSa`2UK`2cayxZ7&r1;cfp(mws1%r8c zwlH=(kY07d#I@(gtgmp^?ux!@U3SAUg<>9aMRw9T*!!zPWv-r} ze#v;;y1MSNrsnnyhLNkpminVUl$vg4X4IyquR4|X;uwHYfb}SXsaQvH%{x34jt6)? zU+iQtz#;(ro+@^*80Y}EyKL4i3Sx5Z;(BQYyq_Znu`+WoOj>ey648J$_wHhZyeZhj z8DV}Y_Lq)>Qb21g#1jc?Yww1E(b!PkJzOh|7rgQEA!iz^5;XGECb3-}BgYT&5Q7+p zX=@T*^0kv^v4BzRBNO-&YAFs|Hzy73=0Jzd5ugB(m6z)FYG4L zLtMaN-EvmRuk^L5ili#@Qmf^!Kt4r5SF67C+Hwn#XZ0ud6Y?t}IMz9cK+Vrj+4%S+ z%VcJpn12xoyvlkc9$Ozr62$^&>h=OPwvoain=BrNf$FdeY{|a@$sH-QT9UlAwiTna zI8g+-2YHuH7k;K?-ZY7{w6yWj#_qu0Z zZU-O`FjZPuOif;LLv&%{27AQ-h@lb`Zxnm%O5yc(zR+PW6*_GYhDwV9Mz4#baiMr# zFL}@WbiadM_3nzbIyrD3YxvXF(G4@gu|BupQVp3dOB#$^uO<5EzhOLN&|{BHjKe;G zr@WU`x`~C1gJPOJ;+=0OxRw0zy&`YK&lam43DzV|EC&&cM>U(jdkv zeaWKt|EMLu`6sc#!k^F7pk@C1Pjlql)3l@AtAA;l8lN?JXjk6Y_!XO-8HT9}I(e~j zQY1(zrL`t3pA8otvD1Y&P0bxo&b9;&ycP zVCMl01B$zDs_G*IlkWWdtj$hO`UE_HUFG>|@=IW>-qFbzswn_}`c$m%43f0??1;Sy z|8S4Q{?3{i3R%>vQvU>lS>^V0AGX%E9@?PI^ehegJ$>tp8q|_NAbubjZM~9ajTH#icXV{v(617 zm3ar*nhz0|dpSYg)^kX)dVUHf>ldkp$f94L()59(ZR|^CFTas^NBQcJ&C+O9J*zn8 z{rnKN5~q;7t)?DCQdOMzoTYy3Rg3L9yiio`g-^m{z0)G?kCRrDW32(Uhk>5W&f3Vx z8OvrMYtA;r8^lsbu!iDq+R5*p&%e!{&L6f{QEjyHwhc1qK_*zY)>@z1(rb5BYg-2r ztPY>isV%?NQzN&-czJGa2IF#eKFY?#O~k9GDCdh-*Uc^M)_&CE z5zHr*afaR0l6Ro_0VlmVt2EN~b3Gxj z!35VGd1$6`pUmCyYz28HJM|0u&2^a zsiNc~V!9M}wLsZrOR$1Ggy0ZI^!0WZb!@*k_)u~O$uImi%T)YlMIYjQHOya0cBPLL zop~NJ<{@^VCAE`=$t_q~mB!PvChYW8i*(&#k^YCRaQSaY%)5lttzoQUm{Rc_H9Y(( zD|QYGE*ZR9D<+UfO|U+EGWRa~O#UEGF(gwSJJpC2i{PkTNgbV%UI{nr6J}>7F>Idk z;T2vPs(%6rgX&-Jpe2czhZ{2!_O8rV?eRE7-s!4E;9)GlukP+c*4DJSy(_sv z`3vu5us%F|#!0R`#wyCPBF_jg`&9OQ_IHKD*tSU(C26cCxvde@cS%%&l3r~_5G-e= zCqfx7!bvefViZ?XbE;>8-qEI(91E_g3{uDRm_3xcU=PJ!v;)zX-K-)be93ne1Sh5I z=sbX`e89a6{g1Q~_^^r$JY=cw{WTWJJ6UN{J~S)+>Hlo`Fa8J&*AvX4!a6?$4Azbb z?6JHyas zo*Rj&3R!YA93 zv9a@RcaGO+<;!+zrj^tA*V&)vUymVfir&r#!uHw=u44{@qa;@?t>LfK1+O%cs%hC) z)hkd&XIHOd66;mO@_a?E#cW-h8n^Gte;)PW7mx@@b%k$@fP{* z7yt~{M@L^{?vhQ9C$pW02>kc#_t;laeRqNVVy(7fP#<=(*&~5kY+X7NVgucHnPI3UGK%2&s*%#|7giKeGiM}X3M?yWy^iypF)CJI6E{eq45EOB?;CcBv>v& zi1i7nS+{34>I@j~aE;v80M&wu?PpN2{VVz!m74;CKo#-0>skmAc_=S26hWAM1qYws z%~n+yI(CdK+G|mc;>MA9CB=Z1AZ3uY?0adQ$usPrEU~l7Urkx&#bz{!w`&{nb_80kkt=BPDtEF!I_eB_mCN@d5qDHHC(6|r|XE9=* zm)e|rEn`J_rBDI3wRchV@M*F}afC9i&Cc0@`OEgc#3yWb+Iae zwac|FJQ&g*9VH{_m5z78H<)K?vqA|0gSE?75xV72Epy!pWo*AHfq*c>$2V=v{kC>Plh1{g>=JcuKO( zT8x!g)@u5();d<0UW6ScAjvlEx~M?_!HiQ0in&^bn-x}dt?1fvo@#>m_9v>f;@0}j zO?=>ACNuAtM$hdAhO}yHvBiEN^FjMy{26;t(yV9;ZV7HU)l3Jf6XvAbAcz^rqY^xa z4c(ZN;A+#xNU@1A@BwzdwzEYCC!t#b|JUdZNqpqKpnf-IizifT| z_gRWvh~de!zUN~ScyrN4Z%nNzFu#8OsJ#BqF&W}jAI3`XTdhseW&8YZgyzY?n)?(}SMuzYcvuSIYy=Fg{_%k*#jDq`~ot9wO ziG5Z_#}2Bsf!HAlRwl*~f8&-oC>5b#y)D5C2MK)sLAyDVJDFQCeRA{P(*E5*36`3r z4o-8!)=HJhUBzzdI*ifZ^Bb{e?#tW>hdz#bhIe_-xBOKDNGoH$mwyd+4gZq4p5L)U zyhmCk+bGtLo(pZoU5UBkwUc6zPOc+lATQSi8zPNT*%byg1K(F}XD-jHO>Qn%anCbf zvgm{V+EV|^-+Ej%A962#(z1X3WBlI1FELr=2QekGGm*4{!s*30y8^TJ$4D#A7rUL; z@J3~>M9?JR(eAoL5;YEteFRqw>g#~m1^4nHvUW`Mk8G~$sTNcXRBthfl3s<~2IcQD zR#~>kk2-aqhj)^q>g&ouc+d@geKdbR8RRg4|E$e{ZWEJ(){naoUap*Q3%r#Sa(v>N zW3D1BU}SIjLg6v{oy;5Yza(43+apMNrJaM}=`17h$!*4Ax@7UZqqaIBK`Obe75gF4};E z>-@LCEB!Ne6T(211`Qlh_XRf%d$2c$Xv{cL^nOR+< zMPk4S|5plY(huNMWFSX4BEpF*{aQtk9?(#w_TQ9u6A_%4xDLZG1*|+@K9e{?{Lk!; z8pE43W|j@94iv$afvaajqE23Hw;0BH1u-Fc{6@*ARlS3Tg4(Fvk6}-SYDeh~E|0k? zuSJ5iSgDWZjKwa*t|Q4LXEn>s2su(BrWWu)iF_P5}L|#7XIY$Wi{dZvZ@51$|*_tH8;08$#j_Y zUaxGGo1U9R6^OZs0=UrwHhemN6xSR(><5y6V9j{dm-m{f$*XSq>gue5$s%9lFnh!C zY5a+wwCIjQR=D=E<(~X$rcksnVxmfDoU2RlXDbc0pw10*_va3QH;!Z!yDFSd@*|i} zg8Rx9-b4m#r3%rQq9k0$V6c9j&42H}9~R7(YYg8&X2+0S{0*HAL4%p}tMY-hylnE# zAWh@Q6$iit;4P20Nxxm^@<$AomdO(Ru2vNWVWjnSmVe=78cf>c!?} zt@`Q*muhOdej3#md!boWq>R5L4DC3lRs2ov&5QuKA8E`*#`^8@Vz@El@Cl>#b|w*m zH*UP|Vf@viV**KNJ%Z{g7bS8j5q7M7amQrEFjAL0hl&<@w9)WVpZxX@S?s_GRDbgp+jpNu`wv;>j~>Rv z=B=P8f`-*+hSDl-Kd%b``cyF25q(LdRZ#>#3$QJJK4~xpORW|nAqLeV_s0J;>wTQY zi72a4GNj^HBhjZC1wV&UmTxu9))Q|_q97F;YAwnqG-9+7r zWsN-Lu{Uv;2sA8l)Q?!cdr_T*FV$JAXnhm$mCZ?&U$4+2L zt4eWrIdc`XP57@v=SSH@_2JBW?MG4{hJK0>N0K#t3AH}QesLIe*)SK}3Qd;z^Pj*? z!w)$J(uK>|#C`=~L(8LlTZ6e0$aSgkI)I;CZ<8S4`1CY^DUDMB2Jfn)vkk~#sg2lI z#o@}Qe%tJ|{W$gf2@nIla~gZCmvHkSM2R96HSDLt4pLvtoum9Nk8=$>|4Q=<1_TUN zpd!?uo}9XhkD)_ltt1jO%DqMXO>fF}R zRTq`ndX*BqGWf`un+=?ANWn5zKs z*01MkF*HnDu}%6KOxE2{nk2_%d6TSX1GiebO5ckSl-f}aKlQ8)APQwqUS$-hMUA9a zso0E-atEu;|=W-+3&P(OMV0mKrS678$F@4r8PK z`*>?d&#ZQeE=PBI(kpp>uS=jHA!8n$`W!~{F)>>Gyl^73x|FzXrmB)8So9Wjo(UJp z61$j&3sH_GIS9dAH-{=Bz+f?`b2$!c#x9P7U9dTM(1tnYW(S9=HrlFPgtmoMtbBtl z4vs3CT}2WxCV80A8E1E;&ZwT6sU0%Pm(x>88I-rek_oU59j-}3DvvO|%!9EE*&=7} z4S-8Hd*pn|N+u`Q@KyiFU^;^@stZV2$I18Bh@G7Wt)snvfmGfaqx?7795$kU80Qf3 zi>QBgVAI%W;~=PGa2MXRq4o<@$xHAkh*Y<`4t<~H(t+>cGgqp%@|8=5uqALBci#Dj znrA!)wHh#Kk%85reZJ6dM{vM=Z|oVHhf$iFyKJqk-7J_^W_VXEON^HDwZ+XnuNu>h zwLD`aTV!9a8y|IK08XlY)m%-8`r3M!+()iMz^6Y{5 zSo*RK&wl=5v}R$nw0h8X%q6=6^Jlru>5^7!EMWUaSUA)FVCvq}8WwH3#Zn|WFX;qcdQQ0ti+-F%7$ zEQ#>MNp18_FT)Q@SeNj#uQ5l7LGu{BYvyrKsWHE*k^^{GJPzH`m%M7o6@na!$+4LC zv8tRaP2{9lz@h*$^z`m!LZ5Xzn6q&HC{AN#DC9fPTqCw{JWf)>7sxWs5{ z0fWx0?YEmal1xA>B$;TW;Cw;h|E3DFtf;5#{i&l_M=EiqOeNmjD$o%n$R=GFGu zdUajxyULrHnsCg!H`Z2*S>1b?3%0~Ew6TxuJ$0TpZYs}DFrJzowz1K(^fx=U?!#mD z!IE{4P?MoLc?Ex5&m)CM&vNp>DVVHdPIXu=AxM@kMHD2nm+fhz*!(y(~1~KLB{5^?U`7Ol*))&2q{3pu6X87%rW!S5Yg~NGEoW;ku&0_VglZ!a3IM zc*kidyAw^A3G9>Pb-LQgvr@h${@lNCg(RVAssnx|5PHBhfYKIzEmuqFI zw3Aj+(ORTep1qzN0%LdR+aShem|rJ#qgtqilBChqj^~x2B-TcUe_vCaJQ&NIBkLr)lam*{TyqiI@eqQVZpx@lxL#WYHzjpIeJDJRT~&g`tD`%D zckM7Qw@D4kU;&&AJNm@PEC8k4CT&DKzk}3=3LL&{nHe5qnRFC_9J0!pYQPzhXJUYTDL0M-7Sxq;)xftFw!OFReU!nK z#uv*l4A*s+rRGW{9UeLjfl9H0zn41`M35gPtQ8bb5}{P8ou*xfottJAmWE8UQu92` z3>v>yE*tYLndFdZ>RN8ws;yN(bhYJNhOc2TCCXD<WIe}u~)YszOiD*C@QfA!t3VHdH%ujFim zV@a71I3+35+dv@120>bLaFHPk#a%vWm1@`5XBRNW$r0cnDnRiD;-Osv!h8MT)h zg|8H6t#_)kJhE@JUn`ZXajkpU<3ZDcyuEYCn42P?&KB3n8V-l_wX&f4$yJ0L-_Fn9 z#0C^RC;teAXox`$ZO=P6ld*?d+V>Ml1DYi*zPVy^IaAro)>1u6edLejG+8p)x!o8e`Jv z#DQIB=N?BK^7#y_{hJVsYYe_LXQ!P;;&qH~V(!)t0dY>T>u8o$W0IqX_C?=duN2zR z=ZnE=j35<(+!cg9k*NK0jF{%&*#ZoeY|-*L+D*R(FszjbtIj&4S!t&u%Syi`IV}&7(Hii?M3lbwH&}FE756R>`(XeW>wW z?iepG`$}VNJY`?Ah5#dd6{KLtj-y$d3!F8pTGq#E>%U4my#{Ym$4= z*Us9`N2At#1k58FuW#1R%*7*q_YsW|Bw5ekW+8eGChM;C6EDQjBBo$1*f!2I@3U^a zxj!DeZoiZ5vz_GG2xCE2)p(8R=%fu+mj_^)M75T2{?i=s4qlga7h~Ac-oZ}bO*pTT zK9t05UM^PZ!dw+W*{JoQ1xUc+%X}l%BzH3MmR}ZlTqKQ!3@#pqhF&BOmM`Sfx*r?F zJ2=KylB&S_i4d|p5?_Ad4H*+W69BanFv(894YaK)7aR@3U@?j1VX88h%B}a;Z#*qk zG*>jwSn4?^@ZrYS#6!dU_Fb}O2o_y6;Qwmz@Z7H z$w{xIw%xcXI#>bge8GAhS)(J5=dn6DP#Cwj^?%KplQ=ZMjl}{;%Y>7gP_i>NboGqw zctlJRG62xF(tKQTr0c$ioqyP+41HxBo1Sad)5%5=Wmv{yLwU$;NjJMZZ_ba}k;td* z^@RhRMc9SvTnfVgUtDRM_DAC(wt-G|)kkwINP?rG7ROEbAkZciNpd%ZxuTjKscDN1=(^&KX6B6S~V$s|&*GF63QI+|e0CNSbZ zkh{RdGH3r#Y~?^-8M|-JNLHP+N)L|p2n)tCZp~&(nX9mJUYd{UAH!(ZV-5qVxHL{Z zeuZ7{KEr~p_*QefVLqbf8PA*W{J=eW{W zV8=e}dt)~_LI(kCqJxDln{wQ^C9s*+d8knn~bO9b-YUjg9Oa`JhKJu#PwY~z$h2SYwVRP#a z+gUtkZ;PMe;G8&8ib~rUR4Sai7zH_}jYwS*QV^a@0bRqIAyPGbt-?RFG$gdc<#EPd@h%qtE?KZo?6UA5p(A_0R{CeCPs z$K2esr3;sB_Zz5V)W@~@R5^Sg)RZHQRC6VzO6^A%dJoLiI^OGRDlgo`8!y3%X- z(Kmr-?JBdlC7J5(wrSHzViYB{biK>_g?8~=&mgv_z+}>iW5aLjdCi)Vtr)g@3}q$d zM!;XWzbGVzZSU);vnCW6Bd!&93+rxZ)YCPk}%9{i*}*XbekwrFw@o zo-1kR6p}0Xyee0kUrFAJaW&@hb~N@Hs$`TkY?f%lgZm<-Ta3BdQxRdr#0d7YCqUh1 z@^h|ER*QYRyr-SuhsBWj$-%dF;k(&E19^QS*?`Ac0mF>MZu+1vZ;9bPeZBs|-pP#;NCt|PK$8ryaGofe@ zNwvWQ5u7Pj3SNEo23G0Y@SdgekWstZSdU3$c}-PLE4#eHG|X3?6EGxh2b4b%ZiYl4 zM%mX<9sP9YIoro5}usL{eyRDn_Zsu>3#M7Lqq zWWKTEbrHMvgPaS)IsD~|-Db>0&DU~pOK#<4qTYu}GUx+`uXllXc4 zfDHgEh~{RFr2u2@jov_dMSnW;&Gl2S90P_1#;UWs&wBfIIzk)xvJ$ZkuDT8^h;5o( zIUA)K)WqD{)*hN+z9t7Th~uM!mdEL7y(f#6`_X$VEE<8K|JJ7K_J*D-n1?WNR>)k5 zKlAWoJj96>&6l zB>H80bMz*L%(aaOzj|fgR0kP`cXVn`emFrmBsUB7@{JbMQsX;`n!FOj`PAwxPpg4eIg%L9U|GtXLxX#6;pU>F7Yd5Gc2 zu!B~+0_)9uD1WIn!<6K@W{|IT=&Bu(Q0bU2jT5QQ1y7Y*@3sY9LP?ko=Ijw%dc^3) zL{q0b606+dD*PHBxnk{ya@K^CX?BZ%3gO^ZP{)ez5q9n6@l)y0l0QLpVjMwZ45Vs9 zV!)GHRhzy+)0N3!`BY9fsr&M5UyuXM{J0P3pminj79byd+rDoa1mv~>JC^vqOO~H% zW;Sd=J$Sd>xN(vb-TNTcCkg!co|mnY zwn`OJDMVd+rbaDYylVR&B(@Zu+C@e6j%y1Mq4##r-N zxRY{@G4DnS_KJOX;u-r;<}KEV)S`0s(k}}9NMLWMO%REsyx-p{VGaw2mN!oAaq4pJ z^<+X+f3+{nqkL4~bb6j0fny7d)bjS#vrWs(5|cfF%J{L^CF_bj=ecHR9w^f_gSTsN znvFMkB*K07?`0gY8i-@Rta;b@F{H$WL8ga*(^+1VuzuqdRdavmqh`l{7YWnu#f*#8 z>!qK8zxoa48A($|7Qe0cBAeZJ6y%LbhmUtW57YSwP2dQB3mRDH>75n4Iy(^U)64cZ_z zhjwp8v6qPAJTQh4v?fNKpBDNGT|LEE)kK(KLaC+xOMp_v5B0d1s}ZCF|7zf*9cr7i zxjb#IaFwk#%%h^47{Qt76U-y+Fzg$#1)T=SzAM#>_v;f8yZQ7@n3`6Mko&<;ad&G6 z)muzSds{aWl7qHq>N$IVe87G`cc1NG!XWFigYwdOlsfb~z$+y#eJ(q*Dk7?UF({s+ z&^lO-l+-E4*pMA!m~GQyBiN{-wrpll>99z!SM%6jy=KkuJ_TH>w_klPq*)Zx%x276;dq6 z5cze9P`_ipP8|@q4E~X4n9g3}HAB3g#f{DfRH!#|=#^6czk&x#t1SJI8+7f8TLy8T zJg19^3fk%5ra!?8ziK`_Z+Y*3-o7<31T^X4WHHt|Wbs|NM!ee3#L&k?eVvv2O}xHi zs+-6`k$tz1W}>i_Pper(Xv_Q=eqY&@2*kLT~f3QfBz z*ABuD`G9KGqC@}qy&1t96O)d4uV?w90UcevZXps@Tjj?p?Oe~)moY3CF0#EEgTZ<{ zI$?K5zsh->3F}5;uj84@YBOVKVsOY>kF{EJKN<(dn8!G&2BrAH2#mq}=(1g0U|SYT zKsUK7ZWuwMoIzPQ!|jhf=E`~dEs?AWXEl)S_$9L;7%Y|-W*4}Y)m8BlJzQeYZkIs2 zwMxsuT;`D6smUcwF0yRS{BwsY>w|x}wwD6Oz1aS<)ghqCkI5vl@4Q9ZW^lBK9aP(X zc5Ge7*tiK6lkb9Gt z;qq~zIx1*&wD136`|x?Ha*!7a{fp!p4GcJ6W;5SeI(@XHxr-eJ z1<{Yi7hbIGzqc&}e0!|qomXgL);ij?+KAfp^hjCEf(U?^tk%{}?27i=@#tq*sa>*( zVscd`ON@zJFvyn`g=`vVRd=b@syU2SZIE-*uvHtC0z=4QnVmp}PeTE<%OMK0xz zVK85?f3x#>d%Wi+wzt(d6NN^x@*BK%+ERUa+i`-7eOkO$`E~oo;y;G4VE5xlols8> z<10>*!pZ{Y&Gj9G`QB~sj{T**J$4prlsNxx#d+aVD%Ii`EIn^l(3p@%_H>nJ2nO{Q zv#3Rsg7TE4O2+uwDcFg`s&~gO*VZ6@&39Mtz#xBf#==T%&_WZZ3ii(U3${CY!HP(@ zySooouo#ld6>I^IcA*i(?#W57Rw`+&ci@xMiKTW@Yeir6o7e-?T@qFucsGBNT*B2A z2CCD35Pug;m;`Et>-=FrDQ@Zj*Iu3*r=O@y1;KAGCrjA;C9@frs`Lfg2y7NWvGN#> z{lY)8U#Os@?WkKF0z!xajsu(K7=%eHj^I@oyQ5CBytX7`m1}MwPWq_E{*M3#Z}R+RG6#luUfQq*7m#!)wO7` z=I4s@3JD67t2rWp|E^?v#3s(>EuEiY=WI8yE>VY+QJmC$IfOxXj3j^;fJ+g)il+ ztCF+)D$Ks(=nIDJEI8j9{~yjjZa15Wn%P7o^NY0%Dbx?LO`94YvqINZ8#n<&BF4Lx z@gV1%QeBaERUtA!dNf=IKg2<_Kj3GvN`*v^G=Ne&8ASJEi}fcwALK$ESmF4os;;3Y zQagcLZW@Uk!idZe^a(pRiP%>G!Ae!wX0TR*%I*G6szR8oXveq}=CBQzZ$)#6zm=&g zxDsH1R1i`yXNi28$>aq)36qs(N?DG7?Hw5JqE_69mAV)YPrAclE2unlOpMeB^4cu7 z43#n_PSSw%ZL=j;s<(`gJ?YzW2v^WyRYuk1kuJt;XMDzfb>COm7?EMO;9670Y6VFG znzNyym#q+)vHjl=v3M)^s5xNco3*5pZjOL4!X6n{6A_!dL>t-Y(_96U)zZ>Q`;xXV z^R)Fun(XPqZeyC|pQl#Vn9wGO%?;ob65V-@VX0Ii+6FTv22oB<7mv$PC)Ph`EX>c% zI%)J`+4V1Xtr9$+S25cinYM3BeAszyZ)x9$uQYaABiUPTq(qTOXJK;3M_;mC-*5*G z%wg(lT5zxw5_466L8vrDSY_Bj(j%=o@?UxUk@9lT0MbBizZaF_J2@udKkyU7co;`# zn^Imbn;47w2liB%%d{W0pMxJAQ6+7P6hGrg+a@+@yXeh?KrzQYhZi2SdG!9n!zWpd z9&iUkmt*i=TJQ0Z7wzKA9{bJQJCP`}UdIqp#7_pTF^qn_8MHJ-xX)sQw4#-&qcl{? zP?e_Dvs4|O-Tf6aX3O)b^|y%h6h5yuLwY6hI08fdzV;jT13OQ$8b@_e71@N z$y)LYE3KsVNzB=QJoyppDh{)mXD=>4b~zzbIb}%WKXm!5wVsG`usMF@@O9$&m2wg< zkE4Vcn1d`*7njLOcqWd4LZ$zUNPLXFmvrOVtE@2idFYR@r+U91N9il!FiPT&K8d~i zPhqR{6h$56V)vk&REv>u93;0*8wY-y0*tu$Fg zom-5d!Ba?w3bywfn2@?b4$)Z^(r7_=%iJ^kB1Z4eVlUO%WPQCyoFAh~a6$N)!$;W2 z&?%dMG5EvW582gxhxK-Fw9Y1}oK+_+Mk;eCiVBN`(q=MO0gC91#Cm(056W8aE+2pn zQE*G}Pm-Us-r_lHMsn5Dy|V)5ivE}w!-?}H+xND8WGBX_n!rGk+XhKr7J>#Bb&Txi zM!xwA9`B&FLX;hSEf2$6JRY z>`FE>s8y4jz+A;qBah}|_C(9D{qU|AEE&1gx~^QfxPHRV)$rhHi#J7V-`kOdKu7C} z9OYJSt$0G-IFg0u&~`8v#97%_wfG<#!C-uR5XR<${X^j!?a#8uZ6}UITkw5V={Q@f z%B9~*$Al!HGTQeJHfIv9B6N23Ikjdb3}zL@teHd-+)2(6Y=j`@DhC(dZgcj(CI8a; z?V6>;Tsg^Axs=b7>JAOPW&>~Ou$EnI%>Sex<_Ho>Y zBv%_bp~;pc3)3CJbM(*bI%D_1U}fE*%`4%}`fXA>CMIui5JH&C)o5L6@+MK zxG1rHWvY$A8U%m5HuP0?$2AqLx913USsELaO#p(0&b}NQdeMdpefIm=@3gT(D-&`j zwoD;PNx|P~79DaLT8cX=znY+SwqnRGW9@Q%gH=<``f1o<2XmF% zS*f@Ki4fAP2Vq(|PcY_C0pg3RF$W%XckMv5Cf}SVALEW`i9=bWOqB%Txg4dlNyU>= zF-~1R%VAVWrq#vWX0WP*+XkfakwIf7K#mAAlbp&$xwJz>h~$VS1J2!DM6Ygcpn_Nn zcb+9a*4EMjtA)~h^@F5^nGQ>zy358!uR)-%+m4;bI0~e-B7&j9Wsjg?d<~W1hl?Mu zLA>@RAqX<{3^Qm~pnO7wts3;4A@(`@t1#=kQeK+$WQ?!L(4Citq(QD2a;TH4oe)P`9jfxOCjUD2k=ivc7bVz5Zf20%jp1U#-W5M<-yHua9h$^F$6dJHsEh%1 z{e_L*II6OJ@4A!C9w6C@(kszTkkHu*A%Q7yX;?a%zuNe0A`a59ILyj}FCw|RkKZsI zLgYpbj=`dDS)kiX--XG>)nfO!y8t0K;S%ila;b7y*NaN-3JHDUt; z$B^b!vjdR&O0AT>YM+{X#Qr+@7-zJxt1kp5ON1$tnRlv!Fqtb&CfZ&u=a2OcYJ+w{ ziAfA=GOV`FavFLqAGL35AG9CX@e<6jcb1-yB^77u_0KH6q)u9vMRxx~Y%0RMA~cDA z#We~cfrcM*)7%ZSt4~UP0j@OT->LTwK2!rK@?ay#$jEt{nH#mgDSVIpU2d=KX---b zE=?*8g83}!^4U75%eBcvZY4G=sBIc28ZVnYR@B!y{{3+BGj`Bk#IfuEwt4#jO=UQs zc4YCZbo1g1w&QJkto=^>{%3++&VF7_t%d5(IRg)h(6JrER0RbcI$j}RQyV0qQhVf= zOFnvZghf2zJy+AlGC{Ehn+`1zOl57=}00o&EqWXYBe>Y_r|4sNopgF0EN!S;?G{8=?uHTKu8_4;4c zE}d8)8^ofFkt-bQ>z7b|3`woVK#<3x7wvtCKSw1PwcbAYxT;)LtacQNm||QxZ7p}D zaFD*++cn58M0eDUPlvtc1_)YyT~~}5ISk&v<}GqG#RzempYRRbzXb#L1MD7TVHs`n zlMC-ucCDc_ALw-&hG?F-#u>VQk==zSsS`{U0w@<^C%DN*P-9yIUUlGED!-hVP3))^ zA_w(be~gf5Q^GcbwV_}y87wh~y@+{KT)M(0BZ(wf+S^pktU%EaoyQ>;-&`pN;@fjLEV_E!l zC85=O-vWxG&0e5~%Yos3tN$4T5u~YyKMKKrqhn$$2Cm6iOL(au`w<;D%YZZBx1RP@Sa~&%z{1H7;X({gyihl^s@_%B@dnJlP~> zz?HVhoP99yx16%~iX~fjT5tCrqWQR4nqa+WwUve8;Ek6o)>*Lq-`EF^LU-{c=jF&D%&n|d0!~ap)ZSE2hso%zk@!z>I zWAWquoX0rUM6*D1d=5+J9BQ1)j71VH#c{=}&ee^r$|@Xg>}>`sTzIaKTM1$~DHTND z>A@QjS*X+8dFlsYo5!omsrzkmY}Cfa&!Hk zxkpc9h{HeKxz>_Ovuv6O5L}aH?=GD867C?n8yN4AG;&%0ag(`Z>XSugqz^K=8tTmd$m|HT=ZiR&%Fp+@9#E)RnI;Xe#8*DCI>(+h*iTQ3>DVE z(5eB3sxZ@T*;Ds8r+yO{eJppxx=|wv zK1D}lJqeRn2JaY@@&PqFGfkZG37gVMVzm*g@Nyua`h+cMSgTx-C|!dvR&Q$^v+wVF z)drFrbd7;-txT25)dV>|KZC8+DYpSg4x9IW1FQ{#S;sXmA(%?BT40P6>DygD&8qn= zFkBZA4V~hCoqRCj(rP=wD!cQ|W;?!v0{V#W&q~6V=YI}|RjjxZyLk^&6gdE4Cyx`) zg0K(r4vcenMOK5K#Nh0=*b+VnDlxHhBr_JkT-Z7mo+B=gZULm#^*TXuX>*o^_SMfy zWk@~2Y4IGL1$qig`&QjlPqLy|K-dI@Kx!}e71TkJ2ghpj!%*?#S9O!i_Ry|FPd zHpCpIaZzqiIwp4(9y%H;7Dr9a3QA`O*6UcCTWSVqGd2Ws6$91wg31l+WH*3aL=4h7v!j1Pg}*O& zyT;uO9$YPamNEHOhV3rmGKMvN-N52@OHi8IH*7Olq4uo^s*hwz2&{Jet%_Ko792um z)Lq+JGybaGcS2g6ZMMv5Y_cY1*b(-!Yff`>KXxYvL^f(FSCEoos=$WlFQ<>&^{F{~ zX{yVfFLdEoSi25YoQ}S!rX0=>WLH*-!)C?UY<6&~btDZ_lcTgp1gAb>Q3V*aeF^;Y z^qsfkZP$@j$&Z!fj6ut84&Spxv{P7s$TZ%ci&QC}$aVxLu;v;V}8Qy1|&w z;E0lKj92pe?Bn@ww3E4R+uha%{L`FXkS=SCXdKAyunm_B)!J05Mqvc0$i3Z;^&1P7 z)KlYT9&R}WE&BfCm+UV4Dokvr^+4y6l{#HsRBb^=OcclgYi;QO)a=Z*XGY6xq=y+BYqT#t0B4iIu6?mRw$hh!U+ju zAXJjo^_GxUl{Q@UXF^J2%Ti32esItFlzr(Tww33QR=mh+8I?#Be?YxQ-GR^HFfi1C z0UBXv^w|$6>Rt88?EZ-rCO*L zTtFOPBmVR9n+$f5PziAaC{6?PBvyJ3Ij9VzSvdr5S3k!(Wr_khB_WPvsJHK4CTSfM zUM2J9S5@%O;sa_DY4YscxV>0-!ak9|&qj)^v@GwMxz-qQXM#4ssRTL`beyr^bxg?p zP(gM=30)I(+Y?FKv2 zJ~i>EeJXdzVr<)OL9&I1s8tI=`N4ULNUk<%=hKQ61ExAhFs!3))OyY$wn-SAIaE?_ zZJD%pc3rZ=%_G1K)lwlwHwubj1(le$v9T+x#BY)}eVr@Vt|z?HHDpfV&?cY*-Qy9=SVPo{2iIa9L@;NqoM6A8* zZfnzq0!iZpDf)snJGeh9K+wjvzFIW#kC zPfZ-OPt4zq%z|TV82d@eM!*I+^d^&*c6L5BSGQ%pq}VDSRolfeqsc^0M5Pw2#rw1j z)Gvt4b6zu@2zxQ@sw+B%Db;jp89}qufu4MfUD+H>*9E{aP&aE4PhenE+DcS%N zMJF_On|`hWYjuO>CGLZmb&a2aOmIX{KIi$)(lJyXqZNi2Ln?ISc^He&aZJ_B+@xJ9 z9J9|C9YYcc#uK9T$F5NkX@CO<5O_HI@ac0 ziPLsG^7qyP+>)HX*V&;1>E-J$9EL#ojEr2MPmru;Bev^}m@6Eh#UfOTm#i0*r?@GoW+d6rPDu&edB-h=S0j+wu3EYu!8XhA$1;9D_4h zTWxncsuIo*l*P42D(jD!nl#i_`RmaZb8Rmdfi2`Y-!YqZ-xQ~upgCDFRrf4U(=b#; zq!I6Eov=5w4cU>_8`d7nxdWFqWeDEoK6Wq&bt3{J)4Ug?h? z)&f!~X(yhjh&mv*nmt62=N7&v_cB_Tz_&c|(0B4PBFU8_>}&P&3jKv$k9_~+hx;x2 z+CfC&^4iT#GACLGIuNby?nCUR>I;>CEv<=&s3bbFG=jOoPtpXclIN#)+w*qZUg4zp zDRyx71LJh*!K&&-VDa^WmxBK4qtg*3+N55ZX7zZ_dEytq7Vr&qAYi(-6lQ%FAGpPSmbS{^-d3H_C&0e+{ z)VZ$}AF$KK`|K4qlUyk@8S)9#Wh@L>SjRaNQOsY=cf#w`!Q>JKBcgLP>!BJ9Bvcbj z`Z<;+M7Hl9{V{Frsx9@`{zM0p21@buebu+NFKKX4AxV3S2(+_12r*faulYm z)>}RZX#kJ5f5)|zJ6uq04tQu&R-$VL3521xHj$o5KYQ`8<*)Wayz!aEP8pAB6BB|L zZ-2|Wwql1(*j9p{SP-cclaVayxgi_NwqmQa%U+&qw`bUaH5nVQ`B=i{U|5R8W1h1n z+8`nwMRueGt)@BkbZKwvS!xEg$y51dyzS*A5EunXUHV%fZ_%7I_c;>kxd~e-sU$k1 zIop$%v?I;4cC2~Ix_Iu0&suY|027sT2Q7QKl$NeH^$D6BGCF{mb0gF9<4mmH#4jQC zj@M%rzMpb6No+7ZVLW z;{G9v*Z9ob6*g7EP>adWpMdEauvd%wkUuozGYw1zf7yd;DJD;i-za{uVo1B(9hIZU ze0jMGk5&uv{We|0q2)lxjV;aTI%!~*E6XTV-MXXm_Hg999pzBtp2#W3P{r72)YRG! z(+Z==DH&liu0cE1;)$t2q;eN=bAe=XzG#?n*#HX?S78y7)xi>EvqkUB^Ynv9CASI0 zYMmH9gSAi+)MOr`w*p(D-Qe4p31gx7njRPxnrRaluP3{`=bG|sgv4qS%U3bJ3o3~a z`-H4A7w)mbjc%JkeK$5LMoV3&_~VmZ%Eh;|=ReGa>3Eq^r^<7~b-6Hk)$SloGX&ag8ym6^gV6U?b&%SH$)_LR{By?2Nr=vXp{4V81w``HzKGwe{LGWt;5(UeyW)Oy8yGLbN{*+D=jTu zjto)JI@U@|mli)9IF2WIUAgoPyz%4g6fh4J52Aj*%Wf9C?0hk07m<2R!rI8{F9wYa z7A9!zVZuB{j3MUjmJJpk4vEwkiy>%H>0Qqn7jk2)E5jvae?Jnb{gEj<9GS6!$VIN_ ztc&bqN)}J`vRk;^@R*QYO z{j@#=>d0U*0>oUUFRx#VhKWI)#aPMRLTU%KT1pferkWL3gm&nJa*o0A+7ytriZfb0 z0nCG>S(*#77w@wCjUG$mS8H_SH1}xw%%*!kj?|Ni>!lUS1o`OM{Sr79V(Hyx22XO~4#=Lm<|YQ! zMtzb;oq;(H-q-Q*d6<`K)}~Dm0-7YnaCzBt9#G|lq3PS1N!j+TV?A(AYX#vls)@O0 ziN|-GZ(2+Xp=-p!2BgUN&7S82L33{E3E~{3!99GJ10oV-z6x#nxW`|KXVbRK*g+o1 zUtXsd`ku#n1>uU|4YM_S1A};62W6(IMheWK4>VR12^@=Z3{Nxuw8ZV!lX;*-hq02> ziYIb7O&z5A&%;#Y(v#TajaZg5;m|s2BdM`=!X~hVbNW!p7cn7+nVl~-voL98vC?80 zTu2nONd zKphz@!2`sfJIh^|*T_LsPJ@)uoZ8RLTM0FCjmxW>s3en#Y^`J?=SIKUS?@r(GDN?z z$8r~UL!2>goxaNMp&O2H2LwxmO54}lJ9c3o!5M)I6G%M?X1&|Kx%q3t5q#YQ$@4t! zG$tpnWBbDKLZG)KRRtJc4axR9*mVtK<;)5L^w_ZK`(rKw)8h_dK2Jp`e8*35Zs0#g zH@O!alH{o*&>q*Yl2`6A!@}XlC)pYP0cN{jLzDnR*0S^eZ|^+d>^iFZKX3PKSGOgr zSlU%_uW|)!Y*S-;NB|oXz!-=LaRT`R9|LIwL-@q>{6jD?B|!dkNN9!-9MiFlElaj> zkt}J|Ey?O-+uMEr@9)ga+qZF_I`z1@4HyZ63(=gyrubLO1ioM}9g*Qy^W4v`pC zAns|ibGk^~o*%Ma>6g)4tAa`ZuvH1XZ6WjkGi@_8#PWbP?c=5>tJN^u%>jc)K$bO( zw7L)3_u1`xn3v&)_T z(yePn5m;`qh47L4dS6S6sP5}fSESbu{@bPM-t(~!{x|BPwzdO|t=bH*WEs-Xea>)6 zH|M;>xI`6o%+TObrcb6I2TBOAkT@Clb`(#a8dh#xRyxrDT5_R=*0ZiB3o$0DIAg6w z$q;$9SZq=I8g^}5!S+^k!Qs_9a8P4E4p^6hX=r#Z<*cpL8XFh7rbey6E>AEEf2N<|=756N$ycgo}iRbY;5}`g zaPSG<=eru`qvFz-Em0#NvZ-kf-D3ixX z$kqc1mD6M>E6etX{=}AkH!G^=x^k|qoh?)VRJj~w4v9Y*yoWNvnrl)!6F_x3@FRGWzJ)91AH35iyM%hyL0Eu*y8eWnSKNjB z>?$rG14KFlup|H)TCg8rv+Xtz#=rNbITpFX=um!IzUJH_JXV@E9)(7k-1!sdHh+M= zx8D#SkM(Xm^sca+jv8Eev!4l2x)!iUH>3ukS-v<$(4~NSMa%+RTL;#X9kR!=O~l>X?XJ2~PuizXpHH2s zqp^|&?>al#BybkKjuVK&3{>Vf0W}mk=~!fgaLU&r+kcv;|H$8_~d_DmI#^ zrtCC6rGEzFfCPoQSH_v7<&J15f_^AYo)egD+er;U}n7)zJSb^7{Ccb|9Mz#SYavZd8b zRh)(GpT30gJMvN~>+-aef(03x^cvSU^Uds4_DQ}$W2G>Gf)84Gkf?TKa?dYG1VCls zi@9Gf39l-5cTo%vgxzJ3Kh))VsOpP*k1oWfy z@47YI7kU$#Eh6cYh`N>^JOaD+JqmaB`P`w)Nex~svLIlkWYr)5_4TvZ?0lB(r)Twm z2bS~#PsD@CN2)uqF{0dAcO~~aIu5&|c&Iu$wfHZ?=*fPS7OJXlMjt(9%&ec^=uSBE zsPARwxkNe^^W@`Hnxo=yK=nu!6jH+~OUqU@vLpaY(%6NO@1zUBBr|;2zCMwER9i@k zcG@(rm6Tw)q-`vGAz!&e@EkxZrC%|53Tag_U>LghGG1%v9M`j-oz)nO(#BR3?Hzjo z7JIDs2|N*q7wlD)9nd5cq0S1U#S?1)U|9@ImL(ao*=h#Oa%P(nTc^gt`)pf`~R2P6#r0Qc;h{3~|D%MKsq1z2KB`<~+d~H}D zFvXB1oUqwx(lcTP5m42V!a-3&_#TkIu@x9n2HGtXO;?Z8{da( z=5v4}UO*1av!t}g^xM=dy6??prLs>xw*ZS=Z*$~)i#q0bSx&@E_#oniZb#?=%nX-In z8!TK83jtnYmz-&0lTFNk{~F6=^#k_RGVZai_5|uk9oH;0YVI)cRCP&_R}xW|lyrne zXEjDNUP_DpcCturNBb^*Yr$X74`F)MK05#CBDOVG#qm>W&O3OtVP)JJ+EmcF8 zy-X;B#Emiu~vRqDt3RxO|G%}PB(4AqpY7qMP^lZ0TO+lD8e6IOc2?C?-^LR zbsi)&TnONLD_wzC<6Q#06f!(i`jjbnC7#J6tC`61aM{))040nl1<#>y{o>M?85YT8 z+8Jj6R}G-2o)%@@$-Yq~Fku#%a!-;K3h^1KK;m0fY>hvKc5EyRBhH14z4ACAd{w zGNp(@G%6?Igei3`FoTxvAk6S-uAslUy?6;}F&DH_Iq`cGd}J|BE1Hmvs;0xPs^y8G z>@cwNZgGvxZ+A0xu5~>de@hS0J#;TV?s__(MA4qe!M=pu9j=Qm($22KDB;7Vda1Vo>*5dT~jdQ2d-PgF3fC6564d2d7h1-ywdjc_Upo}YgCSTk{39ZE;bAROy*;3QS?k6j!@| z)RaY{#>f@&pt;=YqAVLjT@fS0-73FXR0yBxtryh zdbn4GW2MT~G}2;jA~9JdIAj23K$*YC&crMWL$vNfz-80&@G#t$1v!G(2w-kH$p)Xfn)0uV&KSkLaQ!6OFIHDqf|&8x&a$2aTfICU`|X zw(&BQYZYCP8q0MI!O!la{*bo`Plp3+=VQdLF2?`{S^LSYz6Q6@k4XozmZwX!ED2c`(rCKexr^>34{A_~kSQS;d7cih zM}Ah2-)5d8Kg^b+XWDgwJM7pG8#LlT2dtAg8Xl&o;#o0zu%iq83KYvU{!W}@Frqt_ z^bt`!>C9obbtX%CUrU~8Q;k~WL^cj5o5R`WaJ0EB*9Pu|hr|0^`Rn-c%n~w5o>$&O zm-Z#S4`Vq$eyej^ZzjS;ytmC>k7@Eh(!W!V1Cpg`3EfEKMN*ER|G_r3X2l7yG9nDQa9HN-oSynb4da)4N#})s| z(`FIqCB+Nijlb=CF<|d)-SJ6I2H;(6d|y(0Sd1QyRT#nZ9t3op!^?bA)=lT8WzJnn zU0eMEzyjg5fTnG*+Bc@M`lYTk0h5QHPkvrTzCK62Ji+Hj%4_8v$Tc3>weMv~gwMeG zwImudlSaaSQX z7yg#DES|=--gjWpJh|h^btP#;hvC$w*#ACr)4#294s|UcQXI6Bu*GTLP z?@5f+8)xIhbdAkoc%&WbV93hb#JywEp)`p?T2wruc-!tDQ=gInh*W)r%4E}Gy&8xJ zl_)jhG|=55Btt1pQ4UA|BxDGw95WiTU7#Suh2O`&C=$Rz(rqzIgwlj_WQjO_5=Z-# zG-%w#q}iMq-ga)H+~IZE@OZRkGm7`5oza0I^L-;Qt&WGm_=1MF?znz3sGt3A^@+sZ zmBtQQyU)WRek9{fUiW*=W8&ikD1^uJlHtf>)bBqR@Ua@Be;&Vi*IWn$tXt^ZCHK;0 z^(JQM{RdWwCVUJPI_ox$e`Pk|_vu>v zLk~c!;e!_c9S-Y`$-fNc=40Q@G;^N_b3B{AlJ3Bd0!+^L_jKP13t3qAPrUwa(ncEG zdJfU365?Mi)!z)QYsMtH=`TlKJ`bParKJNV;}2)8?t178 z@Q$+1xAsP>G!4Xy-Vo_k#d9DOvdX!J23L(5t<@Z3EBG-2ED0wq0u4gdxon7Z)wQ5R zXbz2$K@*=P@}gF$TAm};C=;LhK~o@k9yUpZ2ji?hf&QfCvJ9nBImu!FgpTlc9)n(m z2X@u@0D$kX!~w=L8cRi`F0Avu?A!*rIyU|#O5qfqV?fHC%$r3bwQBlZpP;Medgsmr zXw}Xq{U|ia`NEfSaIFF4PyL43dgOD@7d-wI(3bpYTO93$W1v&O7;tR>S&C5yfoul@ zfM*`Bm2{G@kDUtgO}G|z_q~DoL4({ zK76vgD}`bb`4WG$RK}_$=n;UJ=|yx(F-e~)Ynn;fQ#WQljfoc=B5R2041an6olkzl z%SVmmMS_x`z{#(Dh6pTF~W6MV3 zZN|a4x@IbkuwxmeE0M(EU8>+Q8Xem>ALW%vR>PH@;gV*jbzvZ8-$d*D!>~f#J(R7A zSH}1#Jh~5WrHx72!cF)#nJBXY!}x@AEr5{~ZwooI%#&yGDg&|aZoFAf=NW*&?W&O6 z2bq>Pse6;h1bQ8rrox^ zO>G0+L0mVrU|25&fMR658&nYyHpoegTifNV2k3487@&$)taT@1c*~hGfa*?wL-Vc9 zo${`bQZX*@R%fk8c;6zQ9o?RCq)ekjdXSanw>!7wO}^}{+yk%n+{ZZ1i!riGc!q2Z zL<*YHB;Y`-8)PMmT=W^RUfk@B5@3-)4J7T9F)Jfdi@qL1Ym*}@xi-Mx!crGy#NT2i zMe;oo(zYu(K-9^At5e^3ta6lxWA$Yy$HhD=GnpJoK93u=o;ueKQ20adrO?C;k1%`B z@^n6UEj>T~O%;G=aggqD<1Mp?nHI{r)mP!Ue}^%=4uAxRI0hIc>o)=wK@uU21j39z z$#4MCq!(D;t$p{h>ikz2!uBa2JWJQBHjSy2)32>czxgj>c$29 zo=e^Vp#yGRR0)|KOB!p)e-{%cE_d$Y|3#Geog{gye)k+EJ^Y+!wLDR`@*tEHr$K_G zx673S=n3fsoAgFQbc6%eN0TeLc}7SivQL*)nMj-sbf)!DXt|9rTV)em{H4{b2?JcF zgi*{N@ZOCR&hzXj7)ddx2jlp?V1EfWX$f$pHJlCu2!6?+@bi9$NADtdL;fSUsK#4! z!H?psuypZr}Y;3J8j^tZ?S?2+iyKiSP&-ar@vY;Lt98zx9Uouwexp<~dq%nm@ zst!e|!SnseWP}&B*l|1EXYGKC!+0hR0!9wNAF!35&%k#yINa8Cf|nHmu1K>s$gToh zUGg|fV4TWt@*3qJUhf2GKYTSU^yOrBeSZ#3o!=RFmBTwZpTh>G>JGM8xb&yt$~T|EKs;#_+9*qkpnE&9I|QEQGpddO(Ty{;Hr++oN3I`Gi~Gv z9PS$kLpg%i;U>Q%;i{=00M5FnF~;Os;R~uXfix><_fJF7x5c}Y^xQ$dwv$&H607LW zXvU+r;vKztja?q|U%(h6z~+Om!9xSU$~MEpO}*hN;;NlA1CCVGI>6a3hv#9>H#h(e zHnVV};p?bBEn5nkQ~`R=w!#qz13w;v{KpJ{if=lgrvRS<@LCQ;&r+HM9H4Af zmRi`#eTQv~Vh6%8gSekq`QxJn@a#<#VD8^~F;A9wcm4d5dWi+-v7rs7}aR?HX z~(~HWf%?M6wu-tr*!I9Opm6?6EVbqkx9!#i*hSapm{%)~t4J%}SoZ z7%l%``_WV?lh8c|h5{gx9s++4O?!EdqoPUOT7ab-fcgA;{2ESH2b3u>!U`CV9YErN ztMSNCCv3g0oeD682@|K`7C3*|rXj%x_$44Ju-dW8vv5RpLp&!wTWdmQRM(1MXFUs~ zViB-c-4u$G9kl`ULUe&b(S<~C(_V^C)AO)75bA;0rv;5`(2*(+m7JF$IAZx5B23}2yjS&JInL|X`ZOg5 zpSELcto2Da8JsCY4Codw^NXFk=S=4of55rZ-WzfcAEGyF=K0QD!SAhK@>_st3c8jp zumRj5czP;R4p;$GmMs)lD_*N>@LGM)`3j*j4wWea>)rp$g00U1NHC7f?25c~Q=DWQ zdH~lrr)=>pvBTIJ_}7J}w|h;v2Y?@xcuD$23y{ii_n&1r=zD`b2xX@-RQU-6YSLJ@ z(sR_OXRue(`~5f&%0}J%!UOVJ6>I@zHi4vScpM_(TKum7t|m6Iy$KaV`BjVOO6%iP zI=E7IM;GHnQtHbAn?1LcfzID${LKQdh)f}c*597Haa#QoEpep)L zPk!FHpHZE6{g|8wh2zmWzo{%Izs|X5>--iJxD3hCsluVk&1VMvhV6AVl-Ff+m!A0% zic*b95oH|pUJs44)s(NmRTjXlZe*i!(bpk46LB&xpSu3^@d5auo)3A+jl|QTNO8nt zsIbR-9^x?17U5*av_!pnV;WCG->u;H+~Y-%J~KTh-odv9Y(PCci)-qY33mnoSW?g! zvG$F)q-FRMm3hJwuCyVXasg^_#epi#gr{)?2@qgl|2$h^xL^=R@cv{CY6~Ym&bX`V zom+IJ&!u>!@|IgZw~yIhAMyq$!?n4we6XqaSAb~)%B9h{-@m}Q-{8SG3hzXwz{NKa zyj#w=909FGHwKn1<37qgr83|^D5nhNCt#HUJYV>zb5Ey{p~3v3*ad7Ybi!}_x^q8g zkp976`0mn4JB!w5f8>92yn+E%6=7>EBgDVp{nHfv>NC6%h9VV}g$-pkRQNGJCyliyc+XZr2aY+?3XCA~o3nbZ1UzjEyzUqPcO0xh%P;Z^v#@H*$N z;;ij>G>b}n&nz7@>hwVgI3Rx0&T{n_(o44lU?sVS%xlZp05WGSJbdrf9A{BhS#xw0 z_Gnc2lm_6yApdz3Jlg9Iz?Z9Zy1kVz}r-l$JQ zskmP%&)AfIQl`bF>z4woPNA&u)JI!To{27`3TaUW(5fA+bO~-MJSf`>Z44_9R)sKl zXg{eKp%{KfC`Y&FHUY>M)*RM`#; zm$yp%6`);kqjOg>Y2lJp7!UxlsK3W5w}+)UUISp}y>`4;1_pE^g7DnMEuLw~%Lu6df$ARNdrjw&xXrVLJ?`*y`Get8WG-meVo zCt(ji%(yxl6o>KV*0k_`>mZS*w~x;OfNM8huS;*^0MJI=BAX)HcLd#gA4c3Q<=8lI zR5I||En-3pJ!$ONK_1X~WpAqoX5WbJs_WJ^TprO|WfwS;ze_t_sy3H1AzVtMJXf_; zl!{;TYA{}t$43=&woKFL${e zFr#H~Q!aMEmOO>?Z^qESjWHeqPyKivjgV5@@g#QVv#hNCx8Cbgvx4xgJm}^~%K;_~ zOg3Phnb^5@K-C+aTXqxM9i8vPCi$y7$=^Exz>mKe_JVAyDJw0yY9Kh}wP)T)|H?B3 zI1+d|=N=}{Tmny#T@8+oeBXH&U=~kS_B8ITC2Zt1N=tR4dKUI)KKE((jVmDoJx6>4 zd|bafULOngU7xcJmuWl~ct2igD9v{OSe1~IAmB9^fm#MUt3ph6>S^->l(jaBffD;e zu4ARIUM#)W;1x3fBhy?)D zvWMMn#W(#%%i7b^z?Kpa3k$iE=n3>nHutW=Rsqfq{u+a%Mi2KZaqmNAv1o*`uA-sP zjd7NW;-_2mF_y--4$nIvNfc$;|LXhu=z7K$et^jv{|--^39%`LO3y{%faDr|Q;!#R zB@Ko;m5B*;Z#nGvwsViYf^x)zVyph^c_3RBh~XULs)JRpm~7U8EC>)zDk5*40 z@8QFE4{KWp+v=Zc2^4{VaV6=;$q23_=+psqwbsE?TOK3olyPNn$v;*h04wl8{ofTd zgP=5}7lc!h3xxDwyfpjMwqWSwtZOQzJ@=hNePw<3bZI$b$}^(lXE+Kkfvo!40x?` zCtgU`-<9M|-5QW(CCIP8@NhNg8lmEkH8ocJ&Qz@IZ=%;4Fji-l&v~&aBJ85`M8WMl0Ahtgsx@6K0_316J z3ISMBb`*@3!PjcHQ_6$YTUf)p60acZvXQn&*{j8-O_8-7j|NGavbR9|QJ8TiwfuVy z(2Bei1xL439mYHwpg1l2D z$?+l@0~m!m^p629y{Eu=`I`+ zc~a%7^gIo(JSqoZseo()M|m@*M&S&v2uB(!7y;X!XD>=dm3X9aj3qqAP6|wQ>_Uuo z(7nUyJMVdq+q3Fc z*SPq6H+|`QT;seIu3_%kyaz~1lHL;~ftSDI|7v!f3`;vQ*-3+-6HwMk`FF14cogSy0YMSJQ4HS9w;QR1Y{R7|QyBgDz2T-O|CLBOWWLN4Jql;D{3mSJ7 zDG1ZPd+}t^5UjbFumG}>DofHMeXI?rCj|xmYBpfa3FJtXQGA^`l&y@cQYe&_>)wrm zoW`|Up;N1%idRm7Z{nd^6PgvHv0a_p$A_otdg|Kb+_`rT46Y77faeNuRSg2uI!-A~ zFb7oe0tjzd;?rWS>X0l9RM+8F^ZIuk+~W4G`5X5X@|5q|VK}O)w#n7bzQQ#vy4p1^ zc%G|mI>A-fPve=o-FhhxAjxQIwvK|cfNu{5#(G~J{gr{HzIw#c(eImiX0YZu+Fj?N z9q!1s?{eJfy4ae=dg4irUSZ!sccD+6kHw4pozRAy8@tY+rkXx z=i*KO0YI2BK@lA5JR4nfCc5hJrxEr-5{s@Zy6r)TCR=x)s+9n%w)-hi-?~<~J&7+W z1Ym^{P-{b5f7H=cm{O5A08h2hX~U@iA6nQs837s95>$rFGCUN3re&XH+zN_IpRle> z6q$^g_P%`NExz}s3Lq`Rp3($!fO66JzG})&8>;rBJaw}3;5J*{NgyJRr%OfZw{^>4 ztJ+=1p8s_nd+&Ao9{z;up@rU~4dfMNJ$P#RJhl|QhGUVdoj%Rg&REDq;pKRPnpsy- zGLR!a(ok?o^-I8reoEjtNVERwe|P&H*L7qwd1&R>;W`c-!q%;Kxg%e2>e=h2SJKvZ ztT~CEjRxMqi(xB%SJTeJjh2}~&*ee#cOP-TibH9IA_#4wL1DMl)mfr?WgkrcjE)18 zr#4z{2V||HtS)#G`$EH0!Xddckc9_pDd20viwS!+&k%}Yk%WxK$fP0I=BO)9dpxEz z=(4rt%Nnf*(%woXpfXH>L2~0)5Y!C0akbW1C(5Si#(3!ywVES&f&)vo`@eQl@|vn?+jhiD)tAS=iJ4=Mo1YiB#PyE(u>evjG=4&6ZGV{ zUQT_85&o6nAp@OH5uaotDXb%ms@#HW86}O8PN~3w%JN{vWXm_N&8VxV`EIThXhqJ9 z@r(t@;?2tAA?(~1Du2>)jW&8Ik!lPLrzNkW%!og_;mvAt|Ec?(+x{V*oy@bXqy13& zUeP%~xv9Q2qC5?u#1BX=C|A`()_uxWSGl>fdfZ%LFdnsGtoG74KMG#TVNZB$Z}*iZ z9^}>Kq9Z}U=0w|5dJ`n%g!i^@;t~lu-etGh;Ld|j;^^xFltq;qQr)nOI>q>%&U=VN z6@WaaRE8Wt2C8U89DRs^?eAqxzK@VbwFIJ2P@W6^(78kJK(IebnV!cZc_oKKk_F<) zA?IB*92KR6+e9SBKb&~VG>n!{($Jj(ugWx?6OgQ^4JP2zWBDXPDd9A477AXZE+w@( zw2Xtb6m`=iSQmjNfiTCcl;jM*1bkFkx^eLqZ59jO3C$Lp_AFKof2B`(Y#FMIH2r-Y zic8X%$5W6}`EbBy+JTa^A6P^@N}b$G`rWq4>s5_q(NY?riZx)H7Rf#lME5!<;zKwo z58!|n;8HMoxhzV4gx9!RX+(WNmh-tau6_T*u5m74%5tNOL&{*&5*Pr$vW6gFHKp?8 zK%VZwEONg4U+`2t!?~7MhoYnmr582Bb@7{-3Hu{}65z_plfLc&N#&1JYYQ|DTJIrT ztUo3il`8QeU&?bIU{$I48L;q*cC$hOR?L5*PzAPn)Itc)5|6wlZ}1gFgF~u0{2&nm zCj-WK)3bT9w3I?I_>-B7qS?kY|C`}E9+61Vi?!{`7@*%#J}2>HSA3eD%Yp--Bvp}y zGx#G9Oh0O)y!70<*9NFd;Z_}0PREJDVL?aB%Owxj(QuBKE!v@eUnkw4DjvKCH^6W({;{f2Y@}wN4ue8&| z32(#Oek0|1KKV!jED1JYw5?ayZXe#j6u0d>K>a9RP7IPV@=!xBgL*ow;!lP=4U*L{ z-_BIv7_31v-LIgdU@N>Th!mT)_z-m^!fG~?W;@s-a{h7p7Y-Nz6%!M@8Wm$O&{_yp z9^{!A8lw=WjE~kw+Vxxi8C&;gtltZtNKZxLfItCJHJs_=OM(a8X{R3$o8o7E`XEF8 z4Mi+3S3SoGdLbcdUD&J$zB*_yb>Z=n*Q-ZP1i}e`*}-cQB`qq|7dE9adfiXH7BANq5y&mvq)fHga~B?Z zHS&#ubm%mhC3qb}^#XY!iJE$l!9O@dYnNLNMmt~Of zh4?1(1$o!hsif5#ZFVz@7=N+>*ZS>PQ+cRF2%&keIPYCU-_hqV&?~q{xRlC>1F{gP z2yn2|X3$BB#->AFPLWX7$Vp!?N>n!uW7080jbxDm=^%{6MuX^erMOXoTcr7?@DsN!snj2ghEQFvPk5E5)U zD?|>OOsN7mK!3Jo79RKxc=(shf4G7)7bL{ni5YYuZv6;)OG1hb>0>W~09HUw2T!78 zIOId)Vr^`$eouzH4W8@KJ%LvSvUj1FaN>mOud`G`N)j3|MQf30dl1cHv)K?+RhT3& zxDLi#nXo>2th2D%tkd{Iys=x*3^dLDeGg(8c~pmW)U%Yzhy$nxd3|wWxvVNkB-IOk zckZC<9tsMDR(z>Qp9@t{ctxN_hd@pfJ^#m0GO6)l@?Nag$$K%J&hhzB_u_MHR<3tC z?u93KKcqjBkyP^`oLsJJgEz=FUJYGi3s=TS(sTK606nB(4P9IDuK&&htO%Cvj82>P z2DU~8U}XuHZ!Mps7cC_SbaISUvb9pQZ&Y$b)amHC6mBqG!XG>L<|_qQy}aNGp%Q~M z)1zt$hqU)V+P2S2lk>xmt}VDJ2ebNheD~qv4j6V>kV%2C$9^Fg0$(@oL_pjxNs`G*AyS%Nnlo3MzW9%=i#x zCrU}ZDz)?A`G(PbBYkTW8~`kt7bT217YJ6Ln-oPAvac;=rk%wg-{&)K?kc}TLt#Z` z1qw>HLxXwwsl5Z;7LKV(fJ-&V+F0u0;3gUqD`AB62p(GV)QV@ig}R*uSe{s}CdYkZqiM@KI`gH#6<~Q-CG`<{=S2A-FItqYgpc#N=dMlJ=md1DE zA5nI6BW3~Dm0m`Yxmtz+*rw(B5$gE(a=i?{rh1H$(+2k1@gx*~;XDMNQh+sRks@GX z=2Vn3wY25-G#Uc~WMI?^oG~ZF6;9^G;`#tBc@x<9t-it1UnAiFUMB-hD1L2TY=)Ur zEIysVP)3a?jOi}~L|siQ9PdgK?mR*bzhm+M1=~t6BsZLN-SU>Ed4tc4j|Z7Ghr(pNloOy8Q+HlL>jN63sC?|LgHw4b+Yk-8cx0xM#L%WV}fM)YQ1MOm=O&aeL1zGsfhh*!=GLPGxK+!ahARP0 zm*62;%)z-~DdB?r8tiYBeC>`yO9m6D%5~K}()!-6jKGG+?X?R{``b9V*w92Iqiy zM47>Io5E@VLtU^&2hX%`rm-<}pAQ~cnLR<)ln2`@uwUc1%u+gj$x;2Iu)CPFlcR1K zCF|tq>Prf+0xJs)mEgq%OQ+nRYXnMz+jF=>O}0iSRK2npT&n5<>`#iiV_W!+6n{#_ zj11$^SXYUGPm~1)wC72$#I5TN1yFS^hWeQdvb_eJ)q)YG^;|uxmw8cU4NxT&L~js_ z(7=@nfiTvM)$h6XNIQ5R#{3bwakpU1@uFx^Q5BW6Mm60D7X9|*i|Ms-0OOqB=y`!1 zHb#lnd%l7<>VMGV2jL@SOA)&nH&NV|Iyd*Z{GPACfy7+B6p7!OuGq93&&jfIlY4y{ zExJtsX&t5P(8-;)s>MYG@8T+cG@DpQv3=wxH-?H%1_EUl2Nk-iXfGF!y z3*brF*O7%M(=ebj|CICT9y~R{KaKGih@>nF{0;#OEDcp|7+!60a}*c&QMxt`-36HX z1lJ)Bc|a6zrhz}RITK$Vz@fvBi1VDtjhpgf58G_7lf``?iGlFiFH$93m_~C0*Ee4nMt$j`Lrc~@vFEPgu3`{ z8x5UyK$*N^hwtRL&C7z)Q;-F&GjoDWkQd2=@?(kSG+D`b+&^==sd5OOE3M(7;w@Vu zedai5^tyP;FGHmALUwJ1eHs$6K5@IZJ{6}hQ-D?AV-Ui+#i8O1ShEVDC*OsHj;@3p z26r@qX73Ed6)AgKOh$=7VsB2Z5rmu0HCYwSwt#B6@}=?dVtrpfS#M3JC*V($c74nfJdId z`wJkdM~1WnL>3vVo}o9Pq%a_F2EyaLc=jRfZw-w}b#+Zw+{j?&6`Vhv2oU0E2T&ix z6LxS50F0yk-#Mf&2&!~fFX3IqAz5O0fgujwtytj^uSmb;tzX@=Mhwro;E96eWu(h7 zt07SuuUCIEX^g0QDpqKCp>op9=WZGYVvYXTxpe%1dvra&;y6+Bcz}Rx#b}t6^4nN$6 zASS-^-oq-?80TnU<9Xo?&g}qTbqRd6@>~`&rSjqcI!NU>|IPftxW}@K=dIsmjMqDn zGr+pWcxka@g_kISt6eQ}S^FB~ZaH8c;MIgYPCFYT+e~x<3p%3QNzOQ|Ynez4ucnk*trB7Pc)5#(PC5zpIy@AGD!Jb$}9Oh-tkJXN(g3~W5H(ye1E5qwZXRlPDf zfRz#V#2%y4^CRoB^Y|1>RvYeWFF&iq;rmnO1f%PgAc#{HTp@%S4`s$SRL_;48Q^)$r~kwtLfM>8ak%&d(L0si8>EJ)#ArQak~#4Q=j00 zu|R(UFOn~4*2-aw=HUYzs{m1-<5=q*0?{totMXU?k_@E5Uv1Zt?Fw;XSoeR38E7vA zFq`TJVhq>8eaToedI(8gAbGTo4Dqw(roff<0PYe zN`~cmjd621p#*dCy8$R9qLzBrpSt0x7M3;MllDlFO%s|8J}zKYt-@78CNwdTGvjo& z%7VVc?r729*eEG{eSCS5bu}{!pDt7w)xJ(zCEXYkt3>0Qs6>KFe<{OL*Gx90Z6H_jD60Njt>|Gr#sue<-I!fsaN;Tk*(OgA8`B%9sr~? zpk21mlCY3hR4@6iqaJFEhX%R#A>wcx{cr0=hI*^GwtW=vcP79~>D7Hj4lTe4?!1QA zzwWz-^09U2AE$pOiUR6i1UjlXj1;Nfm5k|z z{jn$Zr{NTU+Y0ASx{{yd%ZRXAYw!RdW%qA6|BQV7*w?q3B}9Qf5Wp#*LI2jc{nc2> z?p@^dO#sKG{5eXRg9ET6WaO(o3 zEc$I%Gm(6tMkgs22#M7!gFH&UV>4$}+Q~#J4DzZmDrWd6sy37XPdgAuc`i+B#PS~^ zU1Hae1W4C5zC9TGBeY{|nnSxW=`Ht$GKuxh$n%zJl$D1|UV zL*k1Dxf`kF#W7mS1In`GWA}gI#dhBA@78F1k&P`=@%$mmk;AyZ=}WWnSQ8H|$_Og>}8`oYolAOP%9 z>gs>`0~kH1Mhpx$dn?Ci@4 zbFKbr zEi}xMX@s{ZKCQ;7F1(s(7~n_0=;K$5cGlKIs@-c}i{V?12VpTk^wE$98MWq5Ikz5< zg2soXUaX?AesmEv09pXmbFT;dzTHO=!F+6#tfj212nknm0a6n5F&(PJl3{?ANFo$1 zhk!^s9;o&;fG|}m#`tlUa_vAk<+(E19Hvkv<#;%Wwdwr?Jh+uVDBn1XMW2(xnsEWGF0QS$CGRKK{K7!;t4qKLwq<^cPjt@)(v!X@O;|` zC%p98kBFra_VtDHOYli~ZJjH2gc-K}^-zef>bOvmEgEPC#!*9LGBASX63 z^sNpm_|8vre2MD)n6EQ>u1%6>zkqJTH`34obk+0QK!PfiY)!0qT#DosYdX!jrPtd} zmpj0C`5!p96@DH59q}RSRWx9Vv1LtW208<;NXuVPFHZE|v-E^A+4pP4IDVV&NqNOa zQwTQLN!iisMI$1tUqTx20$RjHjl5{7EM7$9*SY|AAxG%i=oBz9Dc`(j@^_zloYW~( z7>xFfp%9D5uJMgY`5>Ep+^FN5g7Wy9_<%y}z>pTkTn!^-Cd9)CcT5lzP#y(Uz1_KU z?qxjC#r#HjTIEn$s3?#AJzW7G1(={9V>iVy@eK2ZtuciG%F)_YnrHk0AmXo4lN6%7{p5he_HCZw-cI-xsJKcn;D{w~agF_DB2Oes#!V-LWX6q6EEIVFea2My}i;>Mc6O#8%5e;vHI!gp&Y$88qIIqKa&$-3NzPTPDt zo~8Huht(_K)e|w~2+q=3vv6i($ShO}qa)`04c`se>=mp!w0NpkcQqhi@ly@SK zt}N^sIO@nM;6>#Zd(pL@g9eBdec(X_q+UrJ8enOPV;6!}=~UO8K>L-RnbD*l!PHFA zj*TOG*@Svzuk;CBg;8h&@SINTd&x`C4^$W%lL0`{1cP0_aPHwNQ5Z5FYk3s~GHl8c z2MSuVkGk3cRA)PP@;}n;cew}C7Eg>XW<1GO`h73R>DI3? zyH@Hc_Gi6LZ**XujPH(0w~>dBk>Tt zn&BV=4MabNedvV@dZ-gm+gU$lGRSjyub;l!iN5;%mFSn#d4^sak-qAe^kcq1*bUM( zr5pt0w;P=+@B3I5y&jC6<9Pr0086cgk$OAEG4aR$)KnA7<8ea*i>E@$d?dtcW2ZWl z!!+F4dz@Q|(yXJ!UQkNwUOhtV}bnZSsxad9)^*Ur+bJsXnQu5EXAC z@li=CL>KAUgEwur#(8M11F3v!Pex(KVR$1hz)_!2@{H`#@9m~84ok!z;KNXpR!e3+sEFLTrHN@+@xu zHbCnY@HT?1%2#s3;!Vi|1wLk4s0*!hfxVUSRv&@q0$Y)S9BZ$_2j5J$?Kda~fr}k6 zKzcUaL0_U~|8}7dggH(IJT1NsUd{B?*=G6$9TVkR^?dW^z*q29V?nDfA=aSIul5CB~e^=OaBU`k_dkOWToQIDv6X#Z%*PrT-$^W#!(pCzAh*DV&f$da z4I>#ZDxuKSI?=d@OMi<}1aS2s%IS$)aOwX&0Ca|@4 zV!6hICVvAI_VA6(pX5xJ7@ziA!f?MB17Q24y8?fx>r>tSbN`faai1asVo)UU5uR!Q z@tQXfJNrz3RORL#bjJUh)`TmQu*Ef|`3KJ=(xmucxgv!ue=AvU!XW2;el2?sRFp99nSo@`qsjC2lNdcrF)^#6j&Ki+(IY#yz$>yK=eicZwp~sd7ARpSTvSD zS|dwi4zomZ+8s=Mc|nLR!S`4mYK(6FIAN~wV>#n}3(B7jpGPmfi82&eTR)am4gRja zCGb`=XYeqU`b2m_-a(tSFtudc11!0za|oFRv@8D=K_t|=1Lq=?xQM4b!4GW_&?%5H z3L3X^UrK9nqjMKN%&fn&d>I;;Po~*4Te!is{Zx75PTuqd#zDN*Khvhld6ejI*P=;& zBE1eAP)nKm(?fUE8FY`#e`(-%q70%(OD_G#{*Kv}00K=&u!#ZUe@eJaU;@0<3g@+4 zo_Z$+``Ml`(O}!J1Yfs&g|6|}(0*v)$rcV;_$d{c1C7K7*vhps{u2*T;5W*& zeJxQYg|CNud+&2@)y0%)3-1s|Nl40yz7K-0MypMz?h!K z`|^n7jBDwiQgJ|KfK0|o9@@R(%=debQK4Lt-wrb0vD>-(E~9+WH?~M}Da(m8%C2P@ zWM9KkaTItQ2drEA!@Y{jOYst%$=_9JaG650?mz%mr6s+1p;-%z2rE%pzW-)qb3*EQZ z0<=C#Lw#Z2vWkh37iI&)6s>IIF5 zN*h{KiegN}Ss?%`2wpAj)51>SiCj#%OYs9?wvD*Q*sVZRC8vTydo2o$R(>spDrtO@ zAZzd4tnU8w;H{cZIJ&MH#1=g9Jxu|Ep4iHFJZw$R1>qb~Srf%+&@#f^S!v5|N6cxjSAj(Q?iq%BrhyD0RiiETR(9;-B}IjPJqF%KaUcmX)QHhY|r; z-1vD~i+@I#6S>9x)DRhmCiaiYhO)>o@Ky^w<~9Q^=n7i~Xwa(Kwq%BaB#e?JSmmdC z0xFTXr|yIO_i)g?c&HWN^`8{|77xG_Ou^OdR>fEpdCd&nbZ|yKbSO@{r!(;lX?&D^ z8A~Fz_!Os(I$p&<^pGD##ib2~mQ%a|kKK%=04hL~HYm=L?`T1!i;N$^0Q1X!>$cTJ z&Icbvj`51@{gQvJdKp1BC}2wE%7KDs{W;E^O}W)Aq|6YE{yCFGxrynu0FSSZ%GaZgnr*o9LvK@%PIVV1=(b9GhWBSY)>u)V9Y@E7c%OxdBF5?KkBB@kV7WAdAEmfT7IK_ zJpgPI2gXe6cWFlgJMb>$0AJm7;~4PbJE|9jgZQfeQ?+!psBtE*sk(x+aAU(M;4BPR zmghR#0=J2@n5-(@Iuc~x;deB=dUW_PR$KoTQ~m$euaR5R!c7`Lj`XEeSsakw>OybL zr|b8Ozej$M4U^R%2T_M5n*kc?TNfdG#^8c3KE z@5T2csEWS}vKeo1&L6?Ba&%x=_Wz6-l6T;tqRX!vITd)UCT~?MxS%zk$Geu&B<28R z?U32-wRp4si}?-6tz@VmheFo~w!Erq-pC@P-$9-=1R?^GVz5TC%{Qy$g`MZGixKUB zap+s*sn7+eK;ioXhEHz}hXZ8*uqZ?ct_H8y&JBe+oTN|IFz`d(0L`N7L90u~i%ms| zQ;|sFw9wLNWj58{vY;PFe`6Vy($;U{wR#ohOc#yY%>md-X^L<_vVylu?jD+8UFhLt~?~M_V=bHIzVI%bNA2 zu1U4Nb@cE%hr!d%6rBK~PCRV_P+i>Dy^ddUzL$g7dCiij_^KEO=?wR?M@he~8h zQ;q}LD^lwVHk{?$`2g;Eyh+K%W7KbZ?qb)2XCsR|w=Ir}rQd>{)u8t*jS+pXs6_nA zpY{SwX8{(Kvx;ou0hVUg)FG4;+Rt2czK$Y71l@quR$qZj)p}5UEe6^CUwEwQFr4SF zXG;nUVjsmrYe-&#J$Ivc@K|V|w4);1QH%tnaZD}_2!MgG3@m9}iw=ZUGG*iP07Z6| z;p*T%r{R#vJ3!0<)C}P1JGv>*bd-1`lZtz|5fU_|smFoLDTv9su7W?{HrlBR#_!J%|f?@KpB3b=nMJKbxOtJz{CP9jkC(q zDlIxHFBW^oBOavNwi3W&WrI3GWh%v|ts)g6VvSMIAm>v)=iHJTe0?v(9h8oy?>_V< z#;VA5PC1u)#!V5jl}3?rAmu>Hf#Y)k;3N;#xilPSUy5D}4N1Keub=ugy6ogeh%(T!{v zy^KaRo~{hz{>Dlgw_MvyT%1kE>RWiK)FYB(okaV5s(;7I=3=}*4giZ}PSrK71f+6Q zsLrju6=5Pkhw9phDOAvW6E10HUR?laYG5o?tkChSr6eA{iHU(k| zptDCGbNXgcIglywEJ7QUhf(gcWkykRP==AIveBB*D%3d+$^1mpXulu31m|4KfX>h2 zk_Kq?R~lNydes@+p20K59Hf+TAmu>HfhoxWhQDa?Pwol!HT;otCtZzROnUl3Kx%NW z{{-s*{uSV8A - ), - extensionId: 'faceSensing', - iconURL: makeymakeyIconURL, - insetIconURL: makeymakeyInsetIconURL, - description: ( - - ), - featured: true - }, { name: ( + ), + extensionId: 'faceSensing', + iconURL: faceSensingIconURL, + insetIconURL: faceSensingInsetIconURL, + description: ( + + ), + featured: true + }, { name: ( Date: Wed, 13 Jan 2021 10:47:14 -0500 Subject: [PATCH 1955/1971] Update bleb and form url --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index a10941fc04..33b8759e99 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -557,7 +557,7 @@ class MenuBar extends React.Component { ) : null)}

    From 1cdd2ec56fb7788cd7660cf8d216e50ac4eb46b4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 22 Jan 2021 12:07:47 -0500 Subject: [PATCH 1956/1971] Include a sane cacheTimeout for video getFrame --- .../scratch-vm/src/extensions/scratch3_face_sensing/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index abcf590cfc..ddd868016c 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -98,7 +98,8 @@ class Scratch3FaceSensingBlocks { const frame = this.runtime.ioDevices.video.getFrame({ format: Video.FORMAT_IMAGE_DATA, - dimensions: Scratch3FaceSensingBlocks.DIMENSIONS + dimensions: Scratch3FaceSensingBlocks.DIMENSIONS, + cacheTimeout: this.runtime.currentStepTime }); if (frame) { this.blazeface.estimateFaces(frame, false).then(faces => { From b44321f1aa26fcc452b87507d71869822798208c Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 21 Apr 2021 12:03:32 -0400 Subject: [PATCH 1957/1971] editor logo at top left links to scratch lab home page --- .../src/components/menu-bar/menu-bar.jsx | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 33b8759e99..f734c8cc17 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -356,15 +356,16 @@ class MenuBar extends React.Component {
    Date: Mon, 24 Apr 2023 15:22:01 -0400 Subject: [PATCH 1958/1971] Merge commit '09b4b559c18e1347a1793ae50dfd9fa79cd534aa' as 'packages/scratch-render' --- packages/scratch-render/src/RenderWebGL.js | 2029 ++++++++++++++++++++ packages/scratch-render/src/SVGSkin.js | 239 +++ 2 files changed, 2268 insertions(+) create mode 100644 packages/scratch-render/src/RenderWebGL.js create mode 100644 packages/scratch-render/src/SVGSkin.js diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js new file mode 100644 index 0000000000..c65e674706 --- /dev/null +++ b/packages/scratch-render/src/RenderWebGL.js @@ -0,0 +1,2029 @@ +const EventEmitter = require('events'); + +const hull = require('hull.js'); +const twgl = require('twgl.js'); + +const BitmapSkin = require('./BitmapSkin'); +const Drawable = require('./Drawable'); +const Rectangle = require('./Rectangle'); +const PenSkin = require('./PenSkin'); +const RenderConstants = require('./RenderConstants'); +const ShaderManager = require('./ShaderManager'); +const SVGSkin = require('./SVGSkin'); +const TextBubbleSkin = require('./TextBubbleSkin'); +const EffectTransform = require('./EffectTransform'); +const log = require('./util/log'); + +const __isTouchingDrawablesPoint = twgl.v3.create(); +const __candidatesBounds = new Rectangle(); +const __fenceBounds = new Rectangle(); +const __touchingColor = new Uint8ClampedArray(4); +const __blendColor = new Uint8ClampedArray(4); + +// More pixels than this and we give up to the GPU and take the cost of readPixels +// Width * Height * Number of drawables at location +const __cpuTouchingColorPixelCount = 4e4; + +/** + * @callback RenderWebGL#idFilterFunc + * @param {int} drawableID The ID to filter. + * @return {bool} True if the ID passes the filter, otherwise false. + */ + +/** + * Maximum touch size for a picking check. + * @todo Figure out a reasonable max size. Maybe this should be configurable? + * @type {Array} + * @memberof RenderWebGL + */ +const MAX_TOUCH_SIZE = [3, 3]; + +/** + * Passed to the uniforms for mask in touching color + */ +const MASK_TOUCHING_COLOR_TOLERANCE = 2; + +/** + * Maximum number of pixels in either dimension of "extracted drawable" data + * @type {int} + */ +const MAX_EXTRACTED_DRAWABLE_DIMENSION = 2048; + +/** + * Determines if the mask color is "close enough" (only test the 6 top bits for + * each color). These bit masks are what scratch 2 used to use, so we do the same. + * @param {Uint8Array} a A color3b or color4b value. + * @param {Uint8Array} b A color3b or color4b value. + * @returns {boolean} If the colors match within the parameters. + */ +const maskMatches = (a, b) => ( + // has some non-alpha component to test against + a[3] > 0 && + (a[0] & 0b11111100) === (b[0] & 0b11111100) && + (a[1] & 0b11111100) === (b[1] & 0b11111100) && + (a[2] & 0b11111100) === (b[2] & 0b11111100) +); + +/** + * Determines if the given color is "close enough" (only test the 5 top bits for + * red and green, 4 bits for blue). These bit masks are what scratch 2 used to use, + * so we do the same. + * @param {Uint8Array} a A color3b or color4b value. + * @param {Uint8Array} b A color3b or color4b value / or a larger array when used with offsets + * @param {number} offset An offset into the `b` array, which lets you use a larger array to test + * multiple values at the same time. + * @returns {boolean} If the colors match within the parameters. + */ +const colorMatches = (a, b, offset) => ( + (a[0] & 0b11111000) === (b[offset + 0] & 0b11111000) && + (a[1] & 0b11111000) === (b[offset + 1] & 0b11111000) && + (a[2] & 0b11110000) === (b[offset + 2] & 0b11110000) +); + +/** + * Sprite Fencing - The number of pixels a sprite is required to leave remaining + * onscreen around the edge of the staging area. + * @type {number} + */ +const FENCE_WIDTH = 15; + + +class RenderWebGL extends EventEmitter { + /** + * Check if this environment appears to support this renderer before attempting to create an instance. + * Catching an exception from the constructor is also a valid way to test for (lack of) support. + * @param {canvas} [optCanvas] - An optional canvas to use for the test. Otherwise a temporary canvas will be used. + * @returns {boolean} - True if this environment appears to support this renderer, false otherwise. + */ + static isSupported (optCanvas) { + try { + // Create the context the same way that the constructor will: attributes may make the difference. + return !!RenderWebGL._getContext(optCanvas || document.createElement('canvas')); + } catch (e) { + return false; + } + } + + /** + * Ask TWGL to create a rendering context with the attributes used by this renderer. + * @param {canvas} canvas - attach the context to this canvas. + * @returns {WebGLRenderingContext} - a TWGL rendering context (backed by either WebGL 1.0 or 2.0). + * @private + */ + static _getContext (canvas) { + const contextAttribs = {alpha: false, stencil: true, antialias: false}; + // getWebGLContext = try WebGL 1.0 only + // getContext = try WebGL 2.0 and if that doesn't work, try WebGL 1.0 + // getWebGLContext || getContext = try WebGL 1.0 and if that doesn't work, try WebGL 2.0 + return twgl.getWebGLContext(canvas, contextAttribs) || + twgl.getContext(canvas, contextAttribs); + } + + /** + * Create a renderer for drawing Scratch sprites to a canvas using WebGL. + * Coordinates will default to Scratch 2.0 values if unspecified. + * The stage's "native" size will be calculated from the these coordinates. + * For example, the defaults result in a native size of 480x360. + * Queries such as "touching color?" will always execute at the native size. + * @see RenderWebGL#setStageSize + * @see RenderWebGL#resize + * @param {canvas} canvas The canvas to draw onto. + * @param {int} [xLeft=-240] The x-coordinate of the left edge. + * @param {int} [xRight=240] The x-coordinate of the right edge. + * @param {int} [yBottom=-180] The y-coordinate of the bottom edge. + * @param {int} [yTop=180] The y-coordinate of the top edge. + * @constructor + * @listens RenderWebGL#event:NativeSizeChanged + */ + constructor (canvas, xLeft, xRight, yBottom, yTop) { + super(); + + /** @type {WebGLRenderingContext} */ + const gl = this._gl = RenderWebGL._getContext(canvas); + if (!gl) { + throw new Error('Could not get WebGL context: this browser or environment may not support WebGL.'); + } + + /** @type {RenderWebGL.UseGpuModes} */ + this._useGpuMode = RenderWebGL.UseGpuModes.Automatic; + + /** @type {Drawable[]} */ + this._allDrawables = []; + + /** @type {Skin[]} */ + this._allSkins = []; + + /** @type {Array} */ + this._drawList = []; + + // A list of layer group names in the order they should appear + // from furthest back to furthest in front. + /** @type {Array} */ + this._groupOrdering = []; + + /** + * @typedef LayerGroup + * @property {int} groupIndex The relative position of this layer group in the group ordering + * @property {int} drawListOffset The absolute position of this layer group in the draw list + * This number gets updated as drawables get added to or deleted from the draw list. + */ + + // Map of group name to layer group + /** @type {Object.} */ + this._layerGroups = {}; + + /** @type {int} */ + this._nextDrawableId = RenderConstants.ID_NONE + 1; + + /** @type {int} */ + this._nextSkinId = RenderConstants.ID_NONE + 1; + + /** @type {module:twgl/m4.Mat4} */ + this._projection = twgl.m4.identity(); + + /** @type {ShaderManager} */ + this._shaderManager = new ShaderManager(gl); + + /** @type {HTMLCanvasElement} */ + this._tempCanvas = document.createElement('canvas'); + + /** @type {any} */ + this._regionId = null; + + /** @type {function} */ + this._exitRegion = null; + + /** @type {object} */ + this._backgroundDrawRegionId = { + enter: () => this._enterDrawBackground(), + exit: () => this._exitDrawBackground() + }; + + /** @type {Array.} */ + this._snapshotCallbacks = []; + + /** @type {Array} */ + // Don't set this directly-- use setBackgroundColor so it stays in sync with _backgroundColor3b + this._backgroundColor4f = [0, 0, 0, 1]; + + /** @type {Uint8ClampedArray} */ + // Don't set this directly-- use setBackgroundColor so it stays in sync with _backgroundColor4f + this._backgroundColor3b = new Uint8ClampedArray(3); + + this._createGeometry(); + + this.on(RenderConstants.Events.NativeSizeChanged, this.onNativeSizeChanged); + + this.setBackgroundColor(1, 1, 1); + this.setStageSize(xLeft || -240, xRight || 240, yBottom || -180, yTop || 180); + this.resize(this._nativeSize[0], this._nativeSize[1]); + + gl.disable(gl.DEPTH_TEST); + /** @todo disable when no partial transparency? */ + gl.enable(gl.BLEND); + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + } + + /** + * @returns {WebGLRenderingContext} the WebGL rendering context associated with this renderer. + */ + get gl () { + return this._gl; + } + + /** + * @returns {HTMLCanvasElement} the canvas of the WebGL rendering context associated with this renderer. + */ + get canvas () { + return this._gl && this._gl.canvas; + } + + /** + * Set the physical size of the stage in device-independent pixels. + * This will be multiplied by the device's pixel ratio on high-DPI displays. + * @param {int} pixelsWide The desired width in device-independent pixels. + * @param {int} pixelsTall The desired height in device-independent pixels. + */ + resize (pixelsWide, pixelsTall) { + const {canvas} = this._gl; + const pixelRatio = window.devicePixelRatio || 1; + const newWidth = pixelsWide * pixelRatio; + const newHeight = pixelsTall * pixelRatio; + + // Certain operations, such as moving the color picker, call `resize` once per frame, even though the canvas + // size doesn't change. To avoid unnecessary canvas updates, check that we *really* need to resize the canvas. + if (canvas.width !== newWidth || canvas.height !== newHeight) { + canvas.width = newWidth; + canvas.height = newHeight; + // Resizing the canvas causes it to be cleared, so redraw it. + this.draw(); + } + + } + + /** + * Set the background color for the stage. The stage will be cleared with this + * color each frame. + * @param {number} red The red component for the background. + * @param {number} green The green component for the background. + * @param {number} blue The blue component for the background. + */ + setBackgroundColor (red, green, blue) { + this._backgroundColor4f[0] = red; + this._backgroundColor4f[1] = green; + this._backgroundColor4f[2] = blue; + + this._backgroundColor3b[0] = red * 255; + this._backgroundColor3b[1] = green * 255; + this._backgroundColor3b[2] = blue * 255; + + } + + /** + * Tell the renderer to draw various debug information to the provided canvas + * during certain operations. + * @param {canvas} canvas The canvas to use for debug output. + */ + setDebugCanvas (canvas) { + this._debugCanvas = canvas; + } + + /** + * Control the use of the GPU or CPU paths in `isTouchingColor`. + * @param {RenderWebGL.UseGpuModes} useGpuMode - automatically decide, force CPU, or force GPU. + */ + setUseGpuMode (useGpuMode) { + this._useGpuMode = useGpuMode; + } + + /** + * Set logical size of the stage in Scratch units. + * @param {int} xLeft The left edge's x-coordinate. Scratch 2 uses -240. + * @param {int} xRight The right edge's x-coordinate. Scratch 2 uses 240. + * @param {int} yBottom The bottom edge's y-coordinate. Scratch 2 uses -180. + * @param {int} yTop The top edge's y-coordinate. Scratch 2 uses 180. + */ + setStageSize (xLeft, xRight, yBottom, yTop) { + this._xLeft = xLeft; + this._xRight = xRight; + this._yBottom = yBottom; + this._yTop = yTop; + + // swap yBottom & yTop to fit Scratch convention of +y=up + this._projection = twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1); + + this._setNativeSize(Math.abs(xRight - xLeft), Math.abs(yBottom - yTop)); + } + + /** + * @return {Array} the "native" size of the stage, which is used for pen, query renders, etc. + */ + getNativeSize () { + return [this._nativeSize[0], this._nativeSize[1]]; + } + + /** + * Set the "native" size of the stage, which is used for pen, query renders, etc. + * @param {int} width - the new width to set. + * @param {int} height - the new height to set. + * @private + * @fires RenderWebGL#event:NativeSizeChanged + */ + _setNativeSize (width, height) { + this._nativeSize = [width, height]; + this.emit(RenderConstants.Events.NativeSizeChanged, {newSize: this._nativeSize}); + } + + /** + * Create a new bitmap skin from a snapshot of the provided bitmap data. + * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin. + * @param {!int} [costumeResolution=1] - The resolution to use for this bitmap. + * @param {?Array} [rotationCenter] Optional: rotation center of the skin. If not supplied, the center of + * the skin will be used. + * @returns {!int} the ID for the new skin. + */ + createBitmapSkin (bitmapData, costumeResolution, rotationCenter) { + const skinId = this._nextSkinId++; + const newSkin = new BitmapSkin(skinId, this); + newSkin.setBitmap(bitmapData, costumeResolution, rotationCenter); + this._allSkins[skinId] = newSkin; + return skinId; + } + + /** + * Create a new SVG skin. + * @param {!string} svgData - new SVG to use. + * @param {?Array} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the + * skin will be used + * @returns {!int} the ID for the new skin. + */ + createSVGSkin (svgData, rotationCenter) { + const skinId = this._nextSkinId++; + const newSkin = new SVGSkin(skinId, this); + newSkin.setSVG(svgData, rotationCenter); + this._allSkins[skinId] = newSkin; + return skinId; + } + + /** + * Create a new PenSkin - a skin which implements a Scratch pen layer. + * @returns {!int} the ID for the new skin. + */ + createPenSkin () { + const skinId = this._nextSkinId++; + const newSkin = new PenSkin(skinId, this); + this._allSkins[skinId] = newSkin; + return skinId; + } + + /** + * Create a new SVG skin using the text bubble svg creator. The rotation center + * is always placed at the top left. + * @param {!string} type - either "say" or "think". + * @param {!string} text - the text for the bubble. + * @param {!boolean} pointsLeft - which side the bubble is pointing. + * @returns {!int} the ID for the new skin. + */ + createTextSkin (type, text, pointsLeft) { + const skinId = this._nextSkinId++; + const newSkin = new TextBubbleSkin(skinId, this); + newSkin.setTextBubble(type, text, pointsLeft); + this._allSkins[skinId] = newSkin; + return skinId; + } + + /** + * Update an existing SVG skin, or create an SVG skin if the previous skin was not SVG. + * @param {!int} skinId the ID for the skin to change. + * @param {!string} svgData - new SVG to use. + * @param {?Array} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the + * skin will be used + */ + updateSVGSkin (skinId, svgData, rotationCenter) { + if (this._allSkins[skinId] instanceof SVGSkin) { + this._allSkins[skinId].setSVG(svgData, rotationCenter); + return; + } + + const newSkin = new SVGSkin(skinId, this); + newSkin.setSVG(svgData, rotationCenter); + this._reskin(skinId, newSkin); + } + + /** + * Update an existing bitmap skin, or create a bitmap skin if the previous skin was not bitmap. + * @param {!int} skinId the ID for the skin to change. + * @param {!ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} imgData - new contents for this skin. + * @param {!number} bitmapResolution - the resolution scale for a bitmap costume. + * @param {?Array} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the + * skin will be used + */ + updateBitmapSkin (skinId, imgData, bitmapResolution, rotationCenter) { + if (this._allSkins[skinId] instanceof BitmapSkin) { + this._allSkins[skinId].setBitmap(imgData, bitmapResolution, rotationCenter); + return; + } + + const newSkin = new BitmapSkin(skinId, this); + newSkin.setBitmap(imgData, bitmapResolution, rotationCenter); + this._reskin(skinId, newSkin); + } + + _reskin (skinId, newSkin) { + const oldSkin = this._allSkins[skinId]; + this._allSkins[skinId] = newSkin; + + // Tell drawables to update + for (const drawable of this._allDrawables) { + if (drawable && drawable.skin === oldSkin) { + drawable.skin = newSkin; + } + } + oldSkin.dispose(); + } + + /** + * Update a skin using the text bubble svg creator. + * @param {!int} skinId the ID for the skin to change. + * @param {!string} type - either "say" or "think". + * @param {!string} text - the text for the bubble. + * @param {!boolean} pointsLeft - which side the bubble is pointing. + */ + updateTextSkin (skinId, type, text, pointsLeft) { + if (this._allSkins[skinId] instanceof TextBubbleSkin) { + this._allSkins[skinId].setTextBubble(type, text, pointsLeft); + return; + } + + const newSkin = new TextBubbleSkin(skinId, this); + newSkin.setTextBubble(type, text, pointsLeft); + this._reskin(skinId, newSkin); + } + + + /** + * Destroy an existing skin. Do not use the skin or its ID after calling this. + * @param {!int} skinId - The ID of the skin to destroy. + */ + destroySkin (skinId) { + const oldSkin = this._allSkins[skinId]; + oldSkin.dispose(); + delete this._allSkins[skinId]; + } + + /** + * Create a new Drawable and add it to the scene. + * @param {string} group Layer group to add the drawable to + * @returns {int} The ID of the new Drawable. + */ + createDrawable (group) { + if (!group || !Object.prototype.hasOwnProperty.call(this._layerGroups, group)) { + log.warn('Cannot create a drawable without a known layer group'); + return; + } + const drawableID = this._nextDrawableId++; + const drawable = new Drawable(drawableID); + this._allDrawables[drawableID] = drawable; + this._addToDrawList(drawableID, group); + + drawable.skin = null; + + return drawableID; + } + + /** + * Set the layer group ordering for the renderer. + * @param {Array} groupOrdering The ordered array of layer group + * names + */ + setLayerGroupOrdering (groupOrdering) { + this._groupOrdering = groupOrdering; + for (let i = 0; i < this._groupOrdering.length; i++) { + this._layerGroups[this._groupOrdering[i]] = { + groupIndex: i, + drawListOffset: 0 + }; + } + } + + _addToDrawList (drawableID, group) { + const currentLayerGroup = this._layerGroups[group]; + const currentGroupOrderingIndex = currentLayerGroup.groupIndex; + + const drawListOffset = this._endIndexForKnownLayerGroup(currentLayerGroup); + this._drawList.splice(drawListOffset, 0, drawableID); + + this._updateOffsets('add', currentGroupOrderingIndex); + } + + _updateOffsets (updateType, currentGroupOrderingIndex) { + for (let i = currentGroupOrderingIndex + 1; i < this._groupOrdering.length; i++) { + const laterGroupName = this._groupOrdering[i]; + if (updateType === 'add') { + this._layerGroups[laterGroupName].drawListOffset++; + } else if (updateType === 'delete'){ + this._layerGroups[laterGroupName].drawListOffset--; + } + } + } + + get _visibleDrawList () { + return this._drawList.filter(id => this._allDrawables[id]._visible); + } + + // Given a layer group, return the index where it ends (non-inclusive), + // e.g. the returned index does not have a drawable from this layer group in it) + _endIndexForKnownLayerGroup (layerGroup) { + const groupIndex = layerGroup.groupIndex; + if (groupIndex === this._groupOrdering.length - 1) { + return this._drawList.length; + } + return this._layerGroups[this._groupOrdering[groupIndex + 1]].drawListOffset; + } + + /** + * Destroy a Drawable, removing it from the scene. + * @param {int} drawableID The ID of the Drawable to remove. + * @param {string} group Group name that the drawable belongs to + */ + destroyDrawable (drawableID, group) { + if (!group || !Object.prototype.hasOwnProperty.call(this._layerGroups, group)) { + log.warn('Cannot destroy drawable without known layer group.'); + return; + } + const drawable = this._allDrawables[drawableID]; + drawable.dispose(); + delete this._allDrawables[drawableID]; + + const currentLayerGroup = this._layerGroups[group]; + const endIndex = this._endIndexForKnownLayerGroup(currentLayerGroup); + + let index = currentLayerGroup.drawListOffset; + while (index < endIndex) { + if (this._drawList[index] === drawableID) { + break; + } + index++; + } + if (index < endIndex) { + this._drawList.splice(index, 1); + this._updateOffsets('delete', currentLayerGroup.groupIndex); + } else { + log.warn('Could not destroy drawable that could not be found in layer group.'); + return; + } + } + + /** + * Returns the position of the given drawableID in the draw list. This is + * the absolute position irrespective of layer group. + * @param {number} drawableID The drawable ID to find. + * @return {number} The postion of the given drawable ID. + */ + getDrawableOrder (drawableID) { + return this._drawList.indexOf(drawableID); + } + + /** + * Set a drawable's order in the drawable list (effectively, z/layer). + * Can be used to move drawables to absolute positions in the list, + * or relative to their current positions. + * "go back N layers": setDrawableOrder(id, -N, true, 1); (assuming stage at 0). + * "go to back": setDrawableOrder(id, 1); (assuming stage at 0). + * "go to front": setDrawableOrder(id, Infinity); + * @param {int} drawableID ID of Drawable to reorder. + * @param {number} order New absolute order or relative order adjusment. + * @param {string=} group Name of layer group drawable belongs to. + * Reordering will not take place if drawable cannot be found within the bounds + * of the layer group. + * @param {boolean=} optIsRelative If set, `order` refers to a relative change. + * @param {number=} optMin If set, order constrained to be at least `optMin`. + * @return {?number} New order if changed, or null. + */ + setDrawableOrder (drawableID, order, group, optIsRelative, optMin) { + if (!group || !Object.prototype.hasOwnProperty.call(this._layerGroups, group)) { + log.warn('Cannot set the order of a drawable without a known layer group.'); + return; + } + + const currentLayerGroup = this._layerGroups[group]; + const startIndex = currentLayerGroup.drawListOffset; + const endIndex = this._endIndexForKnownLayerGroup(currentLayerGroup); + + let oldIndex = startIndex; + while (oldIndex < endIndex) { + if (this._drawList[oldIndex] === drawableID) { + break; + } + oldIndex++; + } + + if (oldIndex < endIndex) { + // Remove drawable from the list. + if (order === 0) { + return oldIndex; + } + + const _ = this._drawList.splice(oldIndex, 1)[0]; + // Determine new index. + let newIndex = order; + if (optIsRelative) { + newIndex += oldIndex; + } + + const possibleMin = (optMin || 0) + startIndex; + const min = (possibleMin >= startIndex && possibleMin < endIndex) ? possibleMin : startIndex; + newIndex = Math.max(newIndex, min); + + newIndex = Math.min(newIndex, endIndex); + + // Insert at new index. + this._drawList.splice(newIndex, 0, drawableID); + return newIndex; + } + + return null; + } + + /** + * Draw all current drawables and present the frame on the canvas. + */ + draw () { + this._doExitDrawRegion(); + + const gl = this._gl; + + twgl.bindFramebufferInfo(gl, null); + gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); + gl.clearColor(...this._backgroundColor4f); + gl.clear(gl.COLOR_BUFFER_BIT); + + this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, this._projection, { + framebufferWidth: gl.canvas.width, + framebufferHeight: gl.canvas.height + }); + if (this._snapshotCallbacks.length > 0) { + const snapshot = gl.canvas.toDataURL(); + this._snapshotCallbacks.forEach(cb => cb(snapshot)); + this._snapshotCallbacks = []; + } + } + + /** + * Get the precise bounds for a Drawable. + * @param {int} drawableID ID of Drawable to get bounds for. + * @return {object} Bounds for a tight box around the Drawable. + */ + getBounds (drawableID) { + const drawable = this._allDrawables[drawableID]; + // Tell the Drawable about its updated convex hull, if necessary. + if (drawable.needsConvexHullPoints()) { + const points = this._getConvexHullPointsForDrawable(drawableID); + drawable.setConvexHullPoints(points); + } + const bounds = drawable.getFastBounds(); + // In debug mode, draw the bounds. + if (this._debugCanvas) { + const gl = this._gl; + this._debugCanvas.width = gl.canvas.width; + this._debugCanvas.height = gl.canvas.height; + const context = this._debugCanvas.getContext('2d'); + context.drawImage(gl.canvas, 0, 0); + context.strokeStyle = '#FF0000'; + const pr = window.devicePixelRatio; + context.strokeRect( + pr * (bounds.left + (this._nativeSize[0] / 2)), + pr * (-bounds.top + (this._nativeSize[1] / 2)), + pr * (bounds.right - bounds.left), + pr * (-bounds.bottom + bounds.top) + ); + } + return bounds; + } + + /** + * Get the precise bounds for a Drawable around the top slice. + * Used for positioning speech bubbles more closely to the sprite. + * @param {int} drawableID ID of Drawable to get bubble bounds for. + * @return {object} Bounds for a tight box around the Drawable top slice. + */ + getBoundsForBubble (drawableID) { + const drawable = this._allDrawables[drawableID]; + // Tell the Drawable about its updated convex hull, if necessary. + if (drawable.needsConvexHullPoints()) { + const points = this._getConvexHullPointsForDrawable(drawableID); + drawable.setConvexHullPoints(points); + } + const bounds = drawable.getBoundsForBubble(); + // In debug mode, draw the bounds. + if (this._debugCanvas) { + const gl = this._gl; + this._debugCanvas.width = gl.canvas.width; + this._debugCanvas.height = gl.canvas.height; + const context = this._debugCanvas.getContext('2d'); + context.drawImage(gl.canvas, 0, 0); + context.strokeStyle = '#FF0000'; + const pr = window.devicePixelRatio; + context.strokeRect( + pr * (bounds.left + (this._nativeSize[0] / 2)), + pr * (-bounds.top + (this._nativeSize[1] / 2)), + pr * (bounds.right - bounds.left), + pr * (-bounds.bottom + bounds.top) + ); + } + return bounds; + } + + /** + * Get the current skin (costume) size of a Drawable. + * @param {int} drawableID The ID of the Drawable to measure. + * @return {Array} Skin size, width and height. + */ + getCurrentSkinSize (drawableID) { + const drawable = this._allDrawables[drawableID]; + return this.getSkinSize(drawable.skin.id); + } + + /** + * Get the size of a skin by ID. + * @param {int} skinID The ID of the Skin to measure. + * @return {Array} Skin size, width and height. + */ + getSkinSize (skinID) { + const skin = this._allSkins[skinID]; + return skin.size; + } + + /** + * Get the rotation center of a skin by ID. + * @param {int} skinID The ID of the Skin + * @return {Array} The rotationCenterX and rotationCenterY + */ + getSkinRotationCenter (skinID) { + const skin = this._allSkins[skinID]; + return skin.calculateRotationCenter(); + } + + /** + * Check if a particular Drawable is touching a particular color. + * Unlike touching drawable, if the "tester" is invisble, we will still test. + * @param {int} drawableID The ID of the Drawable to check. + * @param {Array} color3b Test if the Drawable is touching this color. + * @param {Array} [mask3b] Optionally mask the check to this part of Drawable. + * @returns {boolean} True iff the Drawable is touching the color. + */ + isTouchingColor (drawableID, color3b, mask3b) { + const candidates = this._candidatesTouching(drawableID, this._visibleDrawList); + + let bounds; + if (colorMatches(color3b, this._backgroundColor3b, 0)) { + // If the color we're checking for is the background color, don't confine the check to + // candidate drawables' bounds--since the background spans the entire stage, we must check + // everything that lies inside the drawable. + bounds = this._touchingBounds(drawableID); + // e.g. empty costume, or off the stage + if (bounds === null) return false; + } else if (candidates.length === 0) { + // If not checking for the background color, we can return early if there are no candidate drawables. + return false; + } else { + bounds = this._candidatesBounds(candidates); + } + + const maxPixelsForCPU = this._getMaxPixelsForCPU(); + + const debugCanvasContext = this._debugCanvas && this._debugCanvas.getContext('2d'); + if (debugCanvasContext) { + this._debugCanvas.width = bounds.width; + this._debugCanvas.height = bounds.height; + } + + // if there are just too many pixels to CPU render efficiently, we need to let readPixels happen + if (bounds.width * bounds.height * (candidates.length + 1) >= maxPixelsForCPU) { + this._isTouchingColorGpuStart(drawableID, candidates.map(({id}) => id).reverse(), bounds, color3b, mask3b); + } + + const drawable = this._allDrawables[drawableID]; + const point = __isTouchingDrawablesPoint; + const color = __touchingColor; + const hasMask = Boolean(mask3b); + + drawable.updateCPURenderAttributes(); + + // Masked drawable ignores ghost effect + const effectMask = ~ShaderManager.EFFECT_INFO.ghost.mask; + + // Scratch Space - +y is top + for (let y = bounds.bottom; y <= bounds.top; y++) { + if (bounds.width * (y - bounds.bottom) * (candidates.length + 1) >= maxPixelsForCPU) { + return this._isTouchingColorGpuFin(bounds, color3b, y - bounds.bottom); + } + for (let x = bounds.left; x <= bounds.right; x++) { + point[1] = y; + point[0] = x; + // if we use a mask, check our sample color... + if (hasMask ? + maskMatches(Drawable.sampleColor4b(point, drawable, color, effectMask), mask3b) : + drawable.isTouching(point)) { + RenderWebGL.sampleColor3b(point, candidates, color); + if (debugCanvasContext) { + debugCanvasContext.fillStyle = `rgb(${color[0]},${color[1]},${color[2]})`; + debugCanvasContext.fillRect(x - bounds.left, bounds.bottom - y, 1, 1); + } + // ...and the target color is drawn at this pixel + if (colorMatches(color, color3b, 0)) { + return true; + } + } + } + } + return false; + } + + _getMaxPixelsForCPU () { + switch (this._useGpuMode) { + case RenderWebGL.UseGpuModes.ForceCPU: + return Infinity; + case RenderWebGL.UseGpuModes.ForceGPU: + return 0; + case RenderWebGL.UseGpuModes.Automatic: + default: + return __cpuTouchingColorPixelCount; + } + } + + _enterDrawBackground () { + const gl = this.gl; + const currentShader = this._shaderManager.getShader(ShaderManager.DRAW_MODE.background, 0); + gl.disable(gl.BLEND); + gl.useProgram(currentShader.program); + twgl.setBuffersAndAttributes(gl, currentShader, this._bufferInfo); + } + + _exitDrawBackground () { + const gl = this.gl; + gl.enable(gl.BLEND); + } + + _isTouchingColorGpuStart (drawableID, candidateIDs, bounds, color3b, mask3b) { + this._doExitDrawRegion(); + + const gl = this._gl; + twgl.bindFramebufferInfo(gl, this._queryBufferInfo); + + // Limit size of viewport to the bounds around the target Drawable, + // and create the projection matrix for the draw. + gl.viewport(0, 0, bounds.width, bounds.height); + const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); + + // Clear the query buffer to fully transparent. This will be the color of pixels that fail the stencil test. + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + + let extraUniforms; + if (mask3b) { + extraUniforms = { + u_colorMask: [mask3b[0] / 255, mask3b[1] / 255, mask3b[2] / 255], + u_colorMaskTolerance: MASK_TOUCHING_COLOR_TOLERANCE / 255 + }; + } + + try { + // Using the stencil buffer, mask out the drawing to either the drawable's alpha channel + // or pixels of the drawable which match the mask color, depending on whether a mask color is given. + // Masked-out pixels will not be checked. + gl.enable(gl.STENCIL_TEST); + gl.stencilFunc(gl.ALWAYS, 1, 1); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); + gl.colorMask(false, false, false, false); + this._drawThese( + [drawableID], + mask3b ? + ShaderManager.DRAW_MODE.colorMask : + ShaderManager.DRAW_MODE.silhouette, + projection, + { + extraUniforms, + ignoreVisibility: true, // Touching color ignores sprite visibility, + effectMask: ~ShaderManager.EFFECT_INFO.ghost.mask + }); + + gl.stencilFunc(gl.EQUAL, 1, 1); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + gl.colorMask(true, true, true, true); + + // Draw the background as a quad. Drawing a background with gl.clear will not mask to the stenciled area. + this.enterDrawRegion(this._backgroundDrawRegionId); + + const uniforms = { + u_backgroundColor: this._backgroundColor4f + }; + + const currentShader = this._shaderManager.getShader(ShaderManager.DRAW_MODE.background, 0); + twgl.setUniforms(currentShader, uniforms); + twgl.drawBufferInfo(gl, this._bufferInfo, gl.TRIANGLES); + + // Draw the candidate drawables on top of the background. + this._drawThese(candidateIDs, ShaderManager.DRAW_MODE.default, projection, + {idFilterFunc: testID => testID !== drawableID} + ); + } finally { + gl.colorMask(true, true, true, true); + gl.disable(gl.STENCIL_TEST); + this._doExitDrawRegion(); + } + } + + _isTouchingColorGpuFin (bounds, color3b, stop) { + const gl = this._gl; + const pixels = new Uint8Array(Math.floor(bounds.width * (bounds.height - stop) * 4)); + gl.readPixels(0, 0, bounds.width, (bounds.height - stop), gl.RGBA, gl.UNSIGNED_BYTE, pixels); + + if (this._debugCanvas) { + this._debugCanvas.width = bounds.width; + this._debugCanvas.height = bounds.height; + const context = this._debugCanvas.getContext('2d'); + const imageData = context.getImageData(0, 0, bounds.width, bounds.height - stop); + imageData.data.set(pixels); + context.putImageData(imageData, 0, 0); + } + + for (let pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) { + // Transparent pixels are masked (either by the drawable's alpha channel or color mask). + if (pixels[pixelBase + 3] !== 0 && colorMatches(color3b, pixels, pixelBase)) { + return true; + } + } + + return false; + } + + /** + * Check if a particular Drawable is touching any in a set of Drawables. + * @param {int} drawableID The ID of the Drawable to check. + * @param {?Array} candidateIDs The Drawable IDs to check, otherwise all visible drawables in the renderer + * @returns {boolean} True if the Drawable is touching one of candidateIDs. + */ + isTouchingDrawables (drawableID, candidateIDs = this._drawList) { + const candidates = this._candidatesTouching(drawableID, + // even if passed an invisible drawable, we will NEVER touch it! + candidateIDs.filter(id => this._allDrawables[id]._visible)); + // if we are invisble we don't touch anything. + if (candidates.length === 0 || !this._allDrawables[drawableID]._visible) { + return false; + } + + // Get the union of all the candidates intersections. + const bounds = this._candidatesBounds(candidates); + + const drawable = this._allDrawables[drawableID]; + const point = __isTouchingDrawablesPoint; + + drawable.updateCPURenderAttributes(); + + // This is an EXTREMELY brute force collision detector, but it is + // still faster than asking the GPU to give us the pixels. + for (let x = bounds.left; x <= bounds.right; x++) { + // Scratch Space - +y is top + point[0] = x; + for (let y = bounds.bottom; y <= bounds.top; y++) { + point[1] = y; + if (drawable.isTouching(point)) { + for (let index = 0; index < candidates.length; index++) { + if (candidates[index].drawable.isTouching(point)) { + return true; + } + } + } + } + } + + return false; + } + + /** + * Convert a client based x/y position on the canvas to a Scratch 3 world space + * Rectangle. This creates recangles with a radius to cover selecting multiple + * scratch pixels with touch / small render areas. + * + * @param {int} centerX The client x coordinate of the picking location. + * @param {int} centerY The client y coordinate of the picking location. + * @param {int} [width] The client width of the touch event (optional). + * @param {int} [height] The client width of the touch event (optional). + * @returns {Rectangle} Scratch world space rectangle, iterate bottom <= top, + * left <= right. + */ + clientSpaceToScratchBounds (centerX, centerY, width = 1, height = 1) { + const gl = this._gl; + + const clientToScratchX = this._nativeSize[0] / gl.canvas.clientWidth; + const clientToScratchY = this._nativeSize[1] / gl.canvas.clientHeight; + + width *= clientToScratchX; + height *= clientToScratchY; + + width = Math.max(1, Math.min(Math.round(width), MAX_TOUCH_SIZE[0])); + height = Math.max(1, Math.min(Math.round(height), MAX_TOUCH_SIZE[1])); + const x = (centerX * clientToScratchX) - ((width - 1) / 2); + // + because scratch y is inverted + const y = (centerY * clientToScratchY) + ((height - 1) / 2); + + const xOfs = (width % 2) ? 0 : -0.5; + // y is offset +0.5 + const yOfs = (height % 2) ? 0 : -0.5; + + const bounds = new Rectangle(); + bounds.initFromBounds(Math.floor(this._xLeft + x + xOfs), Math.floor(this._xLeft + x + xOfs + width - 1), + Math.ceil(this._yTop - y + yOfs), Math.ceil(this._yTop - y + yOfs + height - 1)); + return bounds; + } + + /** + * Determine if the drawable is touching a client based x/y. Helper method for sensing + * touching mouse-pointer. Ignores visibility. + * + * @param {int} drawableID The ID of the drawable to check. + * @param {int} centerX The client x coordinate of the picking location. + * @param {int} centerY The client y coordinate of the picking location. + * @param {int} [touchWidth] The client width of the touch event (optional). + * @param {int} [touchHeight] The client height of the touch event (optional). + * @returns {boolean} If the drawable has any pixels that would draw in the touch area + */ + drawableTouching (drawableID, centerX, centerY, touchWidth, touchHeight) { + const drawable = this._allDrawables[drawableID]; + if (!drawable) { + return false; + } + const bounds = this.clientSpaceToScratchBounds(centerX, centerY, touchWidth, touchHeight); + const worldPos = twgl.v3.create(); + + drawable.updateCPURenderAttributes(); + + for (worldPos[1] = bounds.bottom; worldPos[1] <= bounds.top; worldPos[1]++) { + for (worldPos[0] = bounds.left; worldPos[0] <= bounds.right; worldPos[0]++) { + if (drawable.isTouching(worldPos)) { + return true; + } + } + } + return false; + } + + /** + * Detect which sprite, if any, is at the given location. + * This function will pick all drawables that are visible, unless specific + * candidate drawable IDs are provided. Used for determining what is clicked + * or dragged. Will not select hidden / ghosted sprites. + * + * @param {int} centerX The client x coordinate of the picking location. + * @param {int} centerY The client y coordinate of the picking location. + * @param {int} [touchWidth] The client width of the touch event (optional). + * @param {int} [touchHeight] The client height of the touch event (optional). + * @param {Array} [candidateIDs] The Drawable IDs to pick from, otherwise all visible drawables. + * @returns {int} The ID of the topmost Drawable under the picking location, or + * RenderConstants.ID_NONE if there is no Drawable at that location. + */ + pick (centerX, centerY, touchWidth, touchHeight, candidateIDs) { + const bounds = this.clientSpaceToScratchBounds(centerX, centerY, touchWidth, touchHeight); + if (bounds.left === -Infinity || bounds.bottom === -Infinity) { + return false; + } + + candidateIDs = (candidateIDs || this._drawList).filter(id => { + const drawable = this._allDrawables[id]; + // default pick list ignores visible and ghosted sprites. + if (drawable.getVisible() && drawable.getUniforms().u_ghost !== 0) { + const drawableBounds = drawable.getFastBounds(); + const inRange = bounds.intersects(drawableBounds); + if (!inRange) return false; + + drawable.updateCPURenderAttributes(); + return true; + } + return false; + }); + if (candidateIDs.length === 0) { + return false; + } + + const hits = []; + const worldPos = twgl.v3.create(0, 0, 0); + // Iterate over the scratch pixels and check if any candidate can be + // touched at that point. + for (worldPos[1] = bounds.bottom; worldPos[1] <= bounds.top; worldPos[1]++) { + for (worldPos[0] = bounds.left; worldPos[0] <= bounds.right; worldPos[0]++) { + + // Check candidates in the reverse order they would have been + // drawn. This will determine what candiate's silhouette pixel + // would have been drawn at the point. + for (let d = candidateIDs.length - 1; d >= 0; d--) { + const id = candidateIDs[d]; + const drawable = this._allDrawables[id]; + if (drawable.isTouching(worldPos)) { + hits[id] = (hits[id] || 0) + 1; + break; + } + } + } + } + + // Bias toward selecting anything over nothing + hits[RenderConstants.ID_NONE] = 0; + + let hit = RenderConstants.ID_NONE; + for (const hitID in hits) { + if (Object.prototype.hasOwnProperty.call(hits, hitID) && (hits[hitID] > hits[hit])) { + hit = hitID; + } + } + + return Number(hit); + } + + /** + * @typedef DrawableExtraction + * @property {ImageData} data Raw pixel data for the drawable + * @property {number} x The x coordinate of the drawable's bounding box's top-left corner, in 'CSS pixels' + * @property {number} y The y coordinate of the drawable's bounding box's top-left corner, in 'CSS pixels' + * @property {number} width The drawable's bounding box width, in 'CSS pixels' + * @property {number} height The drawable's bounding box height, in 'CSS pixels' + */ + + /** + * Return a drawable's pixel data and bounds in screen space. + * @param {int} drawableID The ID of the drawable to get pixel data for + * @return {DrawableExtraction} Data about the picked drawable + */ + extractDrawableScreenSpace (drawableID) { + const drawable = this._allDrawables[drawableID]; + if (!drawable) throw new Error(`Could not extract drawable with ID ${drawableID}; it does not exist`); + + this._doExitDrawRegion(); + + const nativeCenterX = this._nativeSize[0] * 0.5; + const nativeCenterY = this._nativeSize[1] * 0.5; + + const scratchBounds = drawable.getFastBounds(); + + const canvas = this.canvas; + // Ratio of the screen-space scale of the stage's canvas to the "native size" of the stage + const scaleFactor = canvas.width / this._nativeSize[0]; + + // Bounds of the extracted drawable, in "canvas pixel space" + // (origin is 0, 0, destination is the canvas width, height). + const canvasSpaceBounds = new Rectangle(); + canvasSpaceBounds.initFromBounds( + (scratchBounds.left + nativeCenterX) * scaleFactor, + (scratchBounds.right + nativeCenterX) * scaleFactor, + // in "canvas space", +y is down, but Rectangle methods assume bottom < top, so swap them + (nativeCenterY - scratchBounds.top) * scaleFactor, + (nativeCenterY - scratchBounds.bottom) * scaleFactor + ); + canvasSpaceBounds.snapToInt(); + + // undo the transformation to transform the bounds, snapped to "canvas-pixel space", back to "Scratch space" + // We have to transform -> snap -> invert transform so that the "Scratch-space" bounds are snapped in + // "canvas-pixel space". + scratchBounds.initFromBounds( + (canvasSpaceBounds.left / scaleFactor) - nativeCenterX, + (canvasSpaceBounds.right / scaleFactor) - nativeCenterX, + nativeCenterY - (canvasSpaceBounds.top / scaleFactor), + nativeCenterY - (canvasSpaceBounds.bottom / scaleFactor) + ); + + const gl = this._gl; + + // Set a reasonable max limit width and height for the bufferInfo bounds + const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); + const clampedWidth = Math.min(MAX_EXTRACTED_DRAWABLE_DIMENSION, canvasSpaceBounds.width, maxTextureSize); + const clampedHeight = Math.min(MAX_EXTRACTED_DRAWABLE_DIMENSION, canvasSpaceBounds.height, maxTextureSize); + + // Make a new bufferInfo since this._queryBufferInfo is limited to 480x360 + const bufferInfo = twgl.createFramebufferInfo(gl, [{format: gl.RGBA}], clampedWidth, clampedHeight); + + try { + twgl.bindFramebufferInfo(gl, bufferInfo); + + // Limit size of viewport to the bounds around the target Drawable, + // and create the projection matrix for the draw. + gl.viewport(0, 0, clampedWidth, clampedHeight); + const projection = twgl.m4.ortho( + scratchBounds.left, + scratchBounds.right, + scratchBounds.top, + scratchBounds.bottom, + -1, 1 + ); + + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + this._drawThese([drawableID], ShaderManager.DRAW_MODE.straightAlpha, projection, + { + // Don't apply the ghost effect. TODO: is this an intentional design decision? + effectMask: ~ShaderManager.EFFECT_INFO.ghost.mask, + // We're doing this in screen-space, so the framebuffer dimensions should be those of the canvas in + // screen-space. This is used to ensure SVG skins are rendered at the proper resolution. + framebufferWidth: canvas.width, + framebufferHeight: canvas.height + }); + + const data = new Uint8Array(Math.floor(clampedWidth * clampedHeight * 4)); + gl.readPixels(0, 0, clampedWidth, clampedHeight, gl.RGBA, gl.UNSIGNED_BYTE, data); + // readPixels can only read into a Uint8Array, but ImageData has to take a Uint8ClampedArray. + // We can share the same underlying buffer between them to avoid having to copy any data. + const imageData = new ImageData(new Uint8ClampedArray(data.buffer), clampedWidth, clampedHeight); + + // On high-DPI devices, the canvas' width (in canvas pixels) will be larger than its width in CSS pixels. + // We want to return the CSS-space bounds, + // so take into account the ratio between the canvas' pixel dimensions and its layout dimensions. + // This is usually the same as 1 / window.devicePixelRatio, but if e.g. you zoom your browser window without + // the canvas resizing, then it'll differ. + const ratio = canvas.getBoundingClientRect().width / canvas.width; + + return { + imageData, + x: canvasSpaceBounds.left * ratio, + y: canvasSpaceBounds.bottom * ratio, + width: canvasSpaceBounds.width * ratio, + height: canvasSpaceBounds.height * ratio + }; + } finally { + gl.deleteFramebuffer(bufferInfo.framebuffer); + } + } + + /** + * @typedef ColorExtraction + * @property {Uint8Array} data Raw pixel data for the drawable + * @property {int} width Drawable bounding box width + * @property {int} height Drawable bounding box height + * @property {object} color Color object with RGBA properties at picked location + */ + + /** + * Return drawable pixel data and color at a given position + * @param {int} x The client x coordinate of the picking location. + * @param {int} y The client y coordinate of the picking location. + * @param {int} radius The client radius to extract pixels with. + * @return {?ColorExtraction} Data about the picked color + */ + extractColor (x, y, radius) { + this._doExitDrawRegion(); + + const scratchX = Math.round(this._nativeSize[0] * ((x / this._gl.canvas.clientWidth) - 0.5)); + const scratchY = Math.round(-this._nativeSize[1] * ((y / this._gl.canvas.clientHeight) - 0.5)); + + const gl = this._gl; + twgl.bindFramebufferInfo(gl, this._queryBufferInfo); + + const bounds = new Rectangle(); + bounds.initFromBounds(scratchX - radius, scratchX + radius, scratchY - radius, scratchY + radius); + + const pickX = scratchX - bounds.left; + const pickY = bounds.top - scratchY; + + gl.viewport(0, 0, bounds.width, bounds.height); + const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); + + gl.clearColor(...this._backgroundColor4f); + gl.clear(gl.COLOR_BUFFER_BIT); + this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, projection); + + const data = new Uint8Array(Math.floor(bounds.width * bounds.height * 4)); + gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, data); + + const pixelBase = Math.floor(4 * ((pickY * bounds.width) + pickX)); + const color = { + r: data[pixelBase], + g: data[pixelBase + 1], + b: data[pixelBase + 2], + a: data[pixelBase + 3] + }; + + if (this._debugCanvas) { + this._debugCanvas.width = bounds.width; + this._debugCanvas.height = bounds.height; + const ctx = this._debugCanvas.getContext('2d'); + const imageData = ctx.createImageData(bounds.width, bounds.height); + imageData.data.set(data); + ctx.putImageData(imageData, 0, 0); + ctx.strokeStyle = 'black'; + ctx.fillStyle = `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`; + ctx.rect(pickX - 4, pickY - 4, 8, 8); + ctx.fill(); + ctx.stroke(); + } + + return { + data: data, + width: bounds.width, + height: bounds.height, + color: color + }; + } + + /** + * Get the candidate bounding box for a touching query. + * @param {int} drawableID ID for drawable of query. + * @return {?Rectangle} Rectangle bounds for touching query, or null. + */ + _touchingBounds (drawableID) { + const drawable = this._allDrawables[drawableID]; + + /** @todo remove this once URL-based skin setting is removed. */ + if (!drawable.skin || !drawable.skin.getTexture([100, 100])) return null; + + const bounds = drawable.getFastBounds(); + + // Limit queries to the stage size. + bounds.clamp(this._xLeft, this._xRight, this._yBottom, this._yTop); + + // Use integer coordinates for queries - weird things happen + // when you provide float width/heights to gl.viewport and projection. + bounds.snapToInt(); + + if (bounds.width === 0 || bounds.height === 0) { + // No space to query. + return null; + } + return bounds; + } + + /** + * Filter a list of candidates for a touching query into only those that + * could possibly intersect the given bounds. + * @param {int} drawableID - ID for drawable of query. + * @param {Array} candidateIDs - Candidates for touching query. + * @return {?Array< {id, drawable, intersection} >} Filtered candidates with useful data. + */ + _candidatesTouching (drawableID, candidateIDs) { + const bounds = this._touchingBounds(drawableID); + const result = []; + if (bounds === null) { + return result; + } + // iterate through the drawables list BACKWARDS - we want the top most item to be the first we check + for (let index = candidateIDs.length - 1; index >= 0; index--) { + const id = candidateIDs[index]; + if (id !== drawableID) { + const drawable = this._allDrawables[id]; + // Text bubbles aren't considered in "touching" queries + if (drawable.skin instanceof TextBubbleSkin) continue; + if (drawable.skin && drawable._visible) { + // Update the CPU position data + drawable.updateCPURenderAttributes(); + const candidateBounds = drawable.getFastBounds(); + + // Push bounds out to integers. If a drawable extends out into half a pixel, that half-pixel still + // needs to be tested. Plus, in some areas we construct another rectangle from the union of these, + // and iterate over its pixels (width * height). Turns out that doesn't work so well when the + // width/height aren't integers. + candidateBounds.snapToInt(); + + if (bounds.intersects(candidateBounds)) { + result.push({ + id, + drawable, + intersection: Rectangle.intersect(bounds, candidateBounds) + }); + } + } + } + } + return result; + } + + /** + * Helper to get the union bounds from a set of candidates returned from the above method + * @private + * @param {Array} candidates info from _candidatesTouching + * @return {Rectangle} the outer bounding box union + */ + _candidatesBounds (candidates) { + return candidates.reduce((memo, {intersection}) => { + if (!memo) { + return intersection; + } + // store the union of the two rectangles in our static rectangle instance + return Rectangle.union(memo, intersection, __candidatesBounds); + }, null); + } + + /** + * Update a drawable's skin. + * @param {number} drawableID The drawable's id. + * @param {number} skinId The skin to update to. + */ + updateDrawableSkinId (drawableID, skinId) { + const drawable = this._allDrawables[drawableID]; + // TODO: https://github.com/LLK/scratch-vm/issues/2288 + if (!drawable) return; + drawable.skin = this._allSkins[skinId]; + } + + /** + * Update a drawable's position. + * @param {number} drawableID The drawable's id. + * @param {Array.} position The new position. + */ + updateDrawablePosition (drawableID, position) { + const drawable = this._allDrawables[drawableID]; + // TODO: https://github.com/LLK/scratch-vm/issues/2288 + if (!drawable) return; + drawable.updatePosition(position); + } + + /** + * Update a drawable's direction. + * @param {number} drawableID The drawable's id. + * @param {number} direction A new direction. + */ + updateDrawableDirection (drawableID, direction) { + const drawable = this._allDrawables[drawableID]; + // TODO: https://github.com/LLK/scratch-vm/issues/2288 + if (!drawable) return; + drawable.updateDirection(direction); + } + + /** + * Update a drawable's scale. + * @param {number} drawableID The drawable's id. + * @param {Array.} scale A new scale. + */ + updateDrawableScale (drawableID, scale) { + const drawable = this._allDrawables[drawableID]; + // TODO: https://github.com/LLK/scratch-vm/issues/2288 + if (!drawable) return; + drawable.updateScale(scale); + } + + /** + * Update a drawable's direction and scale together. + * @param {number} drawableID The drawable's id. + * @param {number} direction A new direction. + * @param {Array.} scale A new scale. + */ + updateDrawableDirectionScale (drawableID, direction, scale) { + const drawable = this._allDrawables[drawableID]; + // TODO: https://github.com/LLK/scratch-vm/issues/2288 + if (!drawable) return; + drawable.updateDirection(direction); + drawable.updateScale(scale); + } + + /** + * Update a drawable's visibility. + * @param {number} drawableID The drawable's id. + * @param {boolean} visible Will the drawable be visible? + */ + updateDrawableVisible (drawableID, visible) { + const drawable = this._allDrawables[drawableID]; + // TODO: https://github.com/LLK/scratch-vm/issues/2288 + if (!drawable) return; + drawable.updateVisible(visible); + } + + /** + * Update a drawable's visual effect. + * @param {number} drawableID The drawable's id. + * @param {string} effectName The effect to change. + * @param {number} value A new effect value. + */ + updateDrawableEffect (drawableID, effectName, value) { + const drawable = this._allDrawables[drawableID]; + // TODO: https://github.com/LLK/scratch-vm/issues/2288 + if (!drawable) return; + drawable.updateEffect(effectName, value); + } + + /** + * Update the position, direction, scale, or effect properties of this Drawable. + * @deprecated Use specific updateDrawable* methods instead. + * @param {int} drawableID The ID of the Drawable to update. + * @param {object.} properties The new property values to set. + */ + updateDrawableProperties (drawableID, properties) { + const drawable = this._allDrawables[drawableID]; + if (!drawable) { + /** + * @todo(https://github.com/LLK/scratch-vm/issues/2288) fix whatever's wrong in the VM which causes this, then add a warning or throw here. + * Right now this happens so much on some projects that a warning or exception here can hang the browser. + */ + return; + } + if ('skinId' in properties) { + this.updateDrawableSkinId(drawableID, properties.skinId); + } + drawable.updateProperties(properties); + } + + /** + * Update the position object's x & y members to keep the drawable fenced in view. + * @param {int} drawableID - The ID of the Drawable to update. + * @param {Array.} position to be fenced - An array of type [x, y] + * @return {Array.} The fenced position as an array [x, y] + */ + getFencedPositionOfDrawable (drawableID, position) { + let x = position[0]; + let y = position[1]; + + const drawable = this._allDrawables[drawableID]; + if (!drawable) { + // @todo(https://github.com/LLK/scratch-vm/issues/2288) fix whatever's wrong in the VM which causes this, then add a warning or throw here. + // Right now this happens so much on some projects that a warning or exception here can hang the browser. + return [x, y]; + } + + const dx = x - drawable._position[0]; + const dy = y - drawable._position[1]; + const aabb = drawable._skin.getFenceBounds(drawable, __fenceBounds); + const inset = Math.floor(Math.min(aabb.width, aabb.height) / 2); + + const sx = this._xRight - Math.min(FENCE_WIDTH, inset); + if (aabb.right + dx < -sx) { + x = Math.ceil(drawable._position[0] - (sx + aabb.right)); + } else if (aabb.left + dx > sx) { + x = Math.floor(drawable._position[0] + (sx - aabb.left)); + } + const sy = this._yTop - Math.min(FENCE_WIDTH, inset); + if (aabb.top + dy < -sy) { + y = Math.ceil(drawable._position[1] - (sy + aabb.top)); + } else if (aabb.bottom + dy > sy) { + y = Math.floor(drawable._position[1] + (sy - aabb.bottom)); + } + return [x, y]; + } + + /** + * Clear a pen layer. + * @param {int} penSkinID - the unique ID of a Pen Skin. + */ + penClear (penSkinID) { + const skin = /** @type {PenSkin} */ this._allSkins[penSkinID]; + skin.clear(); + } + + /** + * Draw a point on a pen layer. + * @param {int} penSkinID - the unique ID of a Pen Skin. + * @param {PenAttributes} penAttributes - how the point should be drawn. + * @param {number} x - the X coordinate of the point to draw. + * @param {number} y - the Y coordinate of the point to draw. + */ + penPoint (penSkinID, penAttributes, x, y) { + const skin = /** @type {PenSkin} */ this._allSkins[penSkinID]; + skin.drawPoint(penAttributes, x, y); + } + + /** + * Draw a line on a pen layer. + * @param {int} penSkinID - the unique ID of a Pen Skin. + * @param {PenAttributes} penAttributes - how the line should be drawn. + * @param {number} x0 - the X coordinate of the beginning of the line. + * @param {number} y0 - the Y coordinate of the beginning of the line. + * @param {number} x1 - the X coordinate of the end of the line. + * @param {number} y1 - the Y coordinate of the end of the line. + */ + penLine (penSkinID, penAttributes, x0, y0, x1, y1) { + const skin = /** @type {PenSkin} */ this._allSkins[penSkinID]; + skin.drawLine(penAttributes, x0, y0, x1, y1); + } + + /** + * Stamp a Drawable onto a pen layer. + * @param {int} penSkinID - the unique ID of a Pen Skin. + * @param {int} stampID - the unique ID of the Drawable to use as the stamp. + */ + penStamp (penSkinID, stampID) { + const stampDrawable = this._allDrawables[stampID]; + if (!stampDrawable) { + return; + } + + const bounds = this._touchingBounds(stampID); + if (!bounds) { + return; + } + + this._doExitDrawRegion(); + + const skin = /** @type {PenSkin} */ this._allSkins[penSkinID]; + + const gl = this._gl; + twgl.bindFramebufferInfo(gl, skin._framebuffer); + + // Limit size of viewport to the bounds around the stamp Drawable and create the projection matrix for the draw. + gl.viewport( + (this._nativeSize[0] * 0.5) + bounds.left, + (this._nativeSize[1] * 0.5) - bounds.top, + bounds.width, + bounds.height + ); + const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); + + // Draw the stamped sprite onto the PenSkin's framebuffer. + this._drawThese([stampID], ShaderManager.DRAW_MODE.default, projection, {ignoreVisibility: true}); + skin._silhouetteDirty = true; + } + + /* ****** + * Truly internal functions: these support the functions above. + ********/ + + /** + * Build geometry (vertex and index) buffers. + * @private + */ + _createGeometry () { + const quad = { + a_position: { + numComponents: 2, + data: [ + -0.5, -0.5, + 0.5, -0.5, + -0.5, 0.5, + -0.5, 0.5, + 0.5, -0.5, + 0.5, 0.5 + ] + }, + a_texCoord: { + numComponents: 2, + data: [ + 1, 0, + 0, 0, + 1, 1, + 1, 1, + 0, 0, + 0, 1 + ] + } + }; + this._bufferInfo = twgl.createBufferInfoFromArrays(this._gl, quad); + } + + /** + * Respond to a change in the "native" rendering size. The native size is used by buffers which are fixed in size + * regardless of the size of the main render target. This includes the buffers used for queries such as picking and + * color-touching. The fixed size allows (more) consistent behavior across devices and presentation modes. + * @param {object} event - The change event. + * @private + */ + onNativeSizeChanged (event) { + const [width, height] = event.newSize; + + const gl = this._gl; + const attachments = [ + {format: gl.RGBA}, + {format: gl.DEPTH_STENCIL} + ]; + + if (!this._pickBufferInfo) { + this._pickBufferInfo = twgl.createFramebufferInfo(gl, attachments, MAX_TOUCH_SIZE[0], MAX_TOUCH_SIZE[1]); + } + + /** @todo should we create this on demand to save memory? */ + // A 480x360 32-bpp buffer is 675 KiB. + if (this._queryBufferInfo) { + twgl.resizeFramebufferInfo(gl, this._queryBufferInfo, attachments, width, height); + } else { + this._queryBufferInfo = twgl.createFramebufferInfo(gl, attachments, width, height); + } + } + + /** + * Enter a draw region. + * + * A draw region is where multiple draw operations are performed with the + * same GL state. WebGL performs poorly when it changes state like blend + * mode. Marking a collection of state values as a "region" the renderer + * can skip superfluous extra state calls when it is already in that + * region. Since one region may be entered from within another a exit + * handle can also be registered that is called when a new region is about + * to be entered to restore a common inbetween state. + * + * @param {any} regionId - id of the region to enter + * @param {function} enter - handle to call when first entering a region + * @param {function} exit - handle to call when leaving a region + */ + enterDrawRegion (regionId, enter = regionId.enter, exit = regionId.exit) { + if (this._regionId !== regionId) { + this._doExitDrawRegion(); + this._regionId = regionId; + enter(); + this._exitRegion = exit; + } + } + + /** + * Forcefully exit the current region returning to a common inbetween GL + * state. + */ + _doExitDrawRegion () { + if (this._exitRegion !== null) { + this._exitRegion(); + } + this._exitRegion = null; + this._regionId = null; + } + + /** + * Draw a set of Drawables, by drawable ID + * @param {Array} drawables The Drawable IDs to draw, possibly this._drawList. + * @param {ShaderManager.DRAW_MODE} drawMode Draw normally, silhouette, etc. + * @param {module:twgl/m4.Mat4} projection The projection matrix to use. + * @param {object} [opts] Options for drawing + * @param {idFilterFunc} opts.filter An optional filter function. + * @param {object.} opts.extraUniforms Extra uniforms for the shaders. + * @param {int} opts.effectMask Bitmask for effects to allow + * @param {boolean} opts.ignoreVisibility Draw all, despite visibility (e.g. stamping, touching color) + * @param {int} opts.framebufferWidth The width of the framebuffer being drawn onto. Defaults to "native" width + * @param {int} opts.framebufferHeight The height of the framebuffer being drawn onto. Defaults to "native" height + * @private + */ + _drawThese (drawables, drawMode, projection, opts = {}) { + + const gl = this._gl; + let currentShader = null; + + const framebufferSpaceScaleDiffers = ( + 'framebufferWidth' in opts && 'framebufferHeight' in opts && + opts.framebufferWidth !== this._nativeSize[0] && opts.framebufferHeight !== this._nativeSize[1] + ); + + const numDrawables = drawables.length; + for (let drawableIndex = 0; drawableIndex < numDrawables; ++drawableIndex) { + const drawableID = drawables[drawableIndex]; + + // If we have a filter, check whether the ID fails + if (opts.filter && !opts.filter(drawableID)) continue; + + const drawable = this._allDrawables[drawableID]; + /** @todo check if drawable is inside the viewport before anything else */ + + // Hidden drawables (e.g., by a "hide" block) are not drawn unless + // the ignoreVisibility flag is used (e.g. for stamping or touchingColor). + if (!drawable.getVisible() && !opts.ignoreVisibility) continue; + + // drawableScale is the "framebuffer-pixel-space" scale of the drawable, as percentages of the drawable's + // "native size" (so 100 = same as skin's "native size", 200 = twice "native size"). + // If the framebuffer dimensions are the same as the stage's "native" size, there's no need to calculate it. + const drawableScale = framebufferSpaceScaleDiffers ? [ + drawable.scale[0] * opts.framebufferWidth / this._nativeSize[0], + drawable.scale[1] * opts.framebufferHeight / this._nativeSize[1] + ] : drawable.scale; + + // If the skin or texture isn't ready yet, skip it. + if (!drawable.skin || !drawable.skin.getTexture(drawableScale)) continue; + + const uniforms = {}; + + let effectBits = drawable.enabledEffects; + effectBits &= Object.prototype.hasOwnProperty.call(opts, 'effectMask') ? opts.effectMask : effectBits; + const newShader = this._shaderManager.getShader(drawMode, effectBits); + + // Manually perform region check. Do not create functions inside a + // loop. + if (this._regionId !== newShader) { + this._doExitDrawRegion(); + this._regionId = newShader; + + currentShader = newShader; + gl.useProgram(currentShader.program); + twgl.setBuffersAndAttributes(gl, currentShader, this._bufferInfo); + Object.assign(uniforms, { + u_projectionMatrix: projection + }); + } + + Object.assign(uniforms, + drawable.skin.getUniforms(drawableScale), + drawable.getUniforms()); + + // Apply extra uniforms after the Drawable's, to allow overwriting. + if (opts.extraUniforms) { + Object.assign(uniforms, opts.extraUniforms); + } + + if (uniforms.u_skin) { + twgl.setTextureParameters( + gl, uniforms.u_skin, { + minMag: drawable.skin.useNearest(drawableScale, drawable) ? gl.NEAREST : gl.LINEAR + } + ); + } + + twgl.setUniforms(currentShader, uniforms); + twgl.drawBufferInfo(gl, this._bufferInfo, gl.TRIANGLES); + } + + this._regionId = null; + } + + /** + * Get the convex hull points for a particular Drawable. + * To do this, calculate it based on the drawable's Silhouette. + * @param {int} drawableID The Drawable IDs calculate convex hull for. + * @return {Array>} points Convex hull points, as [[x, y], ...] + */ + _getConvexHullPointsForDrawable (drawableID) { + const drawable = this._allDrawables[drawableID]; + + const [width, height] = drawable.skin.size; + // No points in the hull if invisible or size is 0. + if (!drawable.getVisible() || width === 0 || height === 0) { + return []; + } + + drawable.updateCPURenderAttributes(); + + /** + * Return the determinant of two vectors, the vector from A to B and the vector from A to C. + * + * The determinant is useful in this case to know if AC is counter-clockwise from AB. + * A positive value means that AC is counter-clockwise from AB. A negative value means AC is clockwise from AB. + * + * @param {Float32Array} A A 2d vector in space. + * @param {Float32Array} B A 2d vector in space. + * @param {Float32Array} C A 2d vector in space. + * @return {number} Greater than 0 if counter clockwise, less than if clockwise, 0 if all points are on a line. + */ + const determinant = function (A, B, C) { + // AB = B - A + // AC = C - A + // det (AB BC) = AB0 * AC1 - AB1 * AC0 + return (((B[0] - A[0]) * (C[1] - A[1])) - ((B[1] - A[1]) * (C[0] - A[0]))); + }; + + // This algorithm for calculating the convex hull somewhat resembles the monotone chain algorithm. + // The main difference is that instead of sorting the points by x-coordinate, and y-coordinate in case of ties, + // it goes through them by y-coordinate in the outer loop and x-coordinate in the inner loop. + // This gives us "left" and "right" hulls, whereas the monotone chain algorithm gives "top" and "bottom" hulls. + // Adapted from https://github.com/LLK/scratch-flash/blob/dcbeeb59d44c3be911545dfe54d46a32404f8e69/src/scratch/ScratchCostume.as#L369-L413 + + const leftHull = []; + const rightHull = []; + + // While convex hull algorithms usually push and pop values from the list of hull points, + // here, we keep indices for the "last" point in each array. Any points past these indices are ignored. + // This is functionally equivalent to pushing and popping from a "stack" of hull points. + let leftEndPointIndex = -1; + let rightEndPointIndex = -1; + + const _pixelPos = twgl.v3.create(); + const _effectPos = twgl.v3.create(); + + let currentPoint; + + // *Not* Scratch Space-- +y is bottom + // Loop over all rows of pixels, starting at the top + for (let y = 0; y < height; y++) { + _pixelPos[1] = y / height; + + // We start at the leftmost point, then go rightwards until we hit an opaque pixel + let x = 0; + for (; x < width; x++) { + _pixelPos[0] = x / width; + EffectTransform.transformPoint(drawable, _pixelPos, _effectPos); + if (drawable.skin.isTouchingLinear(_effectPos)) { + currentPoint = [x, y]; + break; + } + } + + // If we managed to loop all the way through, there are no opaque pixels on this row. Go to the next one + if (x >= width) { + continue; + } + + // Because leftEndPointIndex is initialized to -1, this is skipped for the first two rows. + // It runs only when there are enough points in the left hull to make at least one line. + // If appending the current point to the left hull makes a counter-clockwise turn, + // we want to append the current point. Otherwise, we decrement the index of the "last" hull point until the + // current point makes a counter-clockwise turn. + // This decrementing has the same effect as popping from the point list, but is hopefully faster. + while (leftEndPointIndex > 0) { + if (determinant(leftHull[leftEndPointIndex], leftHull[leftEndPointIndex - 1], currentPoint) > 0) { + break; + } else { + // leftHull.pop(); + --leftEndPointIndex; + } + } + + // This has the same effect as pushing to the point list. + // This "list head pointer" coding style leaves excess points dangling at the end of the list, + // but that doesn't matter; we simply won't copy them over to the final hull. + + // leftHull.push(currentPoint); + leftHull[++leftEndPointIndex] = currentPoint; + + // Now we repeat the process for the right side, looking leftwards for a pixel. + for (x = width - 1; x >= 0; x--) { + _pixelPos[0] = x / width; + EffectTransform.transformPoint(drawable, _pixelPos, _effectPos); + if (drawable.skin.isTouchingLinear(_effectPos)) { + currentPoint = [x, y]; + break; + } + } + + // Because we're coming at this from the right, it goes clockwise this time. + while (rightEndPointIndex > 0) { + if (determinant(rightHull[rightEndPointIndex], rightHull[rightEndPointIndex - 1], currentPoint) < 0) { + break; + } else { + --rightEndPointIndex; + } + } + + rightHull[++rightEndPointIndex] = currentPoint; + } + + // Start off "hullPoints" with the left hull points. + const hullPoints = leftHull; + // This is where we get rid of those dangling extra points. + hullPoints.length = leftEndPointIndex + 1; + // Add points from the right side in reverse order so all points are ordered clockwise. + for (let j = rightEndPointIndex; j >= 0; --j) { + hullPoints.push(rightHull[j]); + } + + // Simplify boundary points using hull.js. + // TODO: Remove this; this algorithm already generates convex hulls. + return hull(hullPoints, Infinity); + } + + /** + * Sample a "final" color from an array of drawables at a given scratch space. + * Will blend any alpha values with the drawables "below" it. + * @param {twgl.v3} vec Scratch Vector Space to sample + * @param {Array} drawables A list of drawables with the "top most" + * drawable at index 0 + * @param {Uint8ClampedArray} dst The color3b space to store the answer in. + * @return {Uint8ClampedArray} The dst vector with everything blended down. + */ + static sampleColor3b (vec, drawables, dst) { + dst = dst || new Uint8ClampedArray(3); + dst.fill(0); + let blendAlpha = 1; + for (let index = 0; blendAlpha !== 0 && index < drawables.length; index++) { + /* + if (left > vec[0] || right < vec[0] || + bottom > vec[1] || top < vec[0]) { + continue; + } + */ + Drawable.sampleColor4b(vec, drawables[index].drawable, __blendColor); + // Equivalent to gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) + dst[0] += __blendColor[0] * blendAlpha; + dst[1] += __blendColor[1] * blendAlpha; + dst[2] += __blendColor[2] * blendAlpha; + blendAlpha *= (1 - (__blendColor[3] / 255)); + } + // Backdrop could be transparent, so we need to go to the "clear color" of the + // draw scene (white) as a fallback if everything was alpha + dst[0] += blendAlpha * 255; + dst[1] += blendAlpha * 255; + dst[2] += blendAlpha * 255; + return dst; + } + + /** + * @callback RenderWebGL#snapshotCallback + * @param {string} dataURI Data URI of the snapshot of the renderer + */ + + /** + * @param {snapshotCallback} callback Function called in the next frame with the snapshot data + */ + requestSnapshot (callback) { + this._snapshotCallbacks.push(callback); + } +} + +// :3 +RenderWebGL.prototype.canHazPixels = RenderWebGL.prototype.extractDrawableScreenSpace; + +/** + * Values for setUseGPU() + * @enum {string} + */ +RenderWebGL.UseGpuModes = { + /** + * Heuristically decide whether to use the GPU path, the CPU path, or a dynamic mixture of the two. + */ + Automatic: 'Automatic', + + /** + * Always use the GPU path. + */ + ForceGPU: 'ForceGPU', + + /** + * Always use the CPU path. + */ + ForceCPU: 'ForceCPU' +}; + +module.exports = RenderWebGL; diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js new file mode 100644 index 0000000000..f35a00466e --- /dev/null +++ b/packages/scratch-render/src/SVGSkin.js @@ -0,0 +1,239 @@ +const twgl = require('twgl.js'); + +const Skin = require('./Skin'); +const {loadSvgString, serializeSvgToString} = require('scratch-svg-renderer'); +const ShaderManager = require('./ShaderManager'); + +const MAX_TEXTURE_DIMENSION = 2048; + +/** + * All scaled renderings of the SVG are stored in an array. The 1.0 scale of + * the SVG is stored at the 8th index. The smallest possible 1 / 256 scale + * rendering is stored at the 0th index. + * @const {number} + */ +const INDEX_OFFSET = 8; + +class SVGSkin extends Skin { + /** + * Create a new SVG skin. + * @param {!int} id - The ID for this Skin. + * @param {!RenderWebGL} renderer - The renderer which will use this skin. + * @constructor + * @extends Skin + */ + constructor (id, renderer) { + super(id); + + /** @type {RenderWebGL} */ + this._renderer = renderer; + + /** @type {HTMLImageElement} */ + this._svgImage = document.createElement('img'); + + /** @type {boolean} */ + this._svgImageLoaded = false; + + /** @type {Array} */ + this._size = [0, 0]; + + /** @type {HTMLCanvasElement} */ + this._canvas = document.createElement('canvas'); + + /** @type {CanvasRenderingContext2D} */ + this._context = this._canvas.getContext('2d'); + + /** @type {Array} */ + this._scaledMIPs = []; + + /** @type {number} */ + this._largestMIPScale = 0; + + /** + * Ratio of the size of the SVG and the max size of the WebGL texture + * @type {Number} + */ + this._maxTextureScale = 1; + } + + /** + * Dispose of this object. Do not use it after calling this method. + */ + dispose () { + this.resetMIPs(); + super.dispose(); + } + + /** + * @return {Array} the natural size, in Scratch units, of this skin. + */ + get size () { + return [this._size[0], this._size[1]]; + } + + useNearest (scale, drawable) { + // If the effect bits for mosaic, pixelate, whirl, or fisheye are set, use linear + if ((drawable.enabledEffects & ( + ShaderManager.EFFECT_INFO.fisheye.mask | + ShaderManager.EFFECT_INFO.whirl.mask | + ShaderManager.EFFECT_INFO.pixelate.mask | + ShaderManager.EFFECT_INFO.mosaic.mask + )) !== 0) { + return false; + } + + // We can't use nearest neighbor unless we are a multiple of 90 rotation + if (drawable._direction % 90 !== 0) { + return false; + } + + // Because SVG skins' bounding boxes are currently not pixel-aligned, the idea here is to hide blurriness + // by using nearest-neighbor scaling if one screen-space pixel is "close enough" to one texture pixel. + // If the scale of the skin is very close to 100 (0.99999 variance is okay I guess) + // TODO: Make this check more precise. We should use nearest if there's less than one pixel's difference + // between the screen-space and texture-space sizes of the skin. Mipmaps make this harder because there are + // multiple textures (and hence multiple texture spaces) and we need to know which one to choose. + if (Math.abs(scale[0]) > 99 && Math.abs(scale[0]) < 101 && + Math.abs(scale[1]) > 99 && Math.abs(scale[1]) < 101) { + return true; + } + return false; + } + + /** + * Create a MIP for a given scale. + * @param {number} scale - The relative size of the MIP + * @return {SVGMIP} An object that handles creating and updating SVG textures. + */ + createMIP (scale) { + const [width, height] = this._size; + this._canvas.width = width * scale; + this._canvas.height = height * scale; + if ( + this._canvas.width <= 0 || + this._canvas.height <= 0 || + // Even if the canvas at the current scale has a nonzero size, the image's dimensions are floored + // pre-scaling; e.g. if an image has a width of 0.4 and is being rendered at 3x scale, the canvas will have + // a width of 1, but the image's width will be rounded down to 0 on some browsers (Firefox) prior to being + // drawn at that scale, resulting in an IndexSizeError if we attempt to draw it. + this._svgImage.naturalWidth <= 0 || + this._svgImage.naturalHeight <= 0 + ) return super.getTexture(); + this._context.clearRect(0, 0, this._canvas.width, this._canvas.height); + this._context.setTransform(scale, 0, 0, scale, 0, 0); + this._context.drawImage(this._svgImage, 0, 0); + + // Pull out the ImageData from the canvas. ImageData speeds up + // updating Silhouette and is better handled by more browsers in + // regards to memory. + const textureData = this._context.getImageData(0, 0, this._canvas.width, this._canvas.height); + + const textureOptions = { + auto: false, + wrap: this._renderer.gl.CLAMP_TO_EDGE, + src: textureData, + premultiplyAlpha: true + }; + + const mip = twgl.createTexture(this._renderer.gl, textureOptions); + + // Check if this is the largest MIP created so far. Currently, silhouettes only get scaled up. + if (this._largestMIPScale < scale) { + this._silhouette.update(textureData); + this._largestMIPScale = scale; + } + + return mip; + } + + updateSilhouette (scale = [100, 100]) { + // Ensure a silhouette exists. + this.getTexture(scale); + } + + /** + * @param {Array} scale - The scaling factors to be used, each in the [0,100] range. + * @return {WebGLTexture} The GL texture representation of this skin when drawing at the given scale. + */ + getTexture (scale) { + // The texture only ever gets uniform scale. Take the larger of the two axes. + const scaleMax = scale ? Math.max(Math.abs(scale[0]), Math.abs(scale[1])) : 100; + const requestedScale = Math.min(scaleMax / 100, this._maxTextureScale); + + // Math.ceil(Math.log2(scale)) means we use the "1x" texture at (0.5, 1] scale, + // the "2x" texture at (1, 2] scale, the "4x" texture at (2, 4] scale, etc. + // This means that one texture pixel will always be between 0.5x and 1x the size of one rendered pixel, + // but never bigger than one rendered pixel--this prevents blurriness from blowing up the texture too much. + const mipLevel = Math.max(Math.ceil(Math.log2(requestedScale)) + INDEX_OFFSET, 0); + // Can't use bitwise stuff here because we need to handle negative exponents + const mipScale = Math.pow(2, mipLevel - INDEX_OFFSET); + + if (this._svgImageLoaded && !this._scaledMIPs[mipLevel]) { + this._scaledMIPs[mipLevel] = this.createMIP(mipScale); + } + + return this._scaledMIPs[mipLevel] || super.getTexture(); + } + + /** + * Do a hard reset of the existing MIPs by deleting them. + */ + resetMIPs () { + this._scaledMIPs.forEach(oldMIP => this._renderer.gl.deleteTexture(oldMIP)); + this._scaledMIPs.length = 0; + this._largestMIPScale = 0; + } + + /** + * Set the contents of this skin to a snapshot of the provided SVG data. + * @param {string} svgData - new SVG to use. + * @param {Array} [rotationCenter] - Optional rotation center for the SVG. If not supplied, it will be + * calculated from the bounding box + * @fires Skin.event:WasAltered + */ + setSVG (svgData, rotationCenter) { + const svgTag = loadSvgString(svgData); + const svgText = serializeSvgToString(svgTag, true /* shouldInjectFonts */); + this._svgImageLoaded = false; + + const {x, y, width, height} = svgTag.viewBox.baseVal; + // While we're setting the size before the image is loaded, this doesn't cause the skin to appear with the wrong + // size for a few frames while the new image is loading, because we don't emit the `WasAltered` event, telling + // drawables using this skin to update, until the image is loaded. + // We need to do this because the VM reads the skin's `size` directly after calling `setSVG`. + // TODO: return a Promise so that the VM can read the skin's `size` after the image is loaded. + this._size[0] = width; + this._size[1] = height; + + // If there is another load already in progress, replace the old onload to effectively cancel the old load + this._svgImage.onload = () => { + if (width === 0 || height === 0) { + super.setEmptyImageData(); + return; + } + + const maxDimension = Math.ceil(Math.max(width, height)); + let testScale = 2; + for (testScale; maxDimension * testScale <= MAX_TEXTURE_DIMENSION; testScale *= 2) { + this._maxTextureScale = testScale; + } + + this.resetMIPs(); + + if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); + // Compensate for viewbox offset. + // See https://github.com/LLK/scratch-render/pull/90. + this._rotationCenter[0] = rotationCenter[0] - x; + this._rotationCenter[1] = rotationCenter[1] - y; + + this._svgImageLoaded = true; + + this.emit(Skin.Events.WasAltered); + }; + + this._svgImage.src = `data:image/svg+xml;utf8,${encodeURIComponent(svgText)}`; + } + +} + +module.exports = SVGSkin; From 4976a0eb7fcb14b3aff5273ac65efb6797a8c2e4 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 24 Apr 2023 15:22:03 -0400 Subject: [PATCH 1959/1971] Merge commit '4d4c25ea7d062a2fa9e48cb6c7931a7c114d80cd' as 'packages/scratch-gui' --- packages/scratch-gui/package.json | 158 +++ .../src/components/menu-bar/menu-bar.jsx | 1001 +++++++++++++++++ .../scratch-gui/src/containers/blocks.jsx | 734 ++++++++++++ packages/scratch-gui/src/containers/stage.jsx | 461 ++++++++ packages/scratch-gui/src/lib/alerts/index.jsx | 222 ++++ .../src/lib/libraries/extensions/index.jsx | 321 ++++++ .../scratch-gui/src/lib/vm-listener-hoc.jsx | 213 ++++ 7 files changed, 3110 insertions(+) create mode 100644 packages/scratch-gui/package.json create mode 100644 packages/scratch-gui/src/components/menu-bar/menu-bar.jsx create mode 100644 packages/scratch-gui/src/containers/blocks.jsx create mode 100644 packages/scratch-gui/src/containers/stage.jsx create mode 100644 packages/scratch-gui/src/lib/alerts/index.jsx create mode 100644 packages/scratch-gui/src/lib/libraries/extensions/index.jsx create mode 100644 packages/scratch-gui/src/lib/vm-listener-hoc.jsx diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json new file mode 100644 index 0000000000..b5b9adfc90 --- /dev/null +++ b/packages/scratch-gui/package.json @@ -0,0 +1,158 @@ +{ + "name": "scratch-gui", + "version": "1.8.40", + "description": "Graphical User Interface for creating and running Scratch 3.0 projects", + "author": "Massachusetts Institute of Technology", + "license": "BSD-3-Clause", + "homepage": "https://github.com/LLK/scratch-gui#readme", + "repository": { + "type": "git", + "url": "https://github.com/LLK/scratch-gui.git" + }, + "main": "./dist/scratch-gui.js", + "scripts": { + "build": "npm run clean && webpack --colors --bail", + "clean": "rimraf ./build && mkdirp build && rimraf ./dist && mkdirp dist", + "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"[skip ci] Build for $(git log --pretty=format:%H -n1)\"", + "prepare": "husky install", + "prune": "./prune-gh-pages.sh", + "i18n:push": "tx-push-src scratch-editor interface translations/en.json", + "i18n:src": "rimraf ./translations/messages/src && babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/src ./translations/", + "start": "webpack-dev-server", + "test": "npm run test:lint && npm run test:unit && npm run build && npm run test:integration", + "test:integration": "jest --maxWorkers=4 test[\\\\/]integration", + "test:lint": "eslint . --ext .js,.jsx", + "test:unit": "jest test[\\\\/]unit", + "test:smoke": "jest --runInBand test[\\\\/]smoke", + "watch": "webpack --colors --watch" + }, + "config": { + "commitizen": { + "path": "cz-conventional-changelog" + } + }, + "dependencies": { + "arraybuffer-loader": "^1.0.6", + "autoprefixer": "^9.0.1", + "base64-loader": "1.0.0", + "bowser": "1.9.4", + "cat-blocks": "npm:scratch-blocks@0.1.0-prerelease.20220318143026", + "classnames": "2.2.6", + "computed-style-to-inline-style": "3.0.0", + "copy-webpack-plugin": "6.4.1", + "core-js": "2.5.7", + "css-loader": "^1.0.0", + "es6-object-assign": "1.1.0", + "file-loader": "2.0.0", + "get-float-time-domain-data": "0.1.0", + "get-user-media-promise": "1.1.4", + "immutable": "3.8.2", + "intl": "1.2.5", + "js-base64": "2.4.9", + "keymirror": "0.1.1", + "lodash.bindall": "4.4.0", + "lodash.debounce": "4.0.8", + "lodash.defaultsdeep": "4.6.1", + "lodash.omit": "4.5.0", + "lodash.throttle": "4.0.1", + "minilog": "3.1.0", + "omggif": "1.0.9", + "papaparse": "5.3.0", + "postcss-import": "^12.0.0", + "postcss-loader": "^3.0.0", + "postcss-simple-vars": "^5.0.1", + "prop-types": "^15.5.10", + "query-string": "^5.1.1", + "raw-loader": "^0.5.1", + "react": "16.2.0", + "react-contextmenu": "2.9.4", + "react-dom": "16.2.1", + "react-draggable": "3.0.5", + "react-ga": "2.5.3", + "react-intl": "2.9.0", + "react-modal": "3.9.1", + "react-popover": "0.5.10", + "react-redux": "5.0.7", + "react-responsive": "5.0.0", + "react-style-proptype": "3.2.2", + "react-tabs": "2.3.0", + "react-tooltip": "3.8.0", + "react-virtualized": "9.20.1", + "redux": "3.7.2", + "redux-throttle": "0.1.1", + "scratch-audio": "0.1.0-prerelease.20221123180128", + "scratch-blocks": "0.1.0-prerelease.20230424044823", + "scratch-l10n": "3.15.20230422032237", + "scratch-paint": "1.1.51", + "scratch-render": "0.1.0-prerelease.20230318150639", + "scratch-render-fonts": "1.0.0-prerelease.20221102164332", + "scratch-storage": "2.2.1", + "scratch-svg-renderer": "0.2.0-prerelease.20230224194137", + "scratch-vm": "1.5.41", + "startaudiocontext": "1.2.1", + "style-loader": "^0.23.0", + "text-encoding": "0.7.0", + "to-style": "1.3.3", + "wav-encoder": "1.3.0", + "xhr": "2.5.0" + }, + "peerDependencies": { + "react": "^16.0.0", + "react-dom": "^16.0.0" + }, + "devDependencies": { + "@babel/cli": "7.14.8", + "@babel/core": "7.14.8", + "@babel/plugin-proposal-object-rest-spread": "7.14.7", + "@babel/plugin-syntax-dynamic-import": "7.2.0", + "@babel/plugin-transform-async-to-generator": "7.14.5", + "@babel/preset-env": "7.14.8", + "@babel/preset-react": "7.14.5", + "@commitlint/cli": "17.1.2", + "@commitlint/config-conventional": "17.1.0", + "babel-core": "7.0.0-bridge.0", + "babel-eslint": "10.0.3", + "babel-loader": "8.2.2", + "chromedriver": "112.0.0", + "enzyme": "3.10.0", + "enzyme-adapter-react-16": "1.15.7", + "eslint": "5.16.0", + "eslint-config-scratch": "6.0.0", + "eslint-import-resolver-webpack": "0.11.1", + "eslint-plugin-import": "2.23.4", + "eslint-plugin-jest": "22.17.0", + "eslint-plugin-react": "7.24.0", + "gh-pages": "3.2.3", + "html-webpack-plugin": "3.2.0", + "husky": "8.0.1", + "jest": "21.2.1", + "jest-junit": "7.0.0", + "mkdirp": "1.0.3", + "raf": "3.4.1", + "react-test-renderer": "16.2.0", + "redux-mock-store": "1.5.3", + "rimraf": "2.7.1", + "scratch-semantic-release-config": "1.0.7", + "selenium-webdriver": "3.6.0", + "semantic-release": "19.0.5", + "uglifyjs-webpack-plugin": "1.3.0", + "web-audio-test-api": "0.5.2", + "webpack": "4.46.0", + "webpack-cli": "3.3.12", + "webpack-dev-server": "3.11.2" + }, + "jest": { + "setupFiles": [ + "raf/polyfill", + "/test/helpers/enzyme-setup.js" + ], + "testPathIgnorePatterns": [ + "src/test.js" + ], + "moduleNameMapper": { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/test/__mocks__/fileMock.js", + "\\.(css|less)$": "/test/__mocks__/styleMock.js", + "editor-msgs(\\.js)?$": "/test/__mocks__/editor-msgs-mock.js" + } + } +} diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx new file mode 100644 index 0000000000..5aba0ffaf1 --- /dev/null +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -0,0 +1,1001 @@ +import classNames from 'classnames'; +import {connect} from 'react-redux'; +import {compose} from 'redux'; +import {defineMessages, FormattedMessage, injectIntl, intlShape} from 'react-intl'; +import PropTypes from 'prop-types'; +import bindAll from 'lodash.bindall'; +import bowser from 'bowser'; +import React from 'react'; + +import VM from 'scratch-vm'; + +import Box from '../box/box.jsx'; +import Button from '../button/button.jsx'; +import CommunityButton from './community-button.jsx'; +import ShareButton from './share-button.jsx'; +import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx'; +import Divider from '../divider/divider.jsx'; +import LanguageSelector from '../../containers/language-selector.jsx'; +import SaveStatus from './save-status.jsx'; +import ProjectWatcher from '../../containers/project-watcher.jsx'; +import MenuBarMenu from './menu-bar-menu.jsx'; +import {MenuItem, MenuSection} from '../menu/menu.jsx'; +import ProjectTitleInput from './project-title-input.jsx'; +import AuthorInfo from './author-info.jsx'; +import AccountNav from '../../containers/account-nav.jsx'; +import LoginDropdown from './login-dropdown.jsx'; +import SB3Downloader from '../../containers/sb3-downloader.jsx'; +import DeletionRestorer from '../../containers/deletion-restorer.jsx'; +import TurboMode from '../../containers/turbo-mode.jsx'; +import MenuBarHOC from '../../containers/menu-bar-hoc.jsx'; + +import {openTipsLibrary} from '../../reducers/modals'; +import {setPlayer} from '../../reducers/mode'; +import { + isTimeTravel220022BC, + isTimeTravel1920, + isTimeTravel1990, + isTimeTravel2020, + isTimeTravelNow, + setTimeTravel +} from '../../reducers/time-travel'; +import { + autoUpdateProject, + getIsUpdating, + getIsShowingProject, + manualUpdateProject, + requestNewProject, + remixProject, + saveProjectAsCopy +} from '../../reducers/project-state'; +import { + openAboutMenu, + closeAboutMenu, + aboutMenuOpen, + openAccountMenu, + closeAccountMenu, + accountMenuOpen, + openFileMenu, + closeFileMenu, + fileMenuOpen, + openEditMenu, + closeEditMenu, + editMenuOpen, + openLanguageMenu, + closeLanguageMenu, + languageMenuOpen, + openLoginMenu, + closeLoginMenu, + loginMenuOpen, + openModeMenu, + closeModeMenu, + modeMenuOpen +} from '../../reducers/menus'; + +import collectMetadata from '../../lib/collect-metadata'; + +import styles from './menu-bar.css'; + +import helpIcon from '../../lib/assets/icon--tutorials.svg'; +import mystuffIcon from './icon--mystuff.png'; +import profileIcon from './icon--profile.png'; +import remixIcon from './icon--remix.svg'; +import dropdownCaret from './dropdown-caret.svg'; +import languageIcon from '../language-selector/language-icon.svg'; +import aboutIcon from './icon--about.svg'; + +import scratchLogo from './scratch-logo.svg'; +import ninetiesLogo from './nineties_logo.svg'; +import catLogo from './cat_logo.svg'; +import prehistoricLogo from './prehistoric-logo.svg'; +import oldtimeyLogo from './oldtimey-logo.svg'; + +import sharedMessages from '../../lib/shared-messages'; + +const ariaMessages = defineMessages({ + language: { + id: 'gui.menuBar.LanguageSelector', + defaultMessage: 'language selector', + description: 'accessibility text for the language selection menu' + }, + tutorials: { + id: 'gui.menuBar.tutorialsLibrary', + defaultMessage: 'Tutorials', + description: 'accessibility text for the tutorials button' + } +}); + +const MenuBarItemTooltip = ({ + children, + className, + enable, + id, + place = 'bottom' +}) => { + if (enable) { + return ( + + {children} + + ); + } + return ( + + {children} + + ); +}; + + +MenuBarItemTooltip.propTypes = { + children: PropTypes.node, + className: PropTypes.string, + enable: PropTypes.bool, + id: PropTypes.string, + place: PropTypes.oneOf(['top', 'bottom', 'left', 'right']) +}; + +const MenuItemTooltip = ({id, isRtl, children, className}) => ( + + {children} + +); + +MenuItemTooltip.propTypes = { + children: PropTypes.node, + className: PropTypes.string, + id: PropTypes.string, + isRtl: PropTypes.bool +}; + +const AboutButton = props => ( + + ); + // Show the About button only if we have a handler for it (like in the desktop app) + const aboutButton = this.buildAboutMenu(this.props.onClickAbout); + return ( + +
    +
    +
    + Scratch +
    + {(this.props.canChangeLanguage) && (
    +
    + + +
    + +
    )} + {(this.props.canManageFiles) && ( +
    + + + + + {newProjectMessage} + + + {(this.props.canSave || this.props.canCreateCopy || this.props.canRemix) && ( + + {this.props.canSave && ( + + {saveNowMessage} + + )} + {this.props.canCreateCopy && ( + + {createCopyMessage} + + )} + {this.props.canRemix && ( + + {remixMessage} + + )} + + )} + + + {this.props.intl.formatMessage(sharedMessages.loadFromComputerTitle)} + + {(className, downloadProjectCallback) => ( + + + + )} + + +
    + )} +
    +
    + +
    + + {(handleRestore, {restorable, deletedItem}) => ( + + {this.restoreOptionMessage(deletedItem)} + + )} + + {(toggleTurboMode, {turboMode}) => ( + + {turboMode ? ( + + ) : ( + + )} + + )} + + + +
    + {this.props.isTotallyNormal && ( +
    +
    + +
    + + + + + {'✓'} + + {' '} + + + + + {'✓'} + + {' '} + + + + +
    + )} +
    + +
    + + +
    + + {this.props.canEditTitle ? ( +
    + + + +
    + ) : ((this.props.authorUsername && this.props.authorUsername !== this.props.username) ? ( + + ) : null)} +
    + {this.props.canShare ? ( + (this.props.isShowingProject || this.props.isUpdating) && ( + + { + waitForUpdate => ( + { + this.handleClickShare(waitForUpdate); + }} + /* eslint-enable react/jsx-no-bind */ + /> + ) + } + + ) + ) : ( + this.props.showComingSoon ? ( + + + + ) : [] + )} + {this.props.canRemix ? remixButton : []} +
    +
    + {this.props.enableCommunity ? ( + (this.props.isShowingProject || this.props.isUpdating) && ( + + { + waitForUpdate => ( + { + this.handleClickSeeCommunity(waitForUpdate); + }} + /* eslint-enable react/jsx-no-bind */ + /> + ) + } + + ) + ) : (this.props.showComingSoon ? ( + + + + ) : [])} +
    +
    + + {/* show the proper UI in the account menu, given whether the user is + logged in, and whether a session is available to log in with */} +
    +
    + {this.props.canSave && ( + + )} +
    + {this.props.sessionExists ? ( + this.props.username ? ( + // ************ user is logged in ************ + + +
    + +
    +
    + +
    + ) : ( + // ********* user not logged in, but a session exists + // ********* so they can choose to log in + +
    + +
    +
    + + +
    +
    + ) + ) : ( + // ******** no login session is available, so don't show login stuff + + {this.props.showComingSoon ? ( + + +
    + +
    +
    + +
    + + + {'scratch-cat'} + + +
    +
    +
    + ) : []} +
    + )} +
    + + {aboutButton} +
    + ); + } +} + +MenuBar.propTypes = { + aboutMenuOpen: PropTypes.bool, + accountMenuOpen: PropTypes.bool, + authorId: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), + authorThumbnailUrl: PropTypes.string, + authorUsername: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), + autoUpdateProject: PropTypes.func, + canChangeLanguage: PropTypes.bool, + canCreateCopy: PropTypes.bool, + canCreateNew: PropTypes.bool, + canEditTitle: PropTypes.bool, + canManageFiles: PropTypes.bool, + canRemix: PropTypes.bool, + canSave: PropTypes.bool, + canShare: PropTypes.bool, + className: PropTypes.string, + confirmReadyToReplaceProject: PropTypes.func, + editMenuOpen: PropTypes.bool, + enableCommunity: PropTypes.bool, + fileMenuOpen: PropTypes.bool, + intl: intlShape, + isRtl: PropTypes.bool, + isShared: PropTypes.bool, + isShowingProject: PropTypes.bool, + isTotallyNormal: PropTypes.bool, + isUpdating: PropTypes.bool, + languageMenuOpen: PropTypes.bool, + locale: PropTypes.string.isRequired, + loginMenuOpen: PropTypes.bool, + logo: PropTypes.string, + modeMenuOpen: PropTypes.bool, + modeNow: PropTypes.bool, + mode220022BC: PropTypes.bool, + mode1920: PropTypes.bool, + mode1990: PropTypes.bool, + mode2020: PropTypes.bool, + + onClickAbout: PropTypes.oneOfType([ + PropTypes.func, // button mode: call this callback when the About button is clicked + PropTypes.arrayOf( // menu mode: list of items in the About menu + PropTypes.shape({ + title: PropTypes.string, // text for the menu item + onClick: PropTypes.func // call this callback when the menu item is clicked + }) + ) + ]), + onClickAccount: PropTypes.func, + onSetTimeTravelMode: PropTypes.func, + onClickEdit: PropTypes.func, + onClickFile: PropTypes.func, + onClickLanguage: PropTypes.func, + onClickLogin: PropTypes.func, + onClickLogo: PropTypes.func, + onClickMode: PropTypes.func, + onClickNew: PropTypes.func, + onClickRemix: PropTypes.func, + onClickSave: PropTypes.func, + onClickSaveAsCopy: PropTypes.func, + onLogOut: PropTypes.func, + onOpenRegistration: PropTypes.func, + onOpenTipLibrary: PropTypes.func, + onProjectTelemetryEvent: PropTypes.func, + onRequestOpenAbout: PropTypes.func, + onRequestCloseAbout: PropTypes.func, + onRequestCloseAccount: PropTypes.func, + onRequestCloseEdit: PropTypes.func, + onRequestCloseFile: PropTypes.func, + onRequestCloseLanguage: PropTypes.func, + onRequestCloseLogin: PropTypes.func, + onRequestCloseMode: PropTypes.func, + onSeeCommunity: PropTypes.func, + onShare: PropTypes.func, + onStartSelectingFileUpload: PropTypes.func, + onToggleLoginOpen: PropTypes.func, + projectTitle: PropTypes.string, + renderLogin: PropTypes.func, + sessionExists: PropTypes.bool, + shouldSaveBeforeTransition: PropTypes.func, + showComingSoon: PropTypes.bool, + userOwnsProject: PropTypes.bool, + username: PropTypes.string, + vm: PropTypes.instanceOf(VM).isRequired +}; + +MenuBar.defaultProps = { + logo: scratchLogo, + onShare: () => {} +}; + +const mapStateToProps = (state, ownProps) => { + const loadingState = state.scratchGui.projectState.loadingState; + const user = state.session && state.session.session && state.session.session.user; + return { + aboutMenuOpen: aboutMenuOpen(state), + accountMenuOpen: accountMenuOpen(state), + fileMenuOpen: fileMenuOpen(state), + editMenuOpen: editMenuOpen(state), + isRtl: state.locales.isRtl, + isUpdating: getIsUpdating(loadingState), + isShowingProject: getIsShowingProject(loadingState), + languageMenuOpen: languageMenuOpen(state), + locale: state.locales.locale, + loginMenuOpen: loginMenuOpen(state), + modeMenuOpen: modeMenuOpen(state), + projectTitle: state.scratchGui.projectTitle, + sessionExists: state.session && typeof state.session.session !== 'undefined', + username: user ? user.username : null, + userOwnsProject: ownProps.authorUsername && user && + (ownProps.authorUsername === user.username), + vm: state.scratchGui.vm, + mode220022BC: isTimeTravel220022BC(state), + mode1920: isTimeTravel1920(state), + mode1990: isTimeTravel1990(state), + mode2020: isTimeTravel2020(state), + modeNow: isTimeTravelNow(state) + }; +}; + +const mapDispatchToProps = dispatch => ({ + autoUpdateProject: () => dispatch(autoUpdateProject()), + onOpenTipLibrary: () => dispatch(openTipsLibrary()), + onClickAccount: () => dispatch(openAccountMenu()), + onRequestCloseAccount: () => dispatch(closeAccountMenu()), + onClickFile: () => dispatch(openFileMenu()), + onRequestCloseFile: () => dispatch(closeFileMenu()), + onClickEdit: () => dispatch(openEditMenu()), + onRequestCloseEdit: () => dispatch(closeEditMenu()), + onClickLanguage: () => dispatch(openLanguageMenu()), + onRequestCloseLanguage: () => dispatch(closeLanguageMenu()), + onClickLogin: () => dispatch(openLoginMenu()), + onRequestCloseLogin: () => dispatch(closeLoginMenu()), + onClickMode: () => dispatch(openModeMenu()), + onRequestCloseMode: () => dispatch(closeModeMenu()), + onRequestOpenAbout: () => dispatch(openAboutMenu()), + onRequestCloseAbout: () => dispatch(closeAboutMenu()), + onClickNew: needSave => dispatch(requestNewProject(needSave)), + onClickRemix: () => dispatch(remixProject()), + onClickSave: () => dispatch(manualUpdateProject()), + onClickSaveAsCopy: () => dispatch(saveProjectAsCopy()), + onSeeCommunity: () => dispatch(setPlayer(true)), + onSetTimeTravelMode: mode => dispatch(setTimeTravel(mode)) +}); + +export default compose( + injectIntl, + MenuBarHOC, + connect( + mapStateToProps, + mapDispatchToProps + ) +)(MenuBar); diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx new file mode 100644 index 0000000000..616d2e0900 --- /dev/null +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -0,0 +1,734 @@ +import bindAll from 'lodash.bindall'; +import debounce from 'lodash.debounce'; +import defaultsDeep from 'lodash.defaultsdeep'; +import makeToolboxXML from '../lib/make-toolbox-xml'; +import PropTypes from 'prop-types'; +import React from 'react'; +import VMScratchBlocks from '../lib/blocks'; +import VM from 'scratch-vm'; + +import log from '../lib/log.js'; +import Prompt from './prompt.jsx'; +import BlocksComponent from '../components/blocks/blocks.jsx'; +import ExtensionLibrary from './extension-library.jsx'; +import extensionData from '../lib/libraries/extensions/index.jsx'; +import CustomProcedures from './custom-procedures.jsx'; +import errorBoundaryHOC from '../lib/error-boundary-hoc.jsx'; +import {BLOCKS_DEFAULT_SCALE, STAGE_DISPLAY_SIZES} from '../lib/layout-constants'; +import DropAreaHOC from '../lib/drop-area-hoc.jsx'; +import DragConstants from '../lib/drag-constants'; +import defineDynamicBlock from '../lib/define-dynamic-block'; + +import {connect} from 'react-redux'; +import {updateToolbox} from '../reducers/toolbox'; +import {activateColorPicker} from '../reducers/color-picker'; +import {closeExtensionLibrary, openSoundRecorder, openConnectionModal} from '../reducers/modals'; +import {activateCustomProcedures, deactivateCustomProcedures} from '../reducers/custom-procedures'; +import {setConnectionModalExtensionId} from '../reducers/connection-modal'; +import {updateMetrics} from '../reducers/workspace-metrics'; +import {isTimeTravel2020} from '../reducers/time-travel'; + +import { + activateTab, + SOUNDS_TAB_INDEX +} from '../reducers/editor-tab'; + +const addFunctionListener = (object, property, callback) => { + const oldFn = object[property]; + object[property] = function (...args) { + const result = oldFn.apply(this, args); + callback.apply(this, result); + return result; + }; +}; + +const DroppableBlocks = DropAreaHOC([ + DragConstants.BACKPACK_CODE +])(BlocksComponent); + +class Blocks extends React.Component { + constructor (props) { + super(props); + this.ScratchBlocks = VMScratchBlocks(props.vm, false); + bindAll(this, [ + 'attachVM', + 'detachVM', + 'getToolboxXML', + 'handleCategorySelected', + 'handleConnectionModalStart', + 'handleDrop', + 'handleStatusButtonUpdate', + 'handleOpenSoundRecorder', + 'handlePromptStart', + 'handlePromptCallback', + 'handlePromptClose', + 'handleCustomProceduresClose', + 'onScriptGlowOn', + 'onScriptGlowOff', + 'onBlockGlowOn', + 'onBlockGlowOff', + 'handleMonitorsUpdate', + 'handleExtensionAdded', + 'handleBlocksInfoUpdate', + 'onTargetsUpdate', + 'onVisualReport', + 'onWorkspaceUpdate', + 'onWorkspaceMetricsChange', + 'setBlocks', + 'setLocale' + ]); + this.ScratchBlocks.prompt = this.handlePromptStart; + this.ScratchBlocks.statusButtonCallback = this.handleConnectionModalStart; + this.ScratchBlocks.recordSoundCallback = this.handleOpenSoundRecorder; + + this.state = { + prompt: null + }; + this.onTargetsUpdate = debounce(this.onTargetsUpdate, 100); + this.toolboxUpdateQueue = []; + } + componentDidMount () { + this.ScratchBlocks = VMScratchBlocks(this.props.vm, this.props.useCatBlocks); + this.ScratchBlocks.prompt = this.handlePromptStart; + this.ScratchBlocks.statusButtonCallback = this.handleConnectionModalStart; + this.ScratchBlocks.recordSoundCallback = this.handleOpenSoundRecorder; + + this.ScratchBlocks.FieldColourSlider.activateEyedropper_ = this.props.onActivateColorPicker; + this.ScratchBlocks.Procedures.externalProcedureDefCallback = this.props.onActivateCustomProcedures; + this.ScratchBlocks.ScratchMsgs.setLocale(this.props.locale); + + const workspaceConfig = defaultsDeep({}, + Blocks.defaultOptions, + this.props.options, + {rtl: this.props.isRtl, toolbox: this.props.toolboxXML} + ); + this.workspace = this.ScratchBlocks.inject(this.blocks, workspaceConfig); + + // Register buttons under new callback keys for creating variables, + // lists, and procedures from extensions. + + const toolboxWorkspace = this.workspace.getFlyout().getWorkspace(); + + const varListButtonCallback = type => + (() => this.ScratchBlocks.Variables.createVariable(this.workspace, null, type)); + const procButtonCallback = () => { + this.ScratchBlocks.Procedures.createProcedureDefCallback_(this.workspace); + }; + + toolboxWorkspace.registerButtonCallback('MAKE_A_VARIABLE', varListButtonCallback('')); + toolboxWorkspace.registerButtonCallback('MAKE_A_LIST', varListButtonCallback('list')); + toolboxWorkspace.registerButtonCallback('MAKE_A_PROCEDURE', procButtonCallback); + + // Store the xml of the toolbox that is actually rendered. + // This is used in componentDidUpdate instead of prevProps, because + // the xml can change while e.g. on the costumes tab. + this._renderedToolboxXML = this.props.toolboxXML; + + // we actually never want the workspace to enable "refresh toolbox" - this basically re-renders the + // entire toolbox every time we reset the workspace. We call updateToolbox as a part of + // componentDidUpdate so the toolbox will still correctly be updated + this.setToolboxRefreshEnabled = this.workspace.setToolboxRefreshEnabled.bind(this.workspace); + this.workspace.setToolboxRefreshEnabled = () => { + this.setToolboxRefreshEnabled(false); + }; + + // @todo change this when blockly supports UI events + addFunctionListener(this.workspace, 'translate', this.onWorkspaceMetricsChange); + addFunctionListener(this.workspace, 'zoom', this.onWorkspaceMetricsChange); + + this.attachVM(); + // Only update blocks/vm locale when visible to avoid sizing issues + // If locale changes while not visible it will get handled in didUpdate + if (this.props.isVisible) { + this.setLocale(); + } + } + shouldComponentUpdate (nextProps, nextState) { + return ( + this.state.prompt !== nextState.prompt || + this.props.isVisible !== nextProps.isVisible || + this._renderedToolboxXML !== nextProps.toolboxXML || + this.props.extensionLibraryVisible !== nextProps.extensionLibraryVisible || + this.props.customProceduresVisible !== nextProps.customProceduresVisible || + this.props.locale !== nextProps.locale || + this.props.anyModalVisible !== nextProps.anyModalVisible || + this.props.stageSize !== nextProps.stageSize + ); + } + componentDidUpdate (prevProps) { + // If any modals are open, call hideChaff to close z-indexed field editors + if (this.props.anyModalVisible && !prevProps.anyModalVisible) { + this.ScratchBlocks.hideChaff(); + } + + // Only rerender the toolbox when the blocks are visible and the xml is + // different from the previously rendered toolbox xml. + // Do not check against prevProps.toolboxXML because that may not have been rendered. + if (this.props.isVisible && this.props.toolboxXML !== this._renderedToolboxXML) { + this.requestToolboxUpdate(); + } + + if (this.props.isVisible === prevProps.isVisible) { + if (this.props.stageSize !== prevProps.stageSize) { + // force workspace to redraw for the new stage size + window.dispatchEvent(new Event('resize')); + } + return; + } + // @todo hack to resize blockly manually in case resize happened while hidden + // @todo hack to reload the workspace due to gui bug #413 + if (this.props.isVisible) { // Scripts tab + this.workspace.setVisible(true); + if (prevProps.locale !== this.props.locale || this.props.locale !== this.props.vm.getLocale()) { + // call setLocale if the locale has changed, or changed while the blocks were hidden. + // vm.getLocale() will be out of sync if locale was changed while not visible + this.setLocale(); + } else { + this.props.vm.refreshWorkspace(); + this.requestToolboxUpdate(); + } + + window.dispatchEvent(new Event('resize')); + } else { + this.workspace.setVisible(false); + } + } + componentWillUnmount () { + this.detachVM(); + this.workspace.dispose(); + clearTimeout(this.toolboxUpdateTimeout); + + // Clear the flyout blocks so that they can be recreated on mount. + this.props.vm.clearFlyoutBlocks(); + } + requestToolboxUpdate () { + clearTimeout(this.toolboxUpdateTimeout); + this.toolboxUpdateTimeout = setTimeout(() => { + this.updateToolbox(); + }, 0); + } + setLocale () { + this.ScratchBlocks.ScratchMsgs.setLocale(this.props.locale); + this.props.vm.setLocale(this.props.locale, this.props.messages) + .then(() => { + this.workspace.getFlyout().setRecyclingEnabled(false); + this.props.vm.refreshWorkspace(); + this.requestToolboxUpdate(); + this.withToolboxUpdates(() => { + this.workspace.getFlyout().setRecyclingEnabled(true); + }); + }); + } + + updateToolbox () { + this.toolboxUpdateTimeout = false; + + const categoryId = this.workspace.toolbox_.getSelectedCategoryId(); + const offset = this.workspace.toolbox_.getCategoryScrollOffset(); + this.workspace.updateToolbox(this.props.toolboxXML); + this._renderedToolboxXML = this.props.toolboxXML; + + // In order to catch any changes that mutate the toolbox during "normal runtime" + // (variable changes/etc), re-enable toolbox refresh. + // Using the setter function will rerender the entire toolbox which we just rendered. + this.workspace.toolboxRefreshEnabled_ = true; + + const currentCategoryPos = this.workspace.toolbox_.getCategoryPositionById(categoryId); + const currentCategoryLen = this.workspace.toolbox_.getCategoryLengthById(categoryId); + if (offset < currentCategoryLen) { + this.workspace.toolbox_.setFlyoutScrollPos(currentCategoryPos + offset); + } else { + this.workspace.toolbox_.setFlyoutScrollPos(currentCategoryPos); + } + + const queue = this.toolboxUpdateQueue; + this.toolboxUpdateQueue = []; + queue.forEach(fn => fn()); + } + + withToolboxUpdates (fn) { + // if there is a queued toolbox update, we need to wait + if (this.toolboxUpdateTimeout) { + this.toolboxUpdateQueue.push(fn); + } else { + fn(); + } + } + + attachVM () { + this.workspace.addChangeListener(this.props.vm.blockListener); + this.flyoutWorkspace = this.workspace + .getFlyout() + .getWorkspace(); + this.flyoutWorkspace.addChangeListener(this.props.vm.flyoutBlockListener); + this.flyoutWorkspace.addChangeListener(this.props.vm.monitorBlockListener); + this.props.vm.addListener('SCRIPT_GLOW_ON', this.onScriptGlowOn); + this.props.vm.addListener('SCRIPT_GLOW_OFF', this.onScriptGlowOff); + this.props.vm.addListener('BLOCK_GLOW_ON', this.onBlockGlowOn); + this.props.vm.addListener('BLOCK_GLOW_OFF', this.onBlockGlowOff); + this.props.vm.addListener('VISUAL_REPORT', this.onVisualReport); + this.props.vm.addListener('workspaceUpdate', this.onWorkspaceUpdate); + this.props.vm.addListener('targetsUpdate', this.onTargetsUpdate); + this.props.vm.addListener('MONITORS_UPDATE', this.handleMonitorsUpdate); + this.props.vm.addListener('EXTENSION_ADDED', this.handleExtensionAdded); + this.props.vm.addListener('BLOCKSINFO_UPDATE', this.handleBlocksInfoUpdate); + this.props.vm.addListener('PERIPHERAL_CONNECTED', this.handleStatusButtonUpdate); + this.props.vm.addListener('PERIPHERAL_DISCONNECTED', this.handleStatusButtonUpdate); + } + detachVM () { + this.props.vm.removeListener('SCRIPT_GLOW_ON', this.onScriptGlowOn); + this.props.vm.removeListener('SCRIPT_GLOW_OFF', this.onScriptGlowOff); + this.props.vm.removeListener('BLOCK_GLOW_ON', this.onBlockGlowOn); + this.props.vm.removeListener('BLOCK_GLOW_OFF', this.onBlockGlowOff); + this.props.vm.removeListener('VISUAL_REPORT', this.onVisualReport); + this.props.vm.removeListener('workspaceUpdate', this.onWorkspaceUpdate); + this.props.vm.removeListener('targetsUpdate', this.onTargetsUpdate); + this.props.vm.removeListener('MONITORS_UPDATE', this.handleMonitorsUpdate); + this.props.vm.removeListener('EXTENSION_ADDED', this.handleExtensionAdded); + this.props.vm.removeListener('BLOCKSINFO_UPDATE', this.handleBlocksInfoUpdate); + this.props.vm.removeListener('PERIPHERAL_CONNECTED', this.handleStatusButtonUpdate); + this.props.vm.removeListener('PERIPHERAL_DISCONNECTED', this.handleStatusButtonUpdate); + } + + updateToolboxBlockValue (id, value) { + this.withToolboxUpdates(() => { + const block = this.workspace + .getFlyout() + .getWorkspace() + .getBlockById(id); + if (block) { + block.inputList[0].fieldRow[0].setValue(value); + } + }); + } + + onTargetsUpdate () { + if (this.props.vm.editingTarget && this.workspace.getFlyout()) { + ['glide', 'move', 'set'].forEach(prefix => { + this.updateToolboxBlockValue(`${prefix}x`, Math.round(this.props.vm.editingTarget.x).toString()); + this.updateToolboxBlockValue(`${prefix}y`, Math.round(this.props.vm.editingTarget.y).toString()); + }); + } + } + onWorkspaceMetricsChange () { + const target = this.props.vm.editingTarget; + if (target && target.id) { + // Dispatch updateMetrics later, since onWorkspaceMetricsChange may be (very indirectly) + // called from a reducer, i.e. when you create a custom procedure. + // TODO: Is this a vehement hack? + setTimeout(() => { + this.props.updateMetrics({ + targetID: target.id, + scrollX: this.workspace.scrollX, + scrollY: this.workspace.scrollY, + scale: this.workspace.scale + }); + }, 0); + } + } + onScriptGlowOn (data) { + this.workspace.glowStack(data.id, true); + } + onScriptGlowOff (data) { + this.workspace.glowStack(data.id, false); + } + onBlockGlowOn (data) { + this.workspace.glowBlock(data.id, true); + } + onBlockGlowOff (data) { + this.workspace.glowBlock(data.id, false); + } + onVisualReport (data) { + this.workspace.reportValue(data.id, data.value); + } + getToolboxXML () { + // Use try/catch because this requires digging pretty deep into the VM + // Code inside intentionally ignores several error situations (no stage, etc.) + // Because they would get caught by this try/catch + try { + let {editingTarget: target, runtime} = this.props.vm; + const stage = runtime.getTargetForStage(); + if (!target) target = stage; // If no editingTarget, use the stage + + const stageCostumes = stage.getCostumes(); + const targetCostumes = target.getCostumes(); + const targetSounds = target.getSounds(); + const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(target); + return makeToolboxXML(false, target.isStage, target.id, dynamicBlocksXML, + targetCostumes[targetCostumes.length - 1].name, + stageCostumes[stageCostumes.length - 1].name, + targetSounds.length > 0 ? targetSounds[targetSounds.length - 1].name : '' + ); + } catch { + return null; + } + } + onWorkspaceUpdate (data) { + // When we change sprites, update the toolbox to have the new sprite's blocks + const toolboxXML = this.getToolboxXML(); + if (toolboxXML) { + this.props.updateToolboxState(toolboxXML); + } + + if (this.props.vm.editingTarget && !this.props.workspaceMetrics.targets[this.props.vm.editingTarget.id]) { + this.onWorkspaceMetricsChange(); + } + + // Remove and reattach the workspace listener (but allow flyout events) + this.workspace.removeChangeListener(this.props.vm.blockListener); + const dom = this.ScratchBlocks.Xml.textToDom(data.xml); + try { + this.ScratchBlocks.Xml.clearWorkspaceAndLoadFromXml(dom, this.workspace); + } catch (error) { + // The workspace is likely incomplete. What did update should be + // functional. + // + // Instead of throwing the error, by logging it and continuing as + // normal lets the other workspace update processes complete in the + // gui and vm, which lets the vm run even if the workspace is + // incomplete. Throwing the error would keep things like setting the + // correct editing target from happening which can interfere with + // some blocks and processes in the vm. + if (error.message) { + error.message = `Workspace Update Error: ${error.message}`; + } + log.error(error); + } + this.workspace.addChangeListener(this.props.vm.blockListener); + + if (this.props.vm.editingTarget && this.props.workspaceMetrics.targets[this.props.vm.editingTarget.id]) { + const {scrollX, scrollY, scale} = this.props.workspaceMetrics.targets[this.props.vm.editingTarget.id]; + this.workspace.scrollX = scrollX; + this.workspace.scrollY = scrollY; + this.workspace.scale = scale; + this.workspace.resize(); + } + + // Clear the undo state of the workspace since this is a + // fresh workspace and we don't want any changes made to another sprites + // workspace to be 'undone' here. + this.workspace.clearUndo(); + } + handleMonitorsUpdate (monitors) { + // Update the checkboxes of the relevant monitors. + // TODO: What about monitors that have fields? See todo in scratch-vm blocks.js changeBlock: + // https://github.com/LLK/scratch-vm/blob/2373f9483edaf705f11d62662f7bb2a57fbb5e28/src/engine/blocks.js#L569-L576 + const flyout = this.workspace.getFlyout(); + for (const monitor of monitors.values()) { + const blockId = monitor.get('id'); + const isVisible = monitor.get('visible'); + flyout.setCheckboxState(blockId, isVisible); + // We also need to update the isMonitored flag for this block on the VM, since it's used to determine + // whether the checkbox is activated or not when the checkbox is re-displayed (e.g. local variables/blocks + // when switching between sprites). + const block = this.props.vm.runtime.monitorBlocks.getBlock(blockId); + if (block) { + block.isMonitored = isVisible; + } + } + } + handleExtensionAdded (categoryInfo) { + const defineBlocks = blockInfoArray => { + if (blockInfoArray && blockInfoArray.length > 0) { + const staticBlocksJson = []; + const dynamicBlocksInfo = []; + blockInfoArray.forEach(blockInfo => { + if (blockInfo.info && blockInfo.info.isDynamic) { + dynamicBlocksInfo.push(blockInfo); + } else if (blockInfo.json) { + staticBlocksJson.push(blockInfo.json); + } + // otherwise it's a non-block entry such as '---' + }); + + this.ScratchBlocks.defineBlocksWithJsonArray(staticBlocksJson); + dynamicBlocksInfo.forEach(blockInfo => { + // This is creating the block factory / constructor -- NOT a specific instance of the block. + // The factory should only know static info about the block: the category info and the opcode. + // Anything else will be picked up from the XML attached to the block instance. + const extendedOpcode = `${categoryInfo.id}_${blockInfo.info.opcode}`; + const blockDefinition = + defineDynamicBlock(this.ScratchBlocks, categoryInfo, blockInfo, extendedOpcode); + this.ScratchBlocks.Blocks[extendedOpcode] = blockDefinition; + }); + } + }; + + // scratch-blocks implements a menu or custom field as a special kind of block ("shadow" block) + // these actually define blocks and MUST run regardless of the UI state + defineBlocks( + Object.getOwnPropertyNames(categoryInfo.customFieldTypes) + .map(fieldTypeName => categoryInfo.customFieldTypes[fieldTypeName].scratchBlocksDefinition)); + defineBlocks(categoryInfo.menus); + defineBlocks(categoryInfo.blocks); + + // Update the toolbox with new blocks if possible + const toolboxXML = this.getToolboxXML(); + if (toolboxXML) { + this.props.updateToolboxState(toolboxXML); + } + } + handleBlocksInfoUpdate (categoryInfo) { + // @todo Later we should replace this to avoid all the warnings from redefining blocks. + this.handleExtensionAdded(categoryInfo); + } + handleCategorySelected (categoryId) { + const extension = extensionData.find(ext => ext.extensionId === categoryId); + if (extension && extension.launchPeripheralConnectionFlow) { + this.handleConnectionModalStart(categoryId); + } + + this.withToolboxUpdates(() => { + this.workspace.toolbox_.setSelectedCategoryById(categoryId); + }); + } + setBlocks (blocks) { + this.blocks = blocks; + } + handlePromptStart (message, defaultValue, callback, optTitle, optVarType) { + const p = {prompt: {callback, message, defaultValue}}; + p.prompt.title = optTitle ? optTitle : + this.ScratchBlocks.Msg.VARIABLE_MODAL_TITLE; + p.prompt.varType = typeof optVarType === 'string' ? + optVarType : this.ScratchBlocks.SCALAR_VARIABLE_TYPE; + p.prompt.showVariableOptions = // This flag means that we should show variable/list options about scope + optVarType !== this.ScratchBlocks.BROADCAST_MESSAGE_VARIABLE_TYPE && + p.prompt.title !== this.ScratchBlocks.Msg.RENAME_VARIABLE_MODAL_TITLE && + p.prompt.title !== this.ScratchBlocks.Msg.RENAME_LIST_MODAL_TITLE; + p.prompt.showCloudOption = (optVarType === this.ScratchBlocks.SCALAR_VARIABLE_TYPE) && this.props.canUseCloud; + this.setState(p); + } + handleConnectionModalStart (extensionId) { + this.props.onOpenConnectionModal(extensionId); + } + handleStatusButtonUpdate () { + this.ScratchBlocks.refreshStatusButtons(this.workspace); + } + handleOpenSoundRecorder () { + this.props.onOpenSoundRecorder(); + } + + /* + * Pass along information about proposed name and variable options (scope and isCloud) + * and additional potentially conflicting variable names from the VM + * to the variable validation prompt callback used in scratch-blocks. + */ + handlePromptCallback (input, variableOptions) { + this.state.prompt.callback( + input, + this.props.vm.runtime.getAllVarNamesOfType(this.state.prompt.varType), + variableOptions); + this.handlePromptClose(); + } + handlePromptClose () { + this.setState({prompt: null}); + } + handleCustomProceduresClose (data) { + this.props.onRequestCloseCustomProcedures(data); + const ws = this.workspace; + ws.refreshToolboxSelection_(); + ws.toolbox_.scrollToCategoryById('myBlocks'); + } + handleDrop (dragInfo) { + fetch(dragInfo.payload.bodyUrl) + .then(response => response.json()) + .then(blocks => this.props.vm.shareBlocksToTarget(blocks, this.props.vm.editingTarget.id)) + .then(() => { + this.props.vm.refreshWorkspace(); + this.updateToolbox(); // To show new variables/custom blocks + }); + } + render () { + /* eslint-disable no-unused-vars */ + const { + anyModalVisible, + canUseCloud, + customProceduresVisible, + extensionLibraryVisible, + options, + stageSize, + vm, + isRtl, + isVisible, + onActivateColorPicker, + onOpenConnectionModal, + onOpenSoundRecorder, + updateToolboxState, + onActivateCustomProcedures, + onRequestCloseExtensionLibrary, + onRequestCloseCustomProcedures, + toolboxXML, + updateMetrics: updateMetricsProp, + useCatBlocks, + workspaceMetrics, + ...props + } = this.props; + /* eslint-enable no-unused-vars */ + return ( + + + {this.state.prompt ? ( + + ) : null} + {extensionLibraryVisible ? ( + + ) : null} + {customProceduresVisible ? ( + + ) : null} + + ); + } +} + +Blocks.propTypes = { + anyModalVisible: PropTypes.bool, + canUseCloud: PropTypes.bool, + customProceduresVisible: PropTypes.bool, + extensionLibraryVisible: PropTypes.bool, + isRtl: PropTypes.bool, + isVisible: PropTypes.bool, + locale: PropTypes.string.isRequired, + messages: PropTypes.objectOf(PropTypes.string), + onActivateColorPicker: PropTypes.func, + onActivateCustomProcedures: PropTypes.func, + onOpenConnectionModal: PropTypes.func, + onOpenSoundRecorder: PropTypes.func, + onRequestCloseCustomProcedures: PropTypes.func, + onRequestCloseExtensionLibrary: PropTypes.func, + options: PropTypes.shape({ + media: PropTypes.string, + zoom: PropTypes.shape({ + controls: PropTypes.bool, + wheel: PropTypes.bool, + startScale: PropTypes.number + }), + colours: PropTypes.shape({ + workspace: PropTypes.string, + flyout: PropTypes.string, + toolbox: PropTypes.string, + toolboxSelected: PropTypes.string, + scrollbar: PropTypes.string, + scrollbarHover: PropTypes.string, + insertionMarker: PropTypes.string, + insertionMarkerOpacity: PropTypes.number, + fieldShadow: PropTypes.string, + dragShadowOpacity: PropTypes.number + }), + comments: PropTypes.bool, + collapse: PropTypes.bool + }), + stageSize: PropTypes.oneOf(Object.keys(STAGE_DISPLAY_SIZES)).isRequired, + toolboxXML: PropTypes.string, + updateMetrics: PropTypes.func, + updateToolboxState: PropTypes.func, + useCatBlocks: PropTypes.bool, + vm: PropTypes.instanceOf(VM).isRequired, + workspaceMetrics: PropTypes.shape({ + targets: PropTypes.objectOf(PropTypes.object) + }) +}; + +Blocks.defaultOptions = { + zoom: { + controls: true, + wheel: true, + startScale: BLOCKS_DEFAULT_SCALE + }, + grid: { + spacing: 40, + length: 2, + colour: '#ddd' + }, + colours: { + workspace: '#F9F9F9', + flyout: '#F9F9F9', + toolbox: '#FFFFFF', + toolboxSelected: '#E9EEF2', + scrollbar: '#CECDCE', + scrollbarHover: '#CECDCE', + insertionMarker: '#000000', + insertionMarkerOpacity: 0.2, + fieldShadow: 'rgba(255, 255, 255, 0.3)', + dragShadowOpacity: 0.6 + }, + comments: true, + collapse: false, + sounds: false +}; + +Blocks.defaultProps = { + isVisible: true, + options: Blocks.defaultOptions +}; + +const mapStateToProps = state => ({ + anyModalVisible: ( + Object.keys(state.scratchGui.modals).some(key => state.scratchGui.modals[key]) || + state.scratchGui.mode.isFullScreen + ), + extensionLibraryVisible: state.scratchGui.modals.extensionLibrary, + isRtl: state.locales.isRtl, + locale: state.locales.locale, + messages: state.locales.messages, + toolboxXML: state.scratchGui.toolbox.toolboxXML, + customProceduresVisible: state.scratchGui.customProcedures.active, + workspaceMetrics: state.scratchGui.workspaceMetrics, + useCatBlocks: isTimeTravel2020(state) +}); + +const mapDispatchToProps = dispatch => ({ + onActivateColorPicker: callback => dispatch(activateColorPicker(callback)), + onActivateCustomProcedures: (data, callback) => dispatch(activateCustomProcedures(data, callback)), + onOpenConnectionModal: id => { + dispatch(setConnectionModalExtensionId(id)); + dispatch(openConnectionModal()); + }, + onOpenSoundRecorder: () => { + dispatch(activateTab(SOUNDS_TAB_INDEX)); + dispatch(openSoundRecorder()); + }, + onRequestCloseExtensionLibrary: () => { + dispatch(closeExtensionLibrary()); + }, + onRequestCloseCustomProcedures: data => { + dispatch(deactivateCustomProcedures(data)); + }, + updateToolboxState: toolboxXML => { + dispatch(updateToolbox(toolboxXML)); + }, + updateMetrics: metrics => { + dispatch(updateMetrics(metrics)); + } +}); + +export default errorBoundaryHOC('Blocks')( + connect( + mapStateToProps, + mapDispatchToProps + )(Blocks) +); diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx new file mode 100644 index 0000000000..caa3de483d --- /dev/null +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -0,0 +1,461 @@ +import bindAll from 'lodash.bindall'; +import PropTypes from 'prop-types'; +import React from 'react'; +import Renderer from 'scratch-render'; +import VM from 'scratch-vm'; +import {connect} from 'react-redux'; + +import {STAGE_DISPLAY_SIZES} from '../lib/layout-constants'; +import {getEventXY} from '../lib/touch-utils'; +import VideoProvider from '../lib/video/video-provider'; +import {BitmapAdapter as V2BitmapAdapter} from 'scratch-svg-renderer'; + +import StageComponent from '../components/stage/stage.jsx'; + +import { + activateColorPicker, + deactivateColorPicker +} from '../reducers/color-picker'; + +const colorPickerRadius = 20; +const dragThreshold = 3; // Same as the block drag threshold + +class Stage extends React.Component { + constructor (props) { + super(props); + bindAll(this, [ + 'attachMouseEvents', + 'cancelMouseDownTimeout', + 'detachMouseEvents', + 'handleDoubleClick', + 'handleQuestionAnswered', + 'onMouseUp', + 'onMouseMove', + 'onMouseDown', + 'onStartDrag', + 'onStopDrag', + 'onWheel', + 'updateRect', + 'questionListener', + 'setDragCanvas', + 'clearDragCanvas', + 'drawDragCanvas', + 'positionDragCanvas' + ]); + this.state = { + mouseDownTimeoutId: null, + mouseDownPosition: null, + isDragging: false, + dragOffset: null, + dragId: null, + colorInfo: null, + question: null + }; + if (this.props.vm.renderer) { + this.renderer = this.props.vm.renderer; + this.canvas = this.renderer.canvas; + } else { + this.canvas = document.createElement('canvas'); + this.renderer = new Renderer(this.canvas); + this.props.vm.attachRenderer(this.renderer); + + // Only attach a video provider once because it is stateful + this.props.vm.setVideoProvider(new VideoProvider()); + + // Calling draw a single time before any project is loaded just makes + // the canvas white instead of solid black–needed because it is not + // possible to use CSS to style the canvas to have a different + // default color + this.props.vm.renderer.draw(); + } + this.props.vm.attachV2BitmapAdapter(new V2BitmapAdapter()); + } + componentDidMount () { + this.attachRectEvents(); + this.attachMouseEvents(this.canvas); + this.updateRect(); + this.props.vm.runtime.addListener('QUESTION', this.questionListener); + } + shouldComponentUpdate (nextProps, nextState) { + return this.props.stageSize !== nextProps.stageSize || + this.props.isColorPicking !== nextProps.isColorPicking || + this.state.colorInfo !== nextState.colorInfo || + this.props.isFullScreen !== nextProps.isFullScreen || + this.state.question !== nextState.question || + this.props.micIndicator !== nextProps.micIndicator || + this.props.isStarted !== nextProps.isStarted; + } + componentDidUpdate (prevProps) { + if (this.props.isColorPicking && !prevProps.isColorPicking) { + this.startColorPickingLoop(); + } else if (!this.props.isColorPicking && prevProps.isColorPicking) { + this.stopColorPickingLoop(); + } + this.updateRect(); + this.renderer.resize(this.rect.width, this.rect.height); + } + componentWillUnmount () { + this.detachMouseEvents(this.canvas); + this.detachRectEvents(); + this.stopColorPickingLoop(); + this.props.vm.runtime.removeListener('QUESTION', this.questionListener); + } + questionListener (question) { + this.setState({question: question}); + } + handleQuestionAnswered (answer) { + this.setState({question: null}, () => { + this.props.vm.runtime.emit('ANSWER', answer); + }); + } + startColorPickingLoop () { + this.intervalId = setInterval(() => { + if (typeof this.pickX === 'number') { + this.setState({colorInfo: this.getColorInfo(this.pickX, this.pickY)}); + } + }, 30); + } + stopColorPickingLoop () { + clearInterval(this.intervalId); + } + attachMouseEvents (canvas) { + document.addEventListener('mousemove', this.onMouseMove); + document.addEventListener('mouseup', this.onMouseUp); + document.addEventListener('touchmove', this.onMouseMove); + document.addEventListener('touchend', this.onMouseUp); + canvas.addEventListener('mousedown', this.onMouseDown); + canvas.addEventListener('touchstart', this.onMouseDown); + canvas.addEventListener('wheel', this.onWheel); + } + detachMouseEvents (canvas) { + document.removeEventListener('mousemove', this.onMouseMove); + document.removeEventListener('mouseup', this.onMouseUp); + document.removeEventListener('touchmove', this.onMouseMove); + document.removeEventListener('touchend', this.onMouseUp); + canvas.removeEventListener('mousedown', this.onMouseDown); + canvas.removeEventListener('touchstart', this.onMouseDown); + canvas.removeEventListener('wheel', this.onWheel); + } + attachRectEvents () { + window.addEventListener('resize', this.updateRect); + window.addEventListener('scroll', this.updateRect); + } + detachRectEvents () { + window.removeEventListener('resize', this.updateRect); + window.removeEventListener('scroll', this.updateRect); + } + updateRect () { + this.rect = this.canvas.getBoundingClientRect(); + } + getScratchCoords (x, y) { + const nativeSize = this.renderer.getNativeSize(); + return [ + (nativeSize[0] / this.rect.width) * (x - (this.rect.width / 2)), + (nativeSize[1] / this.rect.height) * (y - (this.rect.height / 2)) + ]; + } + getColorInfo (x, y) { + return { + x: x, + y: y, + ...this.renderer.extractColor(x, y, colorPickerRadius) + }; + } + handleDoubleClick (e) { + const {x, y} = getEventXY(e); + // Set editing target from cursor position, if clicking on a sprite. + const mousePosition = [x - this.rect.left, y - this.rect.top]; + const drawableId = this.renderer.pick(mousePosition[0], mousePosition[1]); + if (drawableId === null) return; + const targetId = this.props.vm.getTargetIdForDrawableId(drawableId); + if (targetId === null) return; + this.props.vm.setEditingTarget(targetId); + } + onMouseMove (e) { + const {x, y} = getEventXY(e); + const mousePosition = [x - this.rect.left, y - this.rect.top]; + + if (this.props.isColorPicking) { + // Set the pickX/Y for the color picker loop to pick up + this.pickX = mousePosition[0]; + this.pickY = mousePosition[1]; + } + + if (this.state.mouseDown && !this.state.isDragging) { + const distanceFromMouseDown = Math.sqrt( + Math.pow(mousePosition[0] - this.state.mouseDownPosition[0], 2) + + Math.pow(mousePosition[1] - this.state.mouseDownPosition[1], 2) + ); + if (distanceFromMouseDown > dragThreshold) { + this.cancelMouseDownTimeout(); + this.onStartDrag(...this.state.mouseDownPosition); + } + } + if (this.state.mouseDown && this.state.isDragging) { + // Editor drag style only updates the drag canvas, does full update at the end of drag + // Non-editor drag style just updates the sprite continuously. + if (this.props.useEditorDragStyle) { + this.positionDragCanvas(mousePosition[0], mousePosition[1]); + } else { + const spritePosition = this.getScratchCoords(mousePosition[0], mousePosition[1]); + this.props.vm.postSpriteInfo({ + x: spritePosition[0] + this.state.dragOffset[0], + y: -(spritePosition[1] + this.state.dragOffset[1]), + force: true + }); + } + } + const coordinates = { + x: mousePosition[0], + y: mousePosition[1], + canvasWidth: this.rect.width, + canvasHeight: this.rect.height + }; + this.props.vm.postIOData('mouse', coordinates); + } + onMouseUp (e) { + const {x, y} = getEventXY(e); + const mousePosition = [x - this.rect.left, y - this.rect.top]; + this.cancelMouseDownTimeout(); + this.setState({ + mouseDown: false, + mouseDownPosition: null + }); + const data = { + isDown: false, + x: x - this.rect.left, + y: y - this.rect.top, + canvasWidth: this.rect.width, + canvasHeight: this.rect.height, + wasDragged: this.state.isDragging + }; + if (this.state.isDragging) { + this.onStopDrag(mousePosition[0], mousePosition[1]); + } + this.props.vm.postIOData('mouse', data); + + if (this.props.isColorPicking && + mousePosition[0] > 0 && mousePosition[0] < this.rect.width && + mousePosition[1] > 0 && mousePosition[1] < this.rect.height + ) { + const {r, g, b} = this.state.colorInfo.color; + const componentToString = c => { + const hex = c.toString(16); + return hex.length === 1 ? `0${hex}` : hex; + }; + const colorString = `#${componentToString(r)}${componentToString(g)}${componentToString(b)}`; + this.props.onDeactivateColorPicker(colorString); + this.setState({colorInfo: null}); + this.pickX = null; + this.pickY = null; + } + } + onMouseDown (e) { + this.updateRect(); + const {x, y} = getEventXY(e); + const mousePosition = [x - this.rect.left, y - this.rect.top]; + if (this.props.isColorPicking) { + // Set the pickX/Y for the color picker loop to pick up + this.pickX = mousePosition[0]; + this.pickY = mousePosition[1]; + // Immediately update the color picker info + this.setState({colorInfo: this.getColorInfo(this.pickX, this.pickY)}); + } else { + if (e.button === 0 || (window.TouchEvent && e instanceof TouchEvent)) { + this.setState({ + mouseDown: true, + mouseDownPosition: mousePosition, + mouseDownTimeoutId: setTimeout( + this.onStartDrag.bind(this, mousePosition[0], mousePosition[1]), + 400 + ) + }); + } + const data = { + isDown: true, + x: mousePosition[0], + y: mousePosition[1], + canvasWidth: this.rect.width, + canvasHeight: this.rect.height + }; + this.props.vm.postIOData('mouse', data); + if (e.preventDefault) { + // Prevent default to prevent touch from dragging page + e.preventDefault(); + // But we do want any active input to be blurred + if (document.activeElement && document.activeElement.blur) { + document.activeElement.blur(); + } + } + } + } + onWheel (e) { + const data = { + deltaX: e.deltaX, + deltaY: e.deltaY + }; + this.props.vm.postIOData('mouseWheel', data); + } + cancelMouseDownTimeout () { + if (this.state.mouseDownTimeoutId !== null) { + clearTimeout(this.state.mouseDownTimeoutId); + } + this.setState({mouseDownTimeoutId: null}); + } + /** + * Initialize the position of the "dragged sprite" canvas + * @param {DrawableExtraction} drawableData The data returned from renderer.extractDrawableScreenSpace + * @param {number} x The x position of the initial drag event + * @param {number} y The y position of the initial drag event + */ + drawDragCanvas (drawableData, x, y) { + const { + imageData, + x: boundsX, + y: boundsY, + width: boundsWidth, + height: boundsHeight + } = drawableData; + this.dragCanvas.width = imageData.width; + this.dragCanvas.height = imageData.height; + // On high-DPI devices, the canvas size in layout-pixels is not equal to the size of the extracted data. + this.dragCanvas.style.width = `${boundsWidth}px`; + this.dragCanvas.style.height = `${boundsHeight}px`; + + this.dragCanvas.getContext('2d').putImageData(imageData, 0, 0); + // Position so that pick location is at (0, 0) so that positionDragCanvas() + // can use translation to move to mouse position smoothly. + this.dragCanvas.style.left = `${boundsX - x}px`; + this.dragCanvas.style.top = `${boundsY - y}px`; + this.dragCanvas.style.display = 'block'; + } + clearDragCanvas () { + this.dragCanvas.width = this.dragCanvas.height = 0; + this.dragCanvas.style.display = 'none'; + } + positionDragCanvas (mouseX, mouseY) { + // mouseX/Y are relative to stage top/left, and dragCanvas is already + // positioned so that the pick location is at (0,0). + this.dragCanvas.style.transform = `translate(${mouseX}px, ${mouseY}px)`; + } + onStartDrag (x, y) { + if (this.state.dragId) return; + const drawableId = this.renderer.pick(x, y); + if (drawableId === null) return; + const targetId = this.props.vm.getTargetIdForDrawableId(drawableId); + if (targetId === null) return; + + const target = this.props.vm.runtime.getTargetById(targetId); + + // Do not start drag unless in editor drag mode or target is draggable + if (!(this.props.useEditorDragStyle || target.draggable)) return; + + // Dragging always brings the target to the front + target.goToFront(); + + const [scratchMouseX, scratchMouseY] = this.getScratchCoords(x, y); + const offsetX = target.x - scratchMouseX; + const offsetY = -(target.y + scratchMouseY); + + this.props.vm.startDrag(targetId); + this.setState({ + isDragging: true, + dragId: targetId, + dragOffset: [offsetX, offsetY] + }); + if (this.props.useEditorDragStyle) { + // Extract the drawable art + const drawableData = this.renderer.extractDrawableScreenSpace(drawableId); + this.drawDragCanvas(drawableData, x, y); + this.positionDragCanvas(x, y); + this.props.vm.postSpriteInfo({visible: false}); + this.props.vm.renderer.draw(); + } + } + onStopDrag (mouseX, mouseY) { + const dragId = this.state.dragId; + const commonStopDragActions = () => { + this.props.vm.stopDrag(dragId); + this.setState({ + isDragging: false, + dragOffset: null, + dragId: null + }); + }; + if (this.props.useEditorDragStyle) { + // Need to sequence these actions to prevent flickering. + const spriteInfo = {visible: true}; + // First update the sprite position if dropped in the stage. + if (mouseX > 0 && mouseX < this.rect.width && + mouseY > 0 && mouseY < this.rect.height) { + const spritePosition = this.getScratchCoords(mouseX, mouseY); + spriteInfo.x = spritePosition[0] + this.state.dragOffset[0]; + spriteInfo.y = -(spritePosition[1] + this.state.dragOffset[1]); + spriteInfo.force = true; + } + this.props.vm.postSpriteInfo(spriteInfo); + // Then clear the dragging canvas and stop drag (potentially slow if selecting sprite) + this.clearDragCanvas(); + commonStopDragActions(); + this.props.vm.renderer.draw(); + } else { + commonStopDragActions(); + } + } + setDragCanvas (canvas) { + this.dragCanvas = canvas; + } + render () { + const { + vm, // eslint-disable-line no-unused-vars + onActivateColorPicker, // eslint-disable-line no-unused-vars + ...props + } = this.props; + return ( + + ); + } +} + +Stage.propTypes = { + isColorPicking: PropTypes.bool, + isFullScreen: PropTypes.bool.isRequired, + isStarted: PropTypes.bool, + micIndicator: PropTypes.bool, + onActivateColorPicker: PropTypes.func, + onDeactivateColorPicker: PropTypes.func, + stageSize: PropTypes.oneOf(Object.keys(STAGE_DISPLAY_SIZES)).isRequired, + useEditorDragStyle: PropTypes.bool, + vm: PropTypes.instanceOf(VM).isRequired +}; + +Stage.defaultProps = { + useEditorDragStyle: true +}; + +const mapStateToProps = state => ({ + isColorPicking: state.scratchGui.colorPicker.active, + isFullScreen: state.scratchGui.mode.isFullScreen, + isStarted: state.scratchGui.vmStatus.started, + micIndicator: state.scratchGui.micIndicator, + // Do not use editor drag style in fullscreen or player mode. + useEditorDragStyle: !(state.scratchGui.mode.isFullScreen || state.scratchGui.mode.isPlayerOnly) +}); + +const mapDispatchToProps = dispatch => ({ + onActivateColorPicker: () => dispatch(activateColorPicker()), + onDeactivateColorPicker: color => dispatch(deactivateColorPicker(color)) +}); + +export default connect( + mapStateToProps, + mapDispatchToProps +)(Stage); diff --git a/packages/scratch-gui/src/lib/alerts/index.jsx b/packages/scratch-gui/src/lib/alerts/index.jsx new file mode 100644 index 0000000000..dbd4f2ce7d --- /dev/null +++ b/packages/scratch-gui/src/lib/alerts/index.jsx @@ -0,0 +1,222 @@ +import React from 'react'; +import {FormattedMessage} from 'react-intl'; +import keyMirror from 'keymirror'; + +import successImage from '../assets/icon--success.svg'; + +const AlertTypes = keyMirror({ + STANDARD: null, + EXTENSION: null, + INLINE: null +}); + +const AlertLevels = { + SUCCESS: 'success', + INFO: 'info', + WARN: 'warn' +}; + +const alerts = [ + { + alertId: 'createSuccess', + alertType: AlertTypes.STANDARD, + clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', + 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], + content: ( + + ), + iconURL: successImage, + level: AlertLevels.SUCCESS, + maxDisplaySecs: 5 + }, + { + alertId: 'createCopySuccess', + alertType: AlertTypes.STANDARD, + clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', + 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], + content: ( + + ), + iconURL: successImage, + level: AlertLevels.SUCCESS, + maxDisplaySecs: 5 + }, + { + alertId: 'createRemixSuccess', + alertType: AlertTypes.STANDARD, + clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', + 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], + content: ( + + ), + iconURL: successImage, + level: AlertLevels.SUCCESS, + maxDisplaySecs: 5 + }, + { + alertId: 'creating', + alertType: AlertTypes.STANDARD, + clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', + 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], + content: ( + + ), + iconSpinner: true, + level: AlertLevels.SUCCESS + }, + { + alertId: 'creatingCopy', + alertType: AlertTypes.STANDARD, + clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', + 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], + content: ( + + ), + iconSpinner: true, + level: AlertLevels.SUCCESS + }, + { + alertId: 'creatingRemix', + alertType: AlertTypes.STANDARD, + clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', + 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], + content: ( + + ), + iconSpinner: true, + level: AlertLevels.SUCCESS + }, + { + alertId: 'creatingError', + clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', + 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], + closeButton: true, + content: ( + + ), + level: AlertLevels.WARN + }, + { + alertId: 'savingError', + clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy', + 'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'], + showDownload: true, + showSaveNow: true, + closeButton: false, + content: ( + + ), + level: AlertLevels.WARN + }, + { + alertId: 'saveSuccess', + alertType: AlertTypes.INLINE, + clearList: ['saveSuccess', 'saving', 'savingError'], + content: ( + + ), + iconURL: successImage, + level: AlertLevels.SUCCESS, + maxDisplaySecs: 3 + }, + { + alertId: 'saving', + alertType: AlertTypes.INLINE, + clearList: ['saveSuccess', 'saving', 'savingError'], + content: ( + + ), + iconSpinner: true, + level: AlertLevels.INFO + }, + { + alertId: 'cloudInfo', + alertType: AlertTypes.STANDARD, + clearList: ['cloudInfo'], + content: ( + + + + ) + }} + /> + ), + closeButton: true, + level: AlertLevels.SUCCESS, + maxDisplaySecs: 15 + }, + { + alertId: 'importingAsset', + alertType: AlertTypes.STANDARD, + clearList: [], + content: ( + + ), + iconSpinner: true, + level: AlertLevels.SUCCESS + } +]; + +export { + alerts as default, + AlertLevels, + AlertTypes +}; diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx new file mode 100644 index 0000000000..ba18b916b5 --- /dev/null +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -0,0 +1,321 @@ +import React from 'react'; +import {FormattedMessage} from 'react-intl'; + +import musicIconURL from './music/music.png'; +import musicInsetIconURL from './music/music-small.svg'; + +import penIconURL from './pen/pen.png'; +import penInsetIconURL from './pen/pen-small.svg'; + +import videoSensingIconURL from './videoSensing/video-sensing.png'; +import videoSensingInsetIconURL from './videoSensing/video-sensing-small.svg'; + +import text2speechIconURL from './text2speech/text2speech.png'; +import text2speechInsetIconURL from './text2speech/text2speech-small.svg'; + +import translateIconURL from './translate/translate.png'; +import translateInsetIconURL from './translate/translate-small.png'; + +import makeymakeyIconURL from './makeymakey/makeymakey.png'; +import makeymakeyInsetIconURL from './makeymakey/makeymakey-small.svg'; + +import microbitIconURL from './microbit/microbit.png'; +import microbitInsetIconURL from './microbit/microbit-small.svg'; +import microbitConnectionIconURL from './microbit/microbit-illustration.svg'; +import microbitConnectionSmallIconURL from './microbit/microbit-small.svg'; + +import ev3IconURL from './ev3/ev3.png'; +import ev3InsetIconURL from './ev3/ev3-small.svg'; +import ev3ConnectionIconURL from './ev3/ev3-hub-illustration.svg'; +import ev3ConnectionSmallIconURL from './ev3/ev3-small.svg'; + +import wedo2IconURL from './wedo2/wedo.png'; // TODO: Rename file names to match variable/prop names? +import wedo2InsetIconURL from './wedo2/wedo-small.svg'; +import wedo2ConnectionIconURL from './wedo2/wedo-illustration.svg'; +import wedo2ConnectionSmallIconURL from './wedo2/wedo-small.svg'; +import wedo2ConnectionTipIconURL from './wedo2/wedo-button-illustration.svg'; + +import boostIconURL from './boost/boost.png'; +import boostInsetIconURL from './boost/boost-small.svg'; +import boostConnectionIconURL from './boost/boost-illustration.svg'; +import boostConnectionSmallIconURL from './boost/boost-small.svg'; +import boostConnectionTipIconURL from './boost/boost-button-illustration.svg'; + +import gdxforIconURL from './gdxfor/gdxfor.png'; +import gdxforInsetIconURL from './gdxfor/gdxfor-small.svg'; +import gdxforConnectionIconURL from './gdxfor/gdxfor-illustration.svg'; +import gdxforConnectionSmallIconURL from './gdxfor/gdxfor-small.svg'; + +export default [ + { + name: ( + + ), + extensionId: 'music', + iconURL: musicIconURL, + insetIconURL: musicInsetIconURL, + description: ( + + ), + featured: true + }, + { + name: ( + + ), + extensionId: 'pen', + iconURL: penIconURL, + insetIconURL: penInsetIconURL, + description: ( + + ), + featured: true + }, + { + name: ( + + ), + extensionId: 'videoSensing', + iconURL: videoSensingIconURL, + insetIconURL: videoSensingInsetIconURL, + description: ( + + ), + featured: true + }, + { + name: ( + + ), + extensionId: 'text2speech', + collaborator: 'Amazon Web Services', + iconURL: text2speechIconURL, + insetIconURL: text2speechInsetIconURL, + description: ( + + ), + featured: true, + internetConnectionRequired: true + }, + { + name: ( + + ), + extensionId: 'translate', + collaborator: 'Google', + iconURL: translateIconURL, + insetIconURL: translateInsetIconURL, + description: ( + + ), + featured: true, + internetConnectionRequired: true + }, + { + name: 'Makey Makey', + extensionId: 'makeymakey', + collaborator: 'JoyLabz', + iconURL: makeymakeyIconURL, + insetIconURL: makeymakeyInsetIconURL, + description: ( + + ), + featured: true + }, + { + name: 'micro:bit', + extensionId: 'microbit', + collaborator: 'micro:bit', + iconURL: microbitIconURL, + insetIconURL: microbitInsetIconURL, + description: ( + + ), + featured: true, + disabled: false, + bluetoothRequired: true, + internetConnectionRequired: true, + launchPeripheralConnectionFlow: true, + useAutoScan: false, + connectionIconURL: microbitConnectionIconURL, + connectionSmallIconURL: microbitConnectionSmallIconURL, + connectingMessage: ( + + ), + helpLink: 'https://scratch.mit.edu/microbit' + }, + { + name: 'LEGO MINDSTORMS EV3', + extensionId: 'ev3', + collaborator: 'LEGO', + iconURL: ev3IconURL, + insetIconURL: ev3InsetIconURL, + description: ( + + ), + featured: true, + disabled: false, + bluetoothRequired: true, + internetConnectionRequired: true, + launchPeripheralConnectionFlow: true, + useAutoScan: false, + connectionIconURL: ev3ConnectionIconURL, + connectionSmallIconURL: ev3ConnectionSmallIconURL, + connectingMessage: ( + + ), + helpLink: 'https://scratch.mit.edu/ev3' + }, + { + name: 'LEGO BOOST', + extensionId: 'boost', + collaborator: 'LEGO', + iconURL: boostIconURL, + insetIconURL: boostInsetIconURL, + description: ( + + ), + featured: true, + disabled: false, + bluetoothRequired: true, + internetConnectionRequired: true, + launchPeripheralConnectionFlow: true, + useAutoScan: true, + connectionIconURL: boostConnectionIconURL, + connectionSmallIconURL: boostConnectionSmallIconURL, + connectionTipIconURL: boostConnectionTipIconURL, + connectingMessage: ( + + ), + helpLink: 'https://scratch.mit.edu/boost' + }, + { + name: 'LEGO Education WeDo 2.0', + extensionId: 'wedo2', + collaborator: 'LEGO', + iconURL: wedo2IconURL, + insetIconURL: wedo2InsetIconURL, + description: ( + + ), + featured: true, + disabled: false, + bluetoothRequired: true, + internetConnectionRequired: true, + launchPeripheralConnectionFlow: true, + useAutoScan: true, + connectionIconURL: wedo2ConnectionIconURL, + connectionSmallIconURL: wedo2ConnectionSmallIconURL, + connectionTipIconURL: wedo2ConnectionTipIconURL, + connectingMessage: ( + + ), + helpLink: 'https://scratch.mit.edu/wedo' + }, + { + name: 'Go Direct Force & Acceleration', + extensionId: 'gdxfor', + collaborator: 'Vernier', + iconURL: gdxforIconURL, + insetIconURL: gdxforInsetIconURL, + description: ( + + ), + featured: true, + disabled: false, + bluetoothRequired: true, + internetConnectionRequired: true, + launchPeripheralConnectionFlow: true, + useAutoScan: false, + connectionIconURL: gdxforConnectionIconURL, + connectionSmallIconURL: gdxforConnectionSmallIconURL, + connectingMessage: ( + + ), + helpLink: 'https://scratch.mit.edu/vernier' + } +]; diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx new file mode 100644 index 0000000000..b6207a5b7d --- /dev/null +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -0,0 +1,213 @@ +import bindAll from 'lodash.bindall'; +import PropTypes from 'prop-types'; +import React from 'react'; +import VM from 'scratch-vm'; + +import {connect} from 'react-redux'; + +import {updateTargets} from '../reducers/targets'; +import {updateBlockDrag} from '../reducers/block-drag'; +import {updateMonitors} from '../reducers/monitors'; +import {setProjectChanged, setProjectUnchanged} from '../reducers/project-changed'; +import {setRunningState, setTurboState, setStartedState} from '../reducers/vm-status'; +import {showExtensionAlert} from '../reducers/alerts'; +import {updateMicIndicator} from '../reducers/mic-indicator'; + +/* + * Higher Order Component to manage events emitted by the VM + * @param {React.Component} WrappedComponent component to manage VM events for + * @returns {React.Component} connected component with vm events bound to redux + */ +const vmListenerHOC = function (WrappedComponent) { + class VMListener extends React.Component { + constructor (props) { + super(props); + bindAll(this, [ + 'handleKeyDown', + 'handleKeyUp', + 'handleProjectChanged', + 'handleTargetsUpdate' + ]); + // We have to start listening to the vm here rather than in + // componentDidMount because the HOC mounts the wrapped component, + // so the HOC componentDidMount triggers after the wrapped component + // mounts. + // If the wrapped component uses the vm in componentDidMount, then + // we need to start listening before mounting the wrapped component. + this.props.vm.on('targetsUpdate', this.handleTargetsUpdate); + this.props.vm.on('MONITORS_UPDATE', this.props.onMonitorsUpdate); + this.props.vm.on('BLOCK_DRAG_UPDATE', this.props.onBlockDragUpdate); + this.props.vm.on('TURBO_MODE_ON', this.props.onTurboModeOn); + this.props.vm.on('TURBO_MODE_OFF', this.props.onTurboModeOff); + this.props.vm.on('PROJECT_RUN_START', this.props.onProjectRunStart); + this.props.vm.on('PROJECT_RUN_STOP', this.props.onProjectRunStop); + this.props.vm.on('PROJECT_CHANGED', this.handleProjectChanged); + this.props.vm.on('RUNTIME_STARTED', this.props.onRuntimeStarted); + this.props.vm.on('PROJECT_START', this.props.onGreenFlag); + this.props.vm.on('PERIPHERAL_CONNECTION_LOST_ERROR', this.props.onShowExtensionAlert); + this.props.vm.on('MIC_LISTENING', this.props.onMicListeningUpdate); + + } + componentDidMount () { + if (this.props.attachKeyboardEvents) { + document.addEventListener('keydown', this.handleKeyDown); + document.addEventListener('keyup', this.handleKeyUp); + } + this.props.vm.postIOData('userData', {username: this.props.username}); + } + componentDidUpdate (prevProps) { + if (prevProps.username !== this.props.username) { + this.props.vm.postIOData('userData', {username: this.props.username}); + } + + // Re-request a targets update when the shouldUpdateTargets state changes to true + // i.e. when the editor transitions out of fullscreen/player only modes + if (this.props.shouldUpdateTargets && !prevProps.shouldUpdateTargets) { + this.props.vm.emitTargetsUpdate(false /* Emit the event, but do not trigger project change */); + } + } + componentWillUnmount () { + this.props.vm.removeListener('PERIPHERAL_CONNECTION_LOST_ERROR', this.props.onShowExtensionAlert); + if (this.props.attachKeyboardEvents) { + document.removeEventListener('keydown', this.handleKeyDown); + document.removeEventListener('keyup', this.handleKeyUp); + } + } + handleProjectChanged () { + if (this.props.shouldUpdateProjectChanged && !this.props.projectChanged) { + this.props.onProjectChanged(); + } + } + handleTargetsUpdate (data) { + if (this.props.shouldUpdateTargets) { + this.props.onTargetsUpdate(data); + } + } + handleKeyDown (e) { + // Don't capture keys intended for Blockly inputs. + if (e.target !== document && e.target !== document.body) return; + + const key = (!e.key || e.key === 'Dead') ? e.keyCode : e.key; + this.props.vm.postIOData('keyboard', { + key: key, + isDown: true + }); + + // Prevent space/arrow key from scrolling the page. + if (e.keyCode === 32 || // 32=space + (e.keyCode >= 37 && e.keyCode <= 40)) { // 37, 38, 39, 40 are arrows + e.preventDefault(); + } + } + handleKeyUp (e) { + // Always capture up events, + // even those that have switched to other targets. + const key = (!e.key || e.key === 'Dead') ? e.keyCode : e.key; + this.props.vm.postIOData('keyboard', { + key: key, + isDown: false + }); + + // E.g., prevent scroll. + if (e.target !== document && e.target !== document.body) { + e.preventDefault(); + } + } + render () { + const { + /* eslint-disable no-unused-vars */ + attachKeyboardEvents, + projectChanged, + shouldUpdateTargets, + shouldUpdateProjectChanged, + onBlockDragUpdate, + onGreenFlag, + onKeyDown, + onKeyUp, + onMicListeningUpdate, + onMonitorsUpdate, + onTargetsUpdate, + onProjectChanged, + onProjectRunStart, + onProjectRunStop, + onProjectSaved, + onRuntimeStarted, + onTurboModeOff, + onTurboModeOn, + onShowExtensionAlert, + /* eslint-enable no-unused-vars */ + ...props + } = this.props; + return ; + } + } + VMListener.propTypes = { + attachKeyboardEvents: PropTypes.bool, + onBlockDragUpdate: PropTypes.func.isRequired, + onGreenFlag: PropTypes.func, + onKeyDown: PropTypes.func, + onKeyUp: PropTypes.func, + onMicListeningUpdate: PropTypes.func.isRequired, + onMonitorsUpdate: PropTypes.func.isRequired, + onProjectChanged: PropTypes.func.isRequired, + onProjectRunStart: PropTypes.func.isRequired, + onProjectRunStop: PropTypes.func.isRequired, + onProjectSaved: PropTypes.func.isRequired, + onRuntimeStarted: PropTypes.func.isRequired, + onShowExtensionAlert: PropTypes.func.isRequired, + onTargetsUpdate: PropTypes.func.isRequired, + onTurboModeOff: PropTypes.func.isRequired, + onTurboModeOn: PropTypes.func.isRequired, + projectChanged: PropTypes.bool, + shouldUpdateTargets: PropTypes.bool, + shouldUpdateProjectChanged: PropTypes.bool, + username: PropTypes.string, + vm: PropTypes.instanceOf(VM).isRequired + }; + VMListener.defaultProps = { + attachKeyboardEvents: true, + onGreenFlag: () => ({}) + }; + const mapStateToProps = state => ({ + projectChanged: state.scratchGui.projectChanged, + // Do not emit target or project updates in fullscreen or player only mode + // or when recording sounds (it leads to garbled recordings on low-power machines) + shouldUpdateTargets: !state.scratchGui.mode.isFullScreen && !state.scratchGui.mode.isPlayerOnly && + !state.scratchGui.modals.soundRecorder, + // Do not update the projectChanged state in fullscreen or player only mode + shouldUpdateProjectChanged: !state.scratchGui.mode.isFullScreen && !state.scratchGui.mode.isPlayerOnly, + vm: state.scratchGui.vm, + username: state.session && state.session.session && state.session.session.user ? + state.session.session.user.username : '' + }); + const mapDispatchToProps = dispatch => ({ + onTargetsUpdate: data => { + dispatch(updateTargets(data.targetList, data.editingTarget)); + }, + onMonitorsUpdate: monitorList => { + dispatch(updateMonitors(monitorList)); + }, + onBlockDragUpdate: areBlocksOverGui => { + dispatch(updateBlockDrag(areBlocksOverGui)); + }, + onProjectRunStart: () => dispatch(setRunningState(true)), + onProjectRunStop: () => dispatch(setRunningState(false)), + onProjectChanged: () => dispatch(setProjectChanged()), + onProjectSaved: () => dispatch(setProjectUnchanged()), + onRuntimeStarted: () => dispatch(setStartedState(true)), + onTurboModeOn: () => dispatch(setTurboState(true)), + onTurboModeOff: () => dispatch(setTurboState(false)), + onShowExtensionAlert: data => { + dispatch(showExtensionAlert(data)); + }, + onMicListeningUpdate: listening => { + dispatch(updateMicIndicator(listening)); + } + }); + return connect( + mapStateToProps, + mapDispatchToProps + )(VMListener); +}; + +export default vmListenerHOC; From 543da145642957af1c82cbdeb50362ea1f222d13 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 24 Apr 2023 15:22:05 -0400 Subject: [PATCH 1960/1971] Merge commit 'b3398d039935ee2520772164c09d1cfdf1be0298' as 'packages/scratch-vm' --- packages/scratch-vm/src/engine/runtime.js | 2662 +++++++++++++++++ .../extension-support/extension-manager.js | 440 +++ .../scratch-vm/src/sprites/rendered-target.js | 1115 +++++++ packages/scratch-vm/src/virtual-machine.js | 1574 ++++++++++ 4 files changed, 5791 insertions(+) create mode 100644 packages/scratch-vm/src/engine/runtime.js create mode 100644 packages/scratch-vm/src/extension-support/extension-manager.js create mode 100644 packages/scratch-vm/src/sprites/rendered-target.js create mode 100644 packages/scratch-vm/src/virtual-machine.js diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js new file mode 100644 index 0000000000..d79337caf8 --- /dev/null +++ b/packages/scratch-vm/src/engine/runtime.js @@ -0,0 +1,2662 @@ +const EventEmitter = require('events'); +const {OrderedMap} = require('immutable'); + +const ArgumentType = require('../extension-support/argument-type'); +const Blocks = require('./blocks'); +const BlocksRuntimeCache = require('./blocks-runtime-cache'); +const BlockType = require('../extension-support/block-type'); +const Profiler = require('./profiler'); +const Sequencer = require('./sequencer'); +const execute = require('./execute.js'); +const ScratchBlocksConstants = require('./scratch-blocks-constants'); +const TargetType = require('../extension-support/target-type'); +const Thread = require('./thread'); +const log = require('../util/log'); +const maybeFormatMessage = require('../util/maybe-format-message'); +const StageLayering = require('./stage-layering'); +const Variable = require('./variable'); +const xmlEscape = require('../util/xml-escape'); +const ScratchLinkWebSocket = require('../util/scratch-link-websocket'); + +// Virtual I/O devices. +const Clock = require('../io/clock'); +const Cloud = require('../io/cloud'); +const Keyboard = require('../io/keyboard'); +const Mouse = require('../io/mouse'); +const MouseWheel = require('../io/mouseWheel'); +const UserData = require('../io/userData'); +const Video = require('../io/video'); + +const StringUtil = require('../util/string-util'); +const uid = require('../util/uid'); + +const defaultBlockPackages = { + scratch3_control: require('../blocks/scratch3_control'), + scratch3_event: require('../blocks/scratch3_event'), + scratch3_looks: require('../blocks/scratch3_looks'), + scratch3_motion: require('../blocks/scratch3_motion'), + scratch3_operators: require('../blocks/scratch3_operators'), + scratch3_sound: require('../blocks/scratch3_sound'), + scratch3_sensing: require('../blocks/scratch3_sensing'), + scratch3_data: require('../blocks/scratch3_data'), + scratch3_procedures: require('../blocks/scratch3_procedures') +}; + +const defaultExtensionColors = ['#0FBD8C', '#0DA57A', '#0B8E69']; + +/** + * Information used for converting Scratch argument types into scratch-blocks data. + * @type {object.} + */ +const ArgumentTypeMap = (() => { + const map = {}; + map[ArgumentType.ANGLE] = { + shadow: { + type: 'math_angle', + // We specify fieldNames here so that we can pick + // create and populate a field with the defaultValue + // specified in the extension. + // When the `fieldName` property is not specified, + // the will be left out of the XML and + // the scratch-blocks defaults for that field will be + // used instead (e.g. default of 0 for number fields) + fieldName: 'NUM' + } + }; + map[ArgumentType.COLOR] = { + shadow: { + type: 'colour_picker', + fieldName: 'COLOUR' + } + }; + map[ArgumentType.NUMBER] = { + shadow: { + type: 'math_number', + fieldName: 'NUM' + } + }; + map[ArgumentType.STRING] = { + shadow: { + type: 'text', + fieldName: 'TEXT' + } + }; + map[ArgumentType.BOOLEAN] = { + check: 'Boolean' + }; + map[ArgumentType.MATRIX] = { + shadow: { + type: 'matrix', + fieldName: 'MATRIX' + } + }; + map[ArgumentType.NOTE] = { + shadow: { + type: 'note', + fieldName: 'NOTE' + } + }; + map[ArgumentType.IMAGE] = { + // Inline images are weird because they're not actually "arguments". + // They are more analagous to the label on a block. + fieldType: 'field_image' + }; + return map; +})(); + +/** + * A pair of functions used to manage the cloud variable limit, + * to be used when adding (or attempting to add) or removing a cloud variable. + * @typedef {object} CloudDataManager + * @property {function} canAddCloudVariable A function to call to check that + * a cloud variable can be added. + * @property {function} addCloudVariable A function to call to track a new + * cloud variable on the runtime. + * @property {function} removeCloudVariable A function to call when + * removing an existing cloud variable. + * @property {function} hasCloudVariables A function to call to check that + * the runtime has any cloud variables. + */ + +/** + * Creates and manages cloud variable limit in a project, + * and returns two functions to be used to add a new + * cloud variable (while checking that it can be added) + * and remove an existing cloud variable. + * These are to be called whenever attempting to create or delete + * a cloud variable. + * @return {CloudDataManager} The functions to be used when adding or removing a + * cloud variable. + */ +const cloudDataManager = () => { + const limit = 10; + let count = 0; + + const canAddCloudVariable = () => count < limit; + + const addCloudVariable = () => { + count++; + }; + + const removeCloudVariable = () => { + count--; + }; + + const hasCloudVariables = () => count > 0; + + return { + canAddCloudVariable, + addCloudVariable, + removeCloudVariable, + hasCloudVariables + }; +}; + +/** + * Numeric ID for Runtime._step in Profiler instances. + * @type {number} + */ +let stepProfilerId = -1; + +/** + * Numeric ID for Sequencer.stepThreads in Profiler instances. + * @type {number} + */ +let stepThreadsProfilerId = -1; + +/** + * Numeric ID for RenderWebGL.draw in Profiler instances. + * @type {number} + */ +let rendererDrawProfilerId = -1; + +/** + * Manages targets, scripts, and the sequencer. + * @constructor + */ +class Runtime extends EventEmitter { + constructor () { + super(); + + /** + * Target management and storage. + * @type {Array.} + */ + this.targets = []; + + /** + * Targets in reverse order of execution. Shares its order with drawables. + * @type {Array.} + */ + this.executableTargets = []; + + /** + * A list of threads that are currently running in the VM. + * Threads are added when execution starts and pruned when execution ends. + * @type {Array.} + */ + this.threads = []; + + /** @type {!Sequencer} */ + this.sequencer = new Sequencer(this); + + /** + * Storage container for flyout blocks. + * These will execute on `_editingTarget.` + * @type {!Blocks} + */ + this.flyoutBlocks = new Blocks(this, true /* force no glow */); + + /** + * Storage container for monitor blocks. + * These will execute on a target maybe + * @type {!Blocks} + */ + this.monitorBlocks = new Blocks(this, true /* force no glow */); + + /** + * Currently known editing target for the VM. + * @type {?Target} + */ + this._editingTarget = null; + + /** + * Map to look up a block primitive's implementation function by its opcode. + * This is a two-step lookup: package name first, then primitive name. + * @type {Object.} + */ + this._primitives = {}; + + /** + * Map to look up all block information by extended opcode. + * @type {Array.} + * @private + */ + this._blockInfo = []; + + /** + * Map to look up hat blocks' metadata. + * Keys are opcode for hat, values are metadata objects. + * @type {Object.} + */ + this._hats = {}; + + /** + * A list of script block IDs that were glowing during the previous frame. + * @type {!Array.} + */ + this._scriptGlowsPreviousFrame = []; + + /** + * Number of non-monitor threads running during the previous frame. + * @type {number} + */ + this._nonMonitorThreadCount = 0; + + /** + * All threads that finished running and were removed from this.threads + * by behaviour in Sequencer.stepThreads. + * @type {Array} + */ + this._lastStepDoneThreads = null; + + /** + * Currently known number of clones, used to enforce clone limit. + * @type {number} + */ + this._cloneCounter = 0; + + /** + * Flag to emit a targets update at the end of a step. When target data + * changes, this flag is set to true. + * @type {boolean} + */ + this._refreshTargets = false; + + /** + * Map to look up all monitor block information by opcode. + * @type {object} + * @private + */ + this.monitorBlockInfo = {}; + + /** + * Ordered map of all monitors, which are MonitorReporter objects. + */ + this._monitorState = OrderedMap({}); + + /** + * Monitor state from last tick + */ + this._prevMonitorState = OrderedMap({}); + + /** + * Whether the project is in "turbo mode." + * @type {Boolean} + */ + this.turboMode = false; + + /** + * Whether the project is in "compatibility mode" (30 TPS). + * @type {Boolean} + */ + this.compatibilityMode = false; + + /** + * A reference to the current runtime stepping interval, set + * by a `setInterval`. + * @type {!number} + */ + this._steppingInterval = null; + + /** + * Current length of a step. + * Changes as mode switches, and used by the sequencer to calculate + * WORK_TIME. + * @type {!number} + */ + this.currentStepTime = null; + + // Set an intial value for this.currentMSecs + this.updateCurrentMSecs(); + + /** + * Whether any primitive has requested a redraw. + * Affects whether `Sequencer.stepThreads` will yield + * after stepping each thread. + * Reset on every frame. + * @type {boolean} + */ + this.redrawRequested = false; + + // Register all given block packages. + this._registerBlockPackages(); + + // Register and initialize "IO devices", containers for processing + // I/O related data. + /** @type {Object.} */ + this.ioDevices = { + clock: new Clock(this), + cloud: new Cloud(this), + keyboard: new Keyboard(this), + mouse: new Mouse(this), + mouseWheel: new MouseWheel(this), + userData: new UserData(), + video: new Video(this) + }; + + /** + * A list of extensions, used to manage hardware connection. + */ + this.peripheralExtensions = {}; + + /** + * A runtime profiler that records timed events for later playback to + * diagnose Scratch performance. + * @type {Profiler} + */ + this.profiler = null; + + const newCloudDataManager = cloudDataManager(); + + /** + * Check wether the runtime has any cloud data. + * @type {function} + * @return {boolean} Whether or not the runtime currently has any + * cloud variables. + */ + this.hasCloudData = newCloudDataManager.hasCloudVariables; + + /** + * A function which checks whether a new cloud variable can be added + * to the runtime. + * @type {function} + * @return {boolean} Whether or not a new cloud variable can be added + * to the runtime. + */ + this.canAddCloudVariable = newCloudDataManager.canAddCloudVariable; + + /** + * A function that tracks a new cloud variable in the runtime, + * updating the cloud variable limit. Calling this function will + * emit a cloud data update event if this is the first cloud variable + * being added. + * @type {function} + */ + this.addCloudVariable = this._initializeAddCloudVariable(newCloudDataManager); + + /** + * A function which updates the runtime's cloud variable limit + * when removing a cloud variable and emits a cloud update event + * if the last of the cloud variables is being removed. + * @type {function} + */ + this.removeCloudVariable = this._initializeRemoveCloudVariable(newCloudDataManager); + + /** + * A string representing the origin of the current project from outside of the + * Scratch community, such as CSFirst. + * @type {?string} + */ + this.origin = null; + + this._initScratchLink(); + } + + /** + * Width of the stage, in pixels. + * @const {number} + */ + static get STAGE_WIDTH () { + return 480; + } + + /** + * Height of the stage, in pixels. + * @const {number} + */ + static get STAGE_HEIGHT () { + return 360; + } + + /** + * Event name for glowing a script. + * @const {string} + */ + static get SCRIPT_GLOW_ON () { + return 'SCRIPT_GLOW_ON'; + } + + /** + * Event name for unglowing a script. + * @const {string} + */ + static get SCRIPT_GLOW_OFF () { + return 'SCRIPT_GLOW_OFF'; + } + + /** + * Event name for glowing a block. + * @const {string} + */ + static get BLOCK_GLOW_ON () { + return 'BLOCK_GLOW_ON'; + } + + /** + * Event name for unglowing a block. + * @const {string} + */ + static get BLOCK_GLOW_OFF () { + return 'BLOCK_GLOW_OFF'; + } + + /** + * Event name for a cloud data update + * to this project. + * @const {string} + */ + static get HAS_CLOUD_DATA_UPDATE () { + return 'HAS_CLOUD_DATA_UPDATE'; + } + + /** + * Event name for turning on turbo mode. + * @const {string} + */ + static get TURBO_MODE_ON () { + return 'TURBO_MODE_ON'; + } + + /** + * Event name for turning off turbo mode. + * @const {string} + */ + static get TURBO_MODE_OFF () { + return 'TURBO_MODE_OFF'; + } + + /** + * Event name when the project is started (threads may not necessarily be + * running). + * @const {string} + */ + static get PROJECT_START () { + return 'PROJECT_START'; + } + + /** + * Event name when threads start running. + * Used by the UI to indicate running status. + * @const {string} + */ + static get PROJECT_RUN_START () { + return 'PROJECT_RUN_START'; + } + + /** + * Event name when threads stop running + * Used by the UI to indicate not-running status. + * @const {string} + */ + static get PROJECT_RUN_STOP () { + return 'PROJECT_RUN_STOP'; + } + + /** + * Event name for project being stopped or restarted by the user. + * Used by blocks that need to reset state. + * @const {string} + */ + static get PROJECT_STOP_ALL () { + return 'PROJECT_STOP_ALL'; + } + + /** + * Event name for target being stopped by a stop for target call. + * Used by blocks that need to stop individual targets. + * @const {string} + */ + static get STOP_FOR_TARGET () { + return 'STOP_FOR_TARGET'; + } + + /** + * Event name for visual value report. + * @const {string} + */ + static get VISUAL_REPORT () { + return 'VISUAL_REPORT'; + } + + /** + * Event name for project loaded report. + * @const {string} + */ + static get PROJECT_LOADED () { + return 'PROJECT_LOADED'; + } + + /** + * Event name for report that a change was made that can be saved + * @const {string} + */ + static get PROJECT_CHANGED () { + return 'PROJECT_CHANGED'; + } + + /** + * Event name for report that a change was made to an extension in the toolbox. + * @const {string} + */ + static get TOOLBOX_EXTENSIONS_NEED_UPDATE () { + return 'TOOLBOX_EXTENSIONS_NEED_UPDATE'; + } + + /** + * Event name for targets update report. + * @const {string} + */ + static get TARGETS_UPDATE () { + return 'TARGETS_UPDATE'; + } + + /** + * Event name for monitors update. + * @const {string} + */ + static get MONITORS_UPDATE () { + return 'MONITORS_UPDATE'; + } + + /** + * Event name for block drag update. + * @const {string} + */ + static get BLOCK_DRAG_UPDATE () { + return 'BLOCK_DRAG_UPDATE'; + } + + /** + * Event name for block drag end. + * @const {string} + */ + static get BLOCK_DRAG_END () { + return 'BLOCK_DRAG_END'; + } + + /** + * Event name for reporting that an extension was added. + * @const {string} + */ + static get EXTENSION_ADDED () { + return 'EXTENSION_ADDED'; + } + + /** + * Event name for reporting that an extension as asked for a custom field to be added + * @const {string} + */ + static get EXTENSION_FIELD_ADDED () { + return 'EXTENSION_FIELD_ADDED'; + } + + /** + * Event name for updating the available set of peripheral devices. + * This causes the peripheral connection modal to update a list of + * available peripherals. + * @const {string} + */ + static get PERIPHERAL_LIST_UPDATE () { + return 'PERIPHERAL_LIST_UPDATE'; + } + + /** + * Event name for when the user picks a bluetooth device to connect to + * via Companion Device Manager (CDM) + * @const {string} + */ + static get USER_PICKED_PERIPHERAL () { + return 'USER_PICKED_PERIPHERAL'; + } + + /** + * Event name for reporting that a peripheral has connected. + * This causes the status button in the blocks menu to indicate 'connected'. + * @const {string} + */ + static get PERIPHERAL_CONNECTED () { + return 'PERIPHERAL_CONNECTED'; + } + + /** + * Event name for reporting that a peripheral has been intentionally disconnected. + * This causes the status button in the blocks menu to indicate 'disconnected'. + * @const {string} + */ + static get PERIPHERAL_DISCONNECTED () { + return 'PERIPHERAL_DISCONNECTED'; + } + + /** + * Event name for reporting that a peripheral has encountered a request error. + * This causes the peripheral connection modal to switch to an error state. + * @const {string} + */ + static get PERIPHERAL_REQUEST_ERROR () { + return 'PERIPHERAL_REQUEST_ERROR'; + } + + /** + * Event name for reporting that a peripheral connection has been lost. + * This causes a 'peripheral connection lost' error alert to display. + * @const {string} + */ + static get PERIPHERAL_CONNECTION_LOST_ERROR () { + return 'PERIPHERAL_CONNECTION_LOST_ERROR'; + } + + /** + * Event name for reporting that a peripheral has not been discovered. + * This causes the peripheral connection modal to show a timeout state. + * @const {string} + */ + static get PERIPHERAL_SCAN_TIMEOUT () { + return 'PERIPHERAL_SCAN_TIMEOUT'; + } + + /** + * Event name to indicate that the microphone is being used to stream audio. + * @const {string} + */ + static get MIC_LISTENING () { + return 'MIC_LISTENING'; + } + + /** + * Event name for reporting that blocksInfo was updated. + * @const {string} + */ + static get BLOCKSINFO_UPDATE () { + return 'BLOCKSINFO_UPDATE'; + } + + /** + * Event name when the runtime tick loop has been started. + * @const {string} + */ + static get RUNTIME_STARTED () { + return 'RUNTIME_STARTED'; + } + + /** + * Event name when the runtime dispose has been called. + * @const {string} + */ + static get RUNTIME_DISPOSED () { + return 'RUNTIME_DISPOSED'; + } + + /** + * Event name for reporting that a block was updated and needs to be rerendered. + * @const {string} + */ + static get BLOCKS_NEED_UPDATE () { + return 'BLOCKS_NEED_UPDATE'; + } + + /** + * How rapidly we try to step threads by default, in ms. + */ + static get THREAD_STEP_INTERVAL () { + return 1000 / 60; + } + + /** + * In compatibility mode, how rapidly we try to step threads, in ms. + */ + static get THREAD_STEP_INTERVAL_COMPATIBILITY () { + return 1000 / 30; + } + + /** + * How many clones can be created at a time. + * @const {number} + */ + static get MAX_CLONES () { + return 300; + } + + // ----------------------------------------------------------------------------- + // ----------------------------------------------------------------------------- + + // Helper function for initializing the addCloudVariable function + _initializeAddCloudVariable (newCloudDataManager) { + // The addCloudVariable function + return (() => { + const hadCloudVarsBefore = this.hasCloudData(); + newCloudDataManager.addCloudVariable(); + if (!hadCloudVarsBefore && this.hasCloudData()) { + this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, true); + } + }); + } + + // Helper function for initializing the removeCloudVariable function + _initializeRemoveCloudVariable (newCloudDataManager) { + return (() => { + const hadCloudVarsBefore = this.hasCloudData(); + newCloudDataManager.removeCloudVariable(); + if (hadCloudVarsBefore && !this.hasCloudData()) { + this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, false); + } + }); + } + + /** + * Register default block packages with this runtime. + * @todo Prefix opcodes with package name. + * @private + */ + _registerBlockPackages () { + for (const packageName in defaultBlockPackages) { + if (defaultBlockPackages.hasOwnProperty(packageName)) { + // @todo pass a different runtime depending on package privilege? + const packageObject = new (defaultBlockPackages[packageName])(this); + // Collect primitives from package. + if (packageObject.getPrimitives) { + const packagePrimitives = packageObject.getPrimitives(); + for (const op in packagePrimitives) { + if (packagePrimitives.hasOwnProperty(op)) { + this._primitives[op] = + packagePrimitives[op].bind(packageObject); + } + } + } + // Collect hat metadata from package. + if (packageObject.getHats) { + const packageHats = packageObject.getHats(); + for (const hatName in packageHats) { + if (packageHats.hasOwnProperty(hatName)) { + this._hats[hatName] = packageHats[hatName]; + } + } + } + // Collect monitored from package. + if (packageObject.getMonitored) { + this.monitorBlockInfo = Object.assign({}, this.monitorBlockInfo, packageObject.getMonitored()); + } + } + } + } + + getMonitorState () { + return this._monitorState; + } + + /** + * Generate an extension-specific menu ID. + * @param {string} menuName - the name of the menu. + * @param {string} extensionId - the ID of the extension hosting the menu. + * @returns {string} - the constructed ID. + * @private + */ + _makeExtensionMenuId (menuName, extensionId) { + return `${extensionId}_menu_${xmlEscape(menuName)}`; + } + + /** + * Create a context ("args") object for use with `formatMessage` on messages which might be target-specific. + * @param {Target} [target] - the target to use as context. If a target is not provided, default to the current + * editing target or the stage. + */ + makeMessageContextForTarget (target) { + const context = {}; + target = target || this.getEditingTarget() || this.getTargetForStage(); + if (target) { + context.targetType = (target.isStage ? TargetType.STAGE : TargetType.SPRITE); + } + } + + /** + * Register the primitives provided by an extension. + * @param {ExtensionMetadata} extensionInfo - information about the extension (id, blocks, etc.) + * @private + */ + _registerExtensionPrimitives (extensionInfo) { + const categoryInfo = { + id: extensionInfo.id, + name: maybeFormatMessage(extensionInfo.name), + showStatusButton: extensionInfo.showStatusButton, + blockIconURI: extensionInfo.blockIconURI, + menuIconURI: extensionInfo.menuIconURI + }; + + if (extensionInfo.color1) { + categoryInfo.color1 = extensionInfo.color1; + categoryInfo.color2 = extensionInfo.color2; + categoryInfo.color3 = extensionInfo.color3; + } else { + categoryInfo.color1 = defaultExtensionColors[0]; + categoryInfo.color2 = defaultExtensionColors[1]; + categoryInfo.color3 = defaultExtensionColors[2]; + } + + this._blockInfo.push(categoryInfo); + + this._fillExtensionCategory(categoryInfo, extensionInfo); + + for (const fieldTypeName in categoryInfo.customFieldTypes) { + if (extensionInfo.customFieldTypes.hasOwnProperty(fieldTypeName)) { + const fieldTypeInfo = categoryInfo.customFieldTypes[fieldTypeName]; + + // Emit events for custom field types from extension + this.emit(Runtime.EXTENSION_FIELD_ADDED, { + name: `field_${fieldTypeInfo.extendedName}`, + implementation: fieldTypeInfo.fieldImplementation + }); + } + } + + this.emit(Runtime.EXTENSION_ADDED, categoryInfo); + } + + /** + * Reregister the primitives for an extension + * @param {ExtensionMetadata} extensionInfo - new info (results of running getInfo) for an extension + * @private + */ + _refreshExtensionPrimitives (extensionInfo) { + const categoryInfo = this._blockInfo.find(info => info.id === extensionInfo.id); + if (categoryInfo) { + categoryInfo.name = maybeFormatMessage(extensionInfo.name); + this._fillExtensionCategory(categoryInfo, extensionInfo); + + this.emit(Runtime.BLOCKSINFO_UPDATE, categoryInfo); + } + } + + /** + * Read extension information, convert menus, blocks and custom field types + * and store the results in the provided category object. + * @param {CategoryInfo} categoryInfo - the category to be filled + * @param {ExtensionMetadata} extensionInfo - the extension metadata to read + * @private + */ + _fillExtensionCategory (categoryInfo, extensionInfo) { + categoryInfo.blocks = []; + categoryInfo.customFieldTypes = {}; + categoryInfo.menus = []; + categoryInfo.menuInfo = {}; + + for (const menuName in extensionInfo.menus) { + if (extensionInfo.menus.hasOwnProperty(menuName)) { + const menuInfo = extensionInfo.menus[menuName]; + const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuInfo, categoryInfo); + categoryInfo.menus.push(convertedMenu); + categoryInfo.menuInfo[menuName] = menuInfo; + } + } + for (const fieldTypeName in extensionInfo.customFieldTypes) { + if (extensionInfo.customFieldTypes.hasOwnProperty(fieldTypeName)) { + const fieldType = extensionInfo.customFieldTypes[fieldTypeName]; + const fieldTypeInfo = this._buildCustomFieldInfo( + fieldTypeName, + fieldType, + extensionInfo.id, + categoryInfo + ); + + categoryInfo.customFieldTypes[fieldTypeName] = fieldTypeInfo; + } + } + + for (const blockInfo of extensionInfo.blocks) { + try { + const convertedBlock = this._convertForScratchBlocks(blockInfo, categoryInfo); + categoryInfo.blocks.push(convertedBlock); + if (convertedBlock.json) { + const opcode = convertedBlock.json.type; + if (blockInfo.blockType !== BlockType.EVENT) { + this._primitives[opcode] = convertedBlock.info.func; + } + if (blockInfo.blockType === BlockType.EVENT || blockInfo.blockType === BlockType.HAT) { + this._hats[opcode] = { + edgeActivated: blockInfo.isEdgeActivated, + restartExistingThreads: blockInfo.shouldRestartExistingThreads + }; + } + } + } catch (e) { + log.error('Error parsing block: ', {block: blockInfo, error: e}); + } + } + } + + /** + * Convert the given extension menu items into the scratch-blocks style of list of pairs. + * If the menu is dynamic (e.g. the passed in argument is a function), return the input unmodified. + * @param {object} menuItems - an array of menu items or a function to retrieve such an array + * @returns {object} - an array of 2 element arrays or the original input function + * @private + */ + _convertMenuItems (menuItems) { + if (typeof menuItems !== 'function') { + const extensionMessageContext = this.makeMessageContextForTarget(); + return menuItems.map(item => { + const formattedItem = maybeFormatMessage(item, extensionMessageContext); + switch (typeof formattedItem) { + case 'string': + return [formattedItem, formattedItem]; + case 'object': + return [maybeFormatMessage(item.text, extensionMessageContext), item.value]; + default: + throw new Error(`Can't interpret menu item: ${JSON.stringify(item)}`); + } + }); + } + return menuItems; + } + + /** + * Build the scratch-blocks JSON for a menu. Note that scratch-blocks treats menus as a special kind of block. + * @param {string} menuName - the name of the menu + * @param {object} menuInfo - a description of this menu and its items + * @property {*} items - an array of menu items or a function to retrieve such an array + * @property {boolean} [acceptReporters] - if true, allow dropping reporters onto this menu + * @param {CategoryInfo} categoryInfo - the category for this block + * @returns {object} - a JSON-esque object ready for scratch-blocks' consumption + * @private + */ + _buildMenuForScratchBlocks (menuName, menuInfo, categoryInfo) { + const menuId = this._makeExtensionMenuId(menuName, categoryInfo.id); + const menuItems = this._convertMenuItems(menuInfo.items); + return { + json: { + message0: '%1', + type: menuId, + inputsInline: true, + output: 'String', + colour: categoryInfo.color1, + colourSecondary: categoryInfo.color2, + colourTertiary: categoryInfo.color3, + outputShape: menuInfo.acceptReporters ? + ScratchBlocksConstants.OUTPUT_SHAPE_ROUND : ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE, + args0: [ + { + type: 'field_dropdown', + name: menuName, + options: menuItems + } + ] + } + }; + } + + _buildCustomFieldInfo (fieldName, fieldInfo, extensionId, categoryInfo) { + const extendedName = `${extensionId}_${fieldName}`; + return { + fieldName: fieldName, + extendedName: extendedName, + argumentTypeInfo: { + shadow: { + type: extendedName, + fieldName: `field_${extendedName}` + } + }, + scratchBlocksDefinition: this._buildCustomFieldTypeForScratchBlocks( + extendedName, + fieldInfo.output, + fieldInfo.outputShape, + categoryInfo + ), + fieldImplementation: fieldInfo.implementation + }; + } + + /** + * Build the scratch-blocks JSON needed for a fieldType. + * Custom field types need to be namespaced to the extension so that extensions can't interfere with each other + * @param {string} fieldName - The name of the field + * @param {string} output - The output of the field + * @param {number} outputShape - Shape of the field (from ScratchBlocksConstants) + * @param {object} categoryInfo - The category the field belongs to (Used to set its colors) + * @returns {object} - Object to be inserted into scratch-blocks + */ + _buildCustomFieldTypeForScratchBlocks (fieldName, output, outputShape, categoryInfo) { + return { + json: { + type: fieldName, + message0: '%1', + inputsInline: true, + output: output, + colour: categoryInfo.color1, + colourSecondary: categoryInfo.color2, + colourTertiary: categoryInfo.color3, + outputShape: outputShape, + args0: [ + { + name: `field_${fieldName}`, + type: `field_${fieldName}` + } + ] + } + }; + } + + /** + * Convert ExtensionBlockMetadata into data ready for scratch-blocks. + * @param {ExtensionBlockMetadata} blockInfo - the block info to convert + * @param {CategoryInfo} categoryInfo - the category for this block + * @returns {ConvertedBlockInfo} - the converted & original block information + * @private + */ + _convertForScratchBlocks (blockInfo, categoryInfo) { + if (blockInfo === '---') { + return this._convertSeparatorForScratchBlocks(blockInfo); + } + + if (blockInfo.blockType === BlockType.BUTTON) { + return this._convertButtonForScratchBlocks(blockInfo); + } + + return this._convertBlockForScratchBlocks(blockInfo, categoryInfo); + } + + /** + * Convert ExtensionBlockMetadata into scratch-blocks JSON & XML, and generate a proxy function. + * @param {ExtensionBlockMetadata} blockInfo - the block to convert + * @param {CategoryInfo} categoryInfo - the category for this block + * @returns {ConvertedBlockInfo} - the converted & original block information + * @private + */ + _convertBlockForScratchBlocks (blockInfo, categoryInfo) { + const extendedOpcode = `${categoryInfo.id}_${blockInfo.opcode}`; + + const blockJSON = { + type: extendedOpcode, + inputsInline: true, + category: categoryInfo.name, + colour: categoryInfo.color1, + colourSecondary: categoryInfo.color2, + colourTertiary: categoryInfo.color3 + }; + const context = { + // TODO: store this somewhere so that we can map args appropriately after translation. + // This maps an arg name to its relative position in the original (usually English) block text. + // When displaying a block in another language we'll need to run a `replace` action similar to the one + // below, but each `[ARG]` will need to be replaced with the number in this map. + argsMap: {}, + blockJSON, + categoryInfo, + blockInfo, + inputList: [] + }; + + // If an icon for the extension exists, prepend it to each block, with a vertical separator. + // We can overspecify an icon for each block, but if no icon exists on a block, fall back to + // the category block icon. + const iconURI = blockInfo.blockIconURI || categoryInfo.blockIconURI; + + if (iconURI) { + blockJSON.extensions = ['scratch_extension']; + blockJSON.message0 = '%1 %2'; + const iconJSON = { + type: 'field_image', + src: iconURI, + width: 40, + height: 40 + }; + const separatorJSON = { + type: 'field_vertical_separator' + }; + blockJSON.args0 = [ + iconJSON, + separatorJSON + ]; + } + + switch (blockInfo.blockType) { + case BlockType.COMMAND: + blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE; + blockJSON.previousStatement = null; // null = available connection; undefined = hat + if (!blockInfo.isTerminal) { + blockJSON.nextStatement = null; // null = available connection; undefined = terminal + } + break; + case BlockType.REPORTER: + blockJSON.output = 'String'; // TODO: distinguish number & string here? + blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_ROUND; + break; + case BlockType.BOOLEAN: + blockJSON.output = 'Boolean'; + blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_HEXAGONAL; + break; + case BlockType.HAT: + case BlockType.EVENT: + if (!blockInfo.hasOwnProperty('isEdgeActivated')) { + // if absent, this property defaults to true + blockInfo.isEdgeActivated = true; + } + blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE; + blockJSON.nextStatement = null; // null = available connection; undefined = terminal + break; + case BlockType.CONDITIONAL: + case BlockType.LOOP: + blockInfo.branchCount = blockInfo.branchCount || 1; + blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE; + blockJSON.previousStatement = null; // null = available connection; undefined = hat + if (!blockInfo.isTerminal) { + blockJSON.nextStatement = null; // null = available connection; undefined = terminal + } + break; + } + + const blockText = Array.isArray(blockInfo.text) ? blockInfo.text : [blockInfo.text]; + let inTextNum = 0; // text for the next block "arm" is blockText[inTextNum] + let inBranchNum = 0; // how many branches have we placed into the JSON so far? + let outLineNum = 0; // used for scratch-blocks `message${outLineNum}` and `args${outLineNum}` + const convertPlaceholders = this._convertPlaceholders.bind(this, context); + const extensionMessageContext = this.makeMessageContextForTarget(); + + // alternate between a block "arm" with text on it and an open slot for a substack + while (inTextNum < blockText.length || inBranchNum < blockInfo.branchCount) { + if (inTextNum < blockText.length) { + context.outLineNum = outLineNum; + const lineText = maybeFormatMessage(blockText[inTextNum], extensionMessageContext); + const convertedText = lineText.replace(/\[(.+?)]/g, convertPlaceholders); + if (blockJSON[`message${outLineNum}`]) { + blockJSON[`message${outLineNum}`] += convertedText; + } else { + blockJSON[`message${outLineNum}`] = convertedText; + } + ++inTextNum; + ++outLineNum; + } + if (inBranchNum < blockInfo.branchCount) { + blockJSON[`message${outLineNum}`] = '%1'; + blockJSON[`args${outLineNum}`] = [{ + type: 'input_statement', + name: `SUBSTACK${inBranchNum > 0 ? inBranchNum + 1 : ''}` + }]; + ++inBranchNum; + ++outLineNum; + } + } + + if (blockInfo.blockType === BlockType.REPORTER) { + if (!blockInfo.disableMonitor && context.inputList.length === 0) { + blockJSON.checkboxInFlyout = true; + } + } else if (blockInfo.blockType === BlockType.LOOP) { + // Add icon to the bottom right of a loop block + blockJSON[`lastDummyAlign${outLineNum}`] = 'RIGHT'; + blockJSON[`message${outLineNum}`] = '%1'; + blockJSON[`args${outLineNum}`] = [{ + type: 'field_image', + src: './static/blocks-media/repeat.svg', // TODO: use a constant or make this configurable? + width: 24, + height: 24, + alt: '*', // TODO remove this since we don't use collapsed blocks in scratch + flip_rtl: true + }]; + ++outLineNum; + } + + const mutation = blockInfo.isDynamic ? `` : ''; + const inputs = context.inputList.join(''); + const blockXML = `${mutation}${inputs}`; + + return { + info: context.blockInfo, + json: context.blockJSON, + xml: blockXML + }; + } + + /** + * Generate a separator between blocks categories or sub-categories. + * @param {ExtensionBlockMetadata} blockInfo - the block to convert + * @param {CategoryInfo} categoryInfo - the category for this block + * @returns {ConvertedBlockInfo} - the converted & original block information + * @private + */ + _convertSeparatorForScratchBlocks (blockInfo) { + return { + info: blockInfo, + xml: '' + }; + } + + /** + * Convert a button for scratch-blocks. A button has no opcode but specifies a callback name in the `func` field. + * @param {ExtensionBlockMetadata} buttonInfo - the button to convert + * @property {string} func - the callback name + * @param {CategoryInfo} categoryInfo - the category for this button + * @returns {ConvertedBlockInfo} - the converted & original button information + * @private + */ + _convertButtonForScratchBlocks (buttonInfo) { + // for now we only support these pre-defined callbacks handled in scratch-blocks + const supportedCallbackKeys = ['MAKE_A_LIST', 'MAKE_A_PROCEDURE', 'MAKE_A_VARIABLE']; + if (supportedCallbackKeys.indexOf(buttonInfo.func) < 0) { + log.error(`Custom button callbacks not supported yet: ${buttonInfo.func}`); + } + + const extensionMessageContext = this.makeMessageContextForTarget(); + const buttonText = maybeFormatMessage(buttonInfo.text, extensionMessageContext); + return { + info: buttonInfo, + xml: `` + }; + } + + /** + * Helper for _convertPlaceholdes which handles inline images which are a specialized case of block "arguments". + * @param {object} argInfo Metadata about the inline image as specified by the extension + * @return {object} JSON blob for a scratch-blocks image field. + * @private + */ + _constructInlineImageJson (argInfo) { + if (!argInfo.dataURI) { + log.warn('Missing data URI in extension block with argument type IMAGE'); + } + return { + type: 'field_image', + src: argInfo.dataURI || '', + // TODO these probably shouldn't be hardcoded...? + width: 24, + height: 24, + // Whether or not the inline image should be flipped horizontally + // in RTL languages. Defaults to false, indicating that the + // image will not be flipped. + flip_rtl: argInfo.flipRTL || false + }; + } + + /** + * Helper for _convertForScratchBlocks which handles linearization of argument placeholders. Called as a callback + * from string#replace. In addition to the return value the JSON and XML items in the context will be filled. + * @param {object} context - information shared with _convertForScratchBlocks about the block, etc. + * @param {string} match - the overall string matched by the placeholder regex, including brackets: '[FOO]'. + * @param {string} placeholder - the name of the placeholder being matched: 'FOO'. + * @return {string} scratch-blocks placeholder for the argument: '%1'. + * @private + */ + _convertPlaceholders (context, match, placeholder) { + // Sanitize the placeholder to ensure valid XML + placeholder = placeholder.replace(/[<"&]/, '_'); + + // Determine whether the argument type is one of the known standard field types + const argInfo = context.blockInfo.arguments[placeholder] || {}; + let argTypeInfo = ArgumentTypeMap[argInfo.type] || {}; + + // Field type not a standard field type, see if extension has registered custom field type + if (!ArgumentTypeMap[argInfo.type] && context.categoryInfo.customFieldTypes[argInfo.type]) { + argTypeInfo = context.categoryInfo.customFieldTypes[argInfo.type].argumentTypeInfo; + } + + // Start to construct the scratch-blocks style JSON defining how the block should be + // laid out + let argJSON; + + // Most field types are inputs (slots on the block that can have other blocks plugged into them) + // check if this is not one of those cases. E.g. an inline image on a block. + if (argTypeInfo.fieldType === 'field_image') { + argJSON = this._constructInlineImageJson(argInfo); + } else { + // Construct input value + + // Layout a block argument (e.g. an input slot on the block) + argJSON = { + type: 'input_value', + name: placeholder + }; + + const defaultValue = + typeof argInfo.defaultValue === 'undefined' ? '' : + xmlEscape(maybeFormatMessage(argInfo.defaultValue, this.makeMessageContextForTarget()).toString()); + + if (argTypeInfo.check) { + // Right now the only type of 'check' we have specifies that the + // input slot on the block accepts Boolean reporters, so it should be + // shaped like a hexagon + argJSON.check = argTypeInfo.check; + } + + let valueName; + let shadowType; + let fieldName; + if (argInfo.menu) { + const menuInfo = context.categoryInfo.menuInfo[argInfo.menu]; + if (menuInfo.acceptReporters) { + valueName = placeholder; + shadowType = this._makeExtensionMenuId(argInfo.menu, context.categoryInfo.id); + fieldName = argInfo.menu; + } else { + argJSON.type = 'field_dropdown'; + argJSON.options = this._convertMenuItems(menuInfo.items); + valueName = null; + shadowType = null; + fieldName = placeholder; + } + } else { + valueName = placeholder; + shadowType = (argTypeInfo.shadow && argTypeInfo.shadow.type) || null; + fieldName = (argTypeInfo.shadow && argTypeInfo.shadow.fieldName) || null; + } + + // is the ScratchBlocks name for a block input. + if (valueName) { + context.inputList.push(``); + } + + // The is a placeholder for a reporter and is visible when there's no reporter in this input. + // Boolean inputs don't need to specify a shadow in the XML. + if (shadowType) { + context.inputList.push(``); + } + + // A displays a dynamic value: a user-editable text field, a drop-down menu, etc. + // Leave out the field if defaultValue or fieldName are not specified + if (defaultValue && fieldName) { + context.inputList.push(`${defaultValue}`); + } + + if (shadowType) { + context.inputList.push(''); + } + + if (valueName) { + context.inputList.push(''); + } + } + + const argsName = `args${context.outLineNum}`; + const blockArgs = (context.blockJSON[argsName] = context.blockJSON[argsName] || []); + if (argJSON) blockArgs.push(argJSON); + const argNum = blockArgs.length; + context.argsMap[placeholder] = argNum; + + return `%${argNum}`; + } + + /** + * @returns {Array.} scratch-blocks XML for each category of extension blocks, in category order. + * @param {?Target} [target] - the active editing target (optional) + * @property {string} id - the category / extension ID + * @property {string} xml - the XML text for this category, starting with `` and ending with `` + */ + getBlocksXML (target) { + return this._blockInfo.map(categoryInfo => { + const {name, color1, color2} = categoryInfo; + // Filter out blocks that aren't supposed to be shown on this target, as determined by the block info's + // `hideFromPalette` and `filter` properties. + const paletteBlocks = categoryInfo.blocks.filter(block => { + let blockFilterIncludesTarget = true; + // If an editing target is not passed, include all blocks + // If the block info doesn't include a `filter` property, always include it + if (target && block.info.filter) { + blockFilterIncludesTarget = block.info.filter.includes( + target.isStage ? TargetType.STAGE : TargetType.SPRITE + ); + } + // If the block info's `hideFromPalette` is true, then filter out this block + return blockFilterIncludesTarget && !block.info.hideFromPalette; + }); + + const colorXML = `colour="${color1}" secondaryColour="${color2}"`; + + // Use a menu icon if there is one. Otherwise, use the block icon. If there's no icon, + // the category menu will show its default colored circle. + let menuIconURI = ''; + if (categoryInfo.menuIconURI) { + menuIconURI = categoryInfo.menuIconURI; + } else if (categoryInfo.blockIconURI) { + menuIconURI = categoryInfo.blockIconURI; + } + const menuIconXML = menuIconURI ? + `iconURI="${menuIconURI}"` : ''; + + let statusButtonXML = ''; + if (categoryInfo.showStatusButton) { + statusButtonXML = 'showStatusButton="true"'; + } + + return { + id: categoryInfo.id, + xml: `${ + paletteBlocks.map(block => block.xml).join('')}` + }; + }); + } + + /** + * @returns {Array.} - an array containing the scratch-blocks JSON information for each dynamic block. + */ + getBlocksJSON () { + return this._blockInfo.reduce( + (result, categoryInfo) => result.concat(categoryInfo.blocks.map(blockInfo => blockInfo.json)), []); + } + + /** + * One-time initialization for Scratch Link support. + */ + _initScratchLink () { + /* global globalThis */ + // Check that we're actually in a real browser, not Node.js or JSDOM, and we have a valid-looking origin. + if (globalThis.document && + globalThis.document.getElementById && + globalThis.origin && + globalThis.origin !== 'null' && + globalThis.navigator && + globalThis.navigator.userAgent && + globalThis.navigator.userAgent.includes && + !globalThis.navigator.userAgent.includes('Node.js') && + !globalThis.navigator.userAgent.includes('jsdom') + ) { + // Create a script tag for the Scratch Link browser extension, unless one already exists + const scriptElement = document.getElementById('scratch-link-extension-script'); + if (!scriptElement) { + const script = document.createElement('script'); + script.id = 'scratch-link-extension-script'; + document.body.appendChild(script); + + // Tell the browser extension to inject its script. + // If the extension isn't present or isn't active, this will do nothing. + globalThis.postMessage('inject-scratch-link-script', globalThis.origin); + } + } + } + + /** + * Get a scratch link socket. + * @param {string} type Either BLE or BT + * @returns {ScratchLinkSocket} The scratch link socket. + */ + getScratchLinkSocket (type) { + const factory = this._linkSocketFactory || this._defaultScratchLinkSocketFactory; + return factory(type); + } + + /** + * Configure how ScratchLink sockets are created. Factory must consume a "type" parameter + * either BT or BLE. + * @param {Function} factory The new factory for creating ScratchLink sockets. + */ + configureScratchLinkSocketFactory (factory) { + this._linkSocketFactory = factory; + } + + /** + * The default scratch link socket creator, using websockets to the installed device manager. + * @param {string} type Either BLE or BT + * @returns {ScratchLinkSocket} The new scratch link socket (a WebSocket object) + */ + _defaultScratchLinkSocketFactory (type) { + const Scratch = self.Scratch; + const ScratchLinkSafariSocket = Scratch && Scratch.ScratchLinkSafariSocket; + // detect this every time in case the user turns on the extension after loading the page + const useSafariSocket = ScratchLinkSafariSocket && ScratchLinkSafariSocket.isSafariHelperCompatible(); + return useSafariSocket ? new ScratchLinkSafariSocket(type) : new ScratchLinkWebSocket(type); + } + + /** + * Register an extension that communications with a hardware peripheral by id, + * to have access to it and its peripheral functions in the future. + * @param {string} extensionId - the id of the extension. + * @param {object} extension - the extension to register. + */ + registerPeripheralExtension (extensionId, extension) { + this.peripheralExtensions[extensionId] = extension; + } + + /** + * Tell the specified extension to scan for a peripheral. + * @param {string} extensionId - the id of the extension. + */ + scanForPeripheral (extensionId) { + if (this.peripheralExtensions[extensionId]) { + this.peripheralExtensions[extensionId].scan(); + } + } + + /** + * Connect to the extension's specified peripheral. + * @param {string} extensionId - the id of the extension. + * @param {number} peripheralId - the id of the peripheral. + */ + connectPeripheral (extensionId, peripheralId) { + if (this.peripheralExtensions[extensionId]) { + this.peripheralExtensions[extensionId].connect(peripheralId); + } + } + + /** + * Disconnect from the extension's connected peripheral. + * @param {string} extensionId - the id of the extension. + */ + disconnectPeripheral (extensionId) { + if (this.peripheralExtensions[extensionId]) { + this.peripheralExtensions[extensionId].disconnect(); + } + } + + /** + * Returns whether the extension has a currently connected peripheral. + * @param {string} extensionId - the id of the extension. + * @return {boolean} - whether the extension has a connected peripheral. + */ + getPeripheralIsConnected (extensionId) { + let isConnected = false; + if (this.peripheralExtensions[extensionId]) { + isConnected = this.peripheralExtensions[extensionId].isConnected(); + } + return isConnected; + } + + /** + * Emit an event to indicate that the microphone is being used to stream audio. + * @param {boolean} listening - true if the microphone is currently listening. + */ + emitMicListening (listening) { + this.emit(Runtime.MIC_LISTENING, listening); + } + + /** + * Retrieve the function associated with the given opcode. + * @param {!string} opcode The opcode to look up. + * @return {Function} The function which implements the opcode. + */ + getOpcodeFunction (opcode) { + return this._primitives[opcode]; + } + + /** + * Return whether an opcode represents a hat block. + * @param {!string} opcode The opcode to look up. + * @return {boolean} True if the op is known to be a hat. + */ + getIsHat (opcode) { + return this._hats.hasOwnProperty(opcode); + } + + /** + * Return whether an opcode represents an edge-activated hat block. + * @param {!string} opcode The opcode to look up. + * @return {boolean} True if the op is known to be a edge-activated hat. + */ + getIsEdgeActivatedHat (opcode) { + return this._hats.hasOwnProperty(opcode) && + this._hats[opcode].edgeActivated; + } + + + /** + * Attach the audio engine + * @param {!AudioEngine} audioEngine The audio engine to attach + */ + attachAudioEngine (audioEngine) { + this.audioEngine = audioEngine; + } + + /** + * Attach the renderer + * @param {!RenderWebGL} renderer The renderer to attach + */ + attachRenderer (renderer) { + this.renderer = renderer; + this.renderer.setLayerGroupOrdering(StageLayering.LAYER_GROUPS); + } + + /** + * Set the bitmap adapter for the VM/runtime, which converts scratch 2 + * bitmaps to scratch 3 bitmaps. (Scratch 3 bitmaps are all bitmap resolution 2) + * @param {!function} bitmapAdapter The adapter to attach + */ + attachV2BitmapAdapter (bitmapAdapter) { + this.v2BitmapAdapter = bitmapAdapter; + } + + /** + * Attach the storage module + * @param {!ScratchStorage} storage The storage module to attach + */ + attachStorage (storage) { + this.storage = storage; + } + + // ----------------------------------------------------------------------------- + // ----------------------------------------------------------------------------- + + /** + * Create a thread and push it to the list of threads. + * @param {!string} id ID of block that starts the stack. + * @param {!Target} target Target to run thread on. + * @param {?object} opts optional arguments + * @param {?boolean} opts.stackClick true if the script was activated by clicking on the stack + * @param {?boolean} opts.updateMonitor true if the script should update a monitor value + * @return {!Thread} The newly created thread. + */ + _pushThread (id, target, opts) { + const thread = new Thread(id); + thread.target = target; + thread.stackClick = Boolean(opts && opts.stackClick); + thread.updateMonitor = Boolean(opts && opts.updateMonitor); + thread.blockContainer = thread.updateMonitor ? + this.monitorBlocks : + target.blocks; + + thread.pushStack(id); + this.threads.push(thread); + return thread; + } + + /** + * Stop a thread: stop running it immediately, and remove it from the thread list later. + * @param {!Thread} thread Thread object to remove from actives + */ + _stopThread (thread) { + // Mark the thread for later removal + thread.isKilled = true; + // Inform sequencer to stop executing that thread. + this.sequencer.retireThread(thread); + } + + /** + * Restart a thread in place, maintaining its position in the list of threads. + * This is used by `startHats` to and is necessary to ensure 2.0-like execution order. + * Test project: https://scratch.mit.edu/projects/130183108/ + * @param {!Thread} thread Thread object to restart. + * @return {Thread} The restarted thread. + */ + _restartThread (thread) { + const newThread = new Thread(thread.topBlock); + newThread.target = thread.target; + newThread.stackClick = thread.stackClick; + newThread.updateMonitor = thread.updateMonitor; + newThread.blockContainer = thread.blockContainer; + newThread.pushStack(thread.topBlock); + const i = this.threads.indexOf(thread); + if (i > -1) { + this.threads[i] = newThread; + return newThread; + } + this.threads.push(thread); + return thread; + } + + /** + * Return whether a thread is currently active/running. + * @param {?Thread} thread Thread object to check. + * @return {boolean} True if the thread is active/running. + */ + isActiveThread (thread) { + return ( + ( + thread.stack.length > 0 && + thread.status !== Thread.STATUS_DONE) && + this.threads.indexOf(thread) > -1); + } + + /** + * Return whether a thread is waiting for more information or done. + * @param {?Thread} thread Thread object to check. + * @return {boolean} True if the thread is waiting + */ + isWaitingThread (thread) { + return ( + thread.status === Thread.STATUS_PROMISE_WAIT || + thread.status === Thread.STATUS_YIELD_TICK || + !this.isActiveThread(thread) + ); + } + + /** + * Toggle a script. + * @param {!string} topBlockId ID of block that starts the script. + * @param {?object} opts optional arguments to toggle script + * @param {?string} opts.target target ID for target to run script on. If not supplied, uses editing target. + * @param {?boolean} opts.stackClick true if the user activated the stack by clicking, false if not. This + * determines whether we show a visual report when turning on the script. + */ + toggleScript (topBlockId, opts) { + opts = Object.assign({ + target: this._editingTarget, + stackClick: false + }, opts); + // Remove any existing thread. + for (let i = 0; i < this.threads.length; i++) { + // Toggling a script that's already running turns it off + if (this.threads[i].topBlock === topBlockId && this.threads[i].status !== Thread.STATUS_DONE) { + const blockContainer = opts.target.blocks; + const opcode = blockContainer.getOpcode(blockContainer.getBlock(topBlockId)); + + if (this.getIsEdgeActivatedHat(opcode) && this.threads[i].stackClick !== opts.stackClick) { + // Allow edge activated hat thread stack click to coexist with + // edge activated hat thread that runs every frame + continue; + } + this._stopThread(this.threads[i]); + return; + } + } + // Otherwise add it. + this._pushThread(topBlockId, opts.target, opts); + } + + /** + * Enqueue a script that when finished will update the monitor for the block. + * @param {!string} topBlockId ID of block that starts the script. + * @param {?Target} optTarget target Target to run script on. If not supplied, uses editing target. + */ + addMonitorScript (topBlockId, optTarget) { + if (!optTarget) optTarget = this._editingTarget; + for (let i = 0; i < this.threads.length; i++) { + // Don't re-add the script if it's already running + if (this.threads[i].topBlock === topBlockId && this.threads[i].status !== Thread.STATUS_DONE && + this.threads[i].updateMonitor) { + return; + } + } + // Otherwise add it. + this._pushThread(topBlockId, optTarget, {updateMonitor: true}); + } + + /** + * Run a function `f` for all scripts in a workspace. + * `f` will be called with two parameters: + * - the top block ID of the script. + * - the target that owns the script. + * @param {!Function} f Function to call for each script. + * @param {Target=} optTarget Optionally, a target to restrict to. + */ + allScriptsDo (f, optTarget) { + let targets = this.executableTargets; + if (optTarget) { + targets = [optTarget]; + } + for (let t = targets.length - 1; t >= 0; t--) { + const target = targets[t]; + const scripts = target.blocks.getScripts(); + for (let j = 0; j < scripts.length; j++) { + const topBlockId = scripts[j]; + f(topBlockId, target); + } + } + } + + allScriptsByOpcodeDo (opcode, f, optTarget) { + let targets = this.executableTargets; + if (optTarget) { + targets = [optTarget]; + } + for (let t = targets.length - 1; t >= 0; t--) { + const target = targets[t]; + const scripts = BlocksRuntimeCache.getScripts(target.blocks, opcode); + for (let j = 0; j < scripts.length; j++) { + f(scripts[j], target); + } + } + } + + /** + * Start all relevant hats. + * @param {!string} requestedHatOpcode Opcode of hats to start. + * @param {object=} optMatchFields Optionally, fields to match on the hat. + * @param {Target=} optTarget Optionally, a target to restrict to. + * @return {Array.} List of threads started by this function. + */ + startHats (requestedHatOpcode, + optMatchFields, optTarget) { + if (!this._hats.hasOwnProperty(requestedHatOpcode)) { + // No known hat with this opcode. + return; + } + const instance = this; + const newThreads = []; + // Look up metadata for the relevant hat. + const hatMeta = instance._hats[requestedHatOpcode]; + + for (const opts in optMatchFields) { + if (!optMatchFields.hasOwnProperty(opts)) continue; + optMatchFields[opts] = optMatchFields[opts].toUpperCase(); + } + + // Consider all scripts, looking for hats with opcode `requestedHatOpcode`. + this.allScriptsByOpcodeDo(requestedHatOpcode, (script, target) => { + const { + blockId: topBlockId, + fieldsOfInputs: hatFields + } = script; + + // Match any requested fields. + // For example: ensures that broadcasts match. + // This needs to happen before the block is evaluated + // (i.e., before the predicate can be run) because "broadcast and wait" + // needs to have a precise collection of started threads. + for (const matchField in optMatchFields) { + if (hatFields[matchField].value !== optMatchFields[matchField]) { + // Field mismatch. + return; + } + } + + if (hatMeta.restartExistingThreads) { + // If `restartExistingThreads` is true, we should stop + // any existing threads starting with the top block. + for (let i = 0; i < this.threads.length; i++) { + if (this.threads[i].target === target && + this.threads[i].topBlock === topBlockId && + // stack click threads and hat threads can coexist + !this.threads[i].stackClick) { + newThreads.push(this._restartThread(this.threads[i])); + return; + } + } + } else { + // If `restartExistingThreads` is false, we should + // give up if any threads with the top block are running. + for (let j = 0; j < this.threads.length; j++) { + if (this.threads[j].target === target && + this.threads[j].topBlock === topBlockId && + // stack click threads and hat threads can coexist + !this.threads[j].stackClick && + this.threads[j].status !== Thread.STATUS_DONE) { + // Some thread is already running. + return; + } + } + } + // Start the thread with this top block. + newThreads.push(this._pushThread(topBlockId, target)); + }, optTarget); + // For compatibility with Scratch 2, edge triggered hats need to be processed before + // threads are stepped. See ScratchRuntime.as for original implementation + newThreads.forEach(thread => { + execute(this.sequencer, thread); + thread.goToNextBlock(); + }); + return newThreads; + } + + + /** + * Dispose all targets. Return to clean state. + */ + dispose () { + this.stopAll(); + // Deleting each target's variable's monitors. + this.targets.forEach(target => { + if (target.isOriginal) target.deleteMonitors(); + }); + + this.targets.map(this.disposeTarget, this); + this._monitorState = OrderedMap({}); + this.emit(Runtime.RUNTIME_DISPOSED); + this.ioDevices.clock.resetProjectTimer(); + // @todo clear out extensions? turboMode? etc. + + // *********** Cloud ******************* + + // If the runtime currently has cloud data, + // emit a has cloud data update event resetting + // it to false + if (this.hasCloudData()) { + this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, false); + } + + this.ioDevices.cloud.clear(); + + // Reset runtime cloud data info + const newCloudDataManager = cloudDataManager(); + this.hasCloudData = newCloudDataManager.hasCloudVariables; + this.canAddCloudVariable = newCloudDataManager.canAddCloudVariable; + this.addCloudVariable = this._initializeAddCloudVariable(newCloudDataManager); + this.removeCloudVariable = this._initializeRemoveCloudVariable(newCloudDataManager); + } + + /** + * Add a target to the runtime. This tracks the sprite pane + * ordering of the target. The target still needs to be put + * into the correct execution order after calling this function. + * @param {Target} target target to add + */ + addTarget (target) { + this.targets.push(target); + this.executableTargets.push(target); + } + + /** + * Move a target in the execution order by a relative amount. + * + * A positve number will make the target execute earlier. A negative number + * will make the target execute later in the order. + * + * @param {Target} executableTarget target to move + * @param {number} delta number of positions to move target by + * @returns {number} new position in execution order + */ + moveExecutable (executableTarget, delta) { + const oldIndex = this.executableTargets.indexOf(executableTarget); + this.executableTargets.splice(oldIndex, 1); + let newIndex = oldIndex + delta; + if (newIndex > this.executableTargets.length) { + newIndex = this.executableTargets.length; + } + if (newIndex <= 0) { + if (this.executableTargets.length > 0 && this.executableTargets[0].isStage) { + newIndex = 1; + } else { + newIndex = 0; + } + } + this.executableTargets.splice(newIndex, 0, executableTarget); + return newIndex; + } + + /** + * Set a target to execute at a specific position in the execution order. + * + * Infinity will set the target to execute first. 0 will set the target to + * execute last (before the stage). + * + * @param {Target} executableTarget target to move + * @param {number} newIndex position in execution order to place the target + * @returns {number} new position in the execution order + */ + setExecutablePosition (executableTarget, newIndex) { + const oldIndex = this.executableTargets.indexOf(executableTarget); + return this.moveExecutable(executableTarget, newIndex - oldIndex); + } + + /** + * Remove a target from the execution set. + * @param {Target} executableTarget target to remove + */ + removeExecutable (executableTarget) { + const oldIndex = this.executableTargets.indexOf(executableTarget); + if (oldIndex > -1) { + this.executableTargets.splice(oldIndex, 1); + } + } + + /** + * Dispose of a target. + * @param {!Target} disposingTarget Target to dispose of. + */ + disposeTarget (disposingTarget) { + this.targets = this.targets.filter(target => { + if (disposingTarget !== target) return true; + // Allow target to do dispose actions. + target.dispose(); + // Remove from list of targets. + return false; + }); + } + + /** + * Stop any threads acting on the target. + * @param {!Target} target Target to stop threads for. + * @param {Thread=} optThreadException Optional thread to skip. + */ + stopForTarget (target, optThreadException) { + // Emit stop event to allow blocks to clean up any state. + this.emit(Runtime.STOP_FOR_TARGET, target, optThreadException); + + // Stop any threads on the target. + for (let i = 0; i < this.threads.length; i++) { + if (this.threads[i] === optThreadException) { + continue; + } + if (this.threads[i].target === target) { + this._stopThread(this.threads[i]); + } + } + } + + /** + * Start all threads that start with the green flag. + */ + greenFlag () { + this.stopAll(); + this.emit(Runtime.PROJECT_START); + this.ioDevices.clock.resetProjectTimer(); + this.targets.forEach(target => target.clearEdgeActivatedValues()); + // Inform all targets of the green flag. + for (let i = 0; i < this.targets.length; i++) { + this.targets[i].onGreenFlag(); + } + this.startHats('event_whenflagclicked'); + } + + /** + * Stop "everything." + */ + stopAll () { + // Emit stop event to allow blocks to clean up any state. + this.emit(Runtime.PROJECT_STOP_ALL); + + // Dispose all clones. + const newTargets = []; + for (let i = 0; i < this.targets.length; i++) { + this.targets[i].onStopAll(); + if (this.targets[i].hasOwnProperty('isOriginal') && + !this.targets[i].isOriginal) { + this.targets[i].dispose(); + } else { + newTargets.push(this.targets[i]); + } + } + this.targets = newTargets; + // Dispose of the active thread. + if (this.sequencer.activeThread !== null) { + this._stopThread(this.sequencer.activeThread); + } + // Remove all remaining threads from executing in the next tick. + this.threads = []; + } + + /** + * Repeatedly run `sequencer.stepThreads` and filter out + * inactive threads after each iteration. + */ + _step () { + if (this.profiler !== null) { + if (stepProfilerId === -1) { + stepProfilerId = this.profiler.idByName('Runtime._step'); + } + this.profiler.start(stepProfilerId); + } + + // Clean up threads that were told to stop during or since the last step + this.threads = this.threads.filter(thread => !thread.isKilled); + + // Find all edge-activated hats, and add them to threads to be evaluated. + for (const hatType in this._hats) { + if (!this._hats.hasOwnProperty(hatType)) continue; + const hat = this._hats[hatType]; + if (hat.edgeActivated) { + this.startHats(hatType); + } + } + this.redrawRequested = false; + this._pushMonitors(); + if (this.profiler !== null) { + if (stepThreadsProfilerId === -1) { + stepThreadsProfilerId = this.profiler.idByName('Sequencer.stepThreads'); + } + this.profiler.start(stepThreadsProfilerId); + } + const doneThreads = this.sequencer.stepThreads(); + if (this.profiler !== null) { + this.profiler.stop(); + } + this._updateGlows(doneThreads); + // Add done threads so that even if a thread finishes within 1 frame, the green + // flag will still indicate that a script ran. + this._emitProjectRunStatus( + this.threads.length + doneThreads.length - + this._getMonitorThreadCount([...this.threads, ...doneThreads])); + // Store threads that completed this iteration for testing and other + // internal purposes. + this._lastStepDoneThreads = doneThreads; + if (this.renderer) { + // @todo: Only render when this.redrawRequested or clones rendered. + if (this.profiler !== null) { + if (rendererDrawProfilerId === -1) { + rendererDrawProfilerId = this.profiler.idByName('RenderWebGL.draw'); + } + this.profiler.start(rendererDrawProfilerId); + } + this.renderer.draw(); + if (this.profiler !== null) { + this.profiler.stop(); + } + } + + if (this._refreshTargets) { + this.emit(Runtime.TARGETS_UPDATE, false /* Don't emit project changed */); + this._refreshTargets = false; + } + + if (!this._prevMonitorState.equals(this._monitorState)) { + this.emit(Runtime.MONITORS_UPDATE, this._monitorState); + this._prevMonitorState = this._monitorState; + } + + if (this.profiler !== null) { + this.profiler.stop(); + this.profiler.reportFrames(); + } + } + + /** + * Get the number of threads in the given array that are monitor threads (threads + * that update monitor values, and don't count as running a script). + * @param {!Array.} threads The set of threads to look through. + * @return {number} The number of monitor threads in threads. + */ + _getMonitorThreadCount (threads) { + let count = 0; + threads.forEach(thread => { + if (thread.updateMonitor) count++; + }); + return count; + } + + /** + * Queue monitor blocks to sequencer to be run. + */ + _pushMonitors () { + this.monitorBlocks.runAllMonitored(this); + } + + /** + * Set the current editing target known by the runtime. + * @param {!Target} editingTarget New editing target. + */ + setEditingTarget (editingTarget) { + const oldEditingTarget = this._editingTarget; + this._editingTarget = editingTarget; + // Script glows must be cleared. + this._scriptGlowsPreviousFrame = []; + this._updateGlows(); + + if (oldEditingTarget !== this._editingTarget) { + this.requestToolboxExtensionsUpdate(); + } + } + + /** + * Set whether we are in 30 TPS compatibility mode. + * @param {boolean} compatibilityModeOn True iff in compatibility mode. + */ + setCompatibilityMode (compatibilityModeOn) { + this.compatibilityMode = compatibilityModeOn; + if (this._steppingInterval) { + clearInterval(this._steppingInterval); + this._steppingInterval = null; + this.start(); + } + } + + /** + * Emit glows/glow clears for scripts after a single tick. + * Looks at `this.threads` and notices which have turned on/off new glows. + * @param {Array.=} optExtraThreads Optional list of inactive threads. + */ + _updateGlows (optExtraThreads) { + const searchThreads = []; + searchThreads.push.apply(searchThreads, this.threads); + if (optExtraThreads) { + searchThreads.push.apply(searchThreads, optExtraThreads); + } + // Set of scripts that request a glow this frame. + const requestedGlowsThisFrame = []; + // Final set of scripts glowing during this frame. + const finalScriptGlows = []; + // Find all scripts that should be glowing. + for (let i = 0; i < searchThreads.length; i++) { + const thread = searchThreads[i]; + const target = thread.target; + if (target === this._editingTarget) { + const blockForThread = thread.blockGlowInFrame; + if (thread.requestScriptGlowInFrame || thread.stackClick) { + let script = target.blocks.getTopLevelScript(blockForThread); + if (!script) { + // Attempt to find in flyout blocks. + script = this.flyoutBlocks.getTopLevelScript( + blockForThread + ); + } + if (script) { + requestedGlowsThisFrame.push(script); + } + } + } + } + // Compare to previous frame. + for (let j = 0; j < this._scriptGlowsPreviousFrame.length; j++) { + const previousFrameGlow = this._scriptGlowsPreviousFrame[j]; + if (requestedGlowsThisFrame.indexOf(previousFrameGlow) < 0) { + // Glow turned off. + this.glowScript(previousFrameGlow, false); + } else { + // Still glowing. + finalScriptGlows.push(previousFrameGlow); + } + } + for (let k = 0; k < requestedGlowsThisFrame.length; k++) { + const currentFrameGlow = requestedGlowsThisFrame[k]; + if (this._scriptGlowsPreviousFrame.indexOf(currentFrameGlow) < 0) { + // Glow turned on. + this.glowScript(currentFrameGlow, true); + finalScriptGlows.push(currentFrameGlow); + } + } + this._scriptGlowsPreviousFrame = finalScriptGlows; + } + + /** + * Emit run start/stop after each tick. Emits when `this.threads.length` goes + * between non-zero and zero + * + * @param {number} nonMonitorThreadCount The new nonMonitorThreadCount + */ + _emitProjectRunStatus (nonMonitorThreadCount) { + if (this._nonMonitorThreadCount === 0 && nonMonitorThreadCount > 0) { + this.emit(Runtime.PROJECT_RUN_START); + } + if (this._nonMonitorThreadCount > 0 && nonMonitorThreadCount === 0) { + this.emit(Runtime.PROJECT_RUN_STOP); + } + this._nonMonitorThreadCount = nonMonitorThreadCount; + } + + /** + * "Quiet" a script's glow: stop the VM from generating glow/unglow events + * about that script. Use when a script has just been deleted, but we may + * still be tracking glow data about it. + * @param {!string} scriptBlockId Id of top-level block in script to quiet. + */ + quietGlow (scriptBlockId) { + const index = this._scriptGlowsPreviousFrame.indexOf(scriptBlockId); + if (index > -1) { + this._scriptGlowsPreviousFrame.splice(index, 1); + } + } + + /** + * Emit feedback for block glowing (used in the sequencer). + * @param {?string} blockId ID for the block to update glow + * @param {boolean} isGlowing True to turn on glow; false to turn off. + */ + glowBlock (blockId, isGlowing) { + if (isGlowing) { + this.emit(Runtime.BLOCK_GLOW_ON, {id: blockId}); + } else { + this.emit(Runtime.BLOCK_GLOW_OFF, {id: blockId}); + } + } + + /** + * Emit feedback for script glowing. + * @param {?string} topBlockId ID for the top block to update glow + * @param {boolean} isGlowing True to turn on glow; false to turn off. + */ + glowScript (topBlockId, isGlowing) { + if (isGlowing) { + this.emit(Runtime.SCRIPT_GLOW_ON, {id: topBlockId}); + } else { + this.emit(Runtime.SCRIPT_GLOW_OFF, {id: topBlockId}); + } + } + + /** + * Emit whether blocks are being dragged over gui + * @param {boolean} areBlocksOverGui True if blocks are dragged out of blocks workspace, false otherwise + */ + emitBlockDragUpdate (areBlocksOverGui) { + this.emit(Runtime.BLOCK_DRAG_UPDATE, areBlocksOverGui); + } + + /** + * Emit event to indicate that the block drag has ended with the blocks outside the blocks workspace + * @param {Array.} blocks The set of blocks dragged to the GUI + * @param {string} topBlockId The original id of the top block being dragged + */ + emitBlockEndDrag (blocks, topBlockId) { + this.emit(Runtime.BLOCK_DRAG_END, blocks, topBlockId); + } + + /** + * Emit value for reporter to show in the blocks. + * @param {string} blockId ID for the block. + * @param {string} value Value to show associated with the block. + */ + visualReport (blockId, value) { + this.emit(Runtime.VISUAL_REPORT, {id: blockId, value: String(value)}); + } + + /** + * Add a monitor to the state. If the monitor already exists in the state, + * updates those properties that are defined in the given monitor record. + * @param {!MonitorRecord} monitor Monitor to add. + */ + requestAddMonitor (monitor) { + const id = monitor.get('id'); + if (!this.requestUpdateMonitor(monitor)) { // update monitor if it exists in the state + // if the monitor did not exist in the state, add it + this._monitorState = this._monitorState.set(id, monitor); + } + } + + /** + * Update a monitor in the state and report success/failure of update. + * @param {!Map} monitor Monitor values to update. Values on the monitor with overwrite + * values on the old monitor with the same ID. If a value isn't defined on the new monitor, + * the old monitor will keep its old value. + * @return {boolean} true if monitor exists in the state and was updated, false if it did not exist. + */ + requestUpdateMonitor (monitor) { + const id = monitor.get('id'); + if (this._monitorState.has(id)) { + this._monitorState = + // Use mergeWith here to prevent undefined values from overwriting existing ones + this._monitorState.set(id, this._monitorState.get(id).mergeWith((prev, next) => { + if (typeof next === 'undefined' || next === null) { + return prev; + } + return next; + }, monitor)); + return true; + } + return false; + } + + /** + * Removes a monitor from the state. Does nothing if the monitor already does + * not exist in the state. + * @param {!string} monitorId ID of the monitor to remove. + */ + requestRemoveMonitor (monitorId) { + this._monitorState = this._monitorState.delete(monitorId); + } + + /** + * Hides a monitor and returns success/failure of action. + * @param {!string} monitorId ID of the monitor to hide. + * @return {boolean} true if monitor exists and was updated, false otherwise + */ + requestHideMonitor (monitorId) { + return this.requestUpdateMonitor(new Map([ + ['id', monitorId], + ['visible', false] + ])); + } + + /** + * Shows a monitor and returns success/failure of action. + * not exist in the state. + * @param {!string} monitorId ID of the monitor to show. + * @return {boolean} true if monitor exists and was updated, false otherwise + */ + requestShowMonitor (monitorId) { + return this.requestUpdateMonitor(new Map([ + ['id', monitorId], + ['visible', true] + ])); + } + + /** + * Removes all monitors with the given target ID from the state. Does nothing if + * the monitor already does not exist in the state. + * @param {!string} targetId Remove all monitors with given target ID. + */ + requestRemoveMonitorByTargetId (targetId) { + this._monitorState = this._monitorState.filterNot(value => value.targetId === targetId); + } + + /** + * Get a target by its id. + * @param {string} targetId Id of target to find. + * @return {?Target} The target, if found. + */ + getTargetById (targetId) { + for (let i = 0; i < this.targets.length; i++) { + const target = this.targets[i]; + if (target.id === targetId) { + return target; + } + } + } + + /** + * Get the first original (non-clone-block-created) sprite given a name. + * @param {string} spriteName Name of sprite to look for. + * @return {?Target} Target representing a sprite of the given name. + */ + getSpriteTargetByName (spriteName) { + for (let i = 0; i < this.targets.length; i++) { + const target = this.targets[i]; + if (target.isStage) { + continue; + } + if (target.sprite && target.sprite.name === spriteName) { + return target; + } + } + } + + /** + * Get a target by its drawable id. + * @param {number} drawableID drawable id of target to find + * @return {?Target} The target, if found + */ + getTargetByDrawableId (drawableID) { + for (let i = 0; i < this.targets.length; i++) { + const target = this.targets[i]; + if (target.drawableID === drawableID) return target; + } + } + + /** + * Update the clone counter to track how many clones are created. + * @param {number} changeAmount How many clones have been created/destroyed. + */ + changeCloneCounter (changeAmount) { + this._cloneCounter += changeAmount; + } + + /** + * Return whether there are clones available. + * @return {boolean} True until the number of clones hits Runtime.MAX_CLONES. + */ + clonesAvailable () { + return this._cloneCounter < Runtime.MAX_CLONES; + } + + /** + * Report that the project has loaded in the Virtual Machine. + */ + emitProjectLoaded () { + this.emit(Runtime.PROJECT_LOADED); + } + + /** + * Report that the project has changed in a way that would affect serialization + */ + emitProjectChanged () { + this.emit(Runtime.PROJECT_CHANGED); + } + + /** + * Report that a new target has been created, possibly by cloning an existing target. + * @param {Target} newTarget - the newly created target. + * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any. + * @fires Runtime#targetWasCreated + */ + fireTargetWasCreated (newTarget, sourceTarget) { + this.emit('targetWasCreated', newTarget, sourceTarget); + } + + /** + * Report that a clone target is being removed. + * @param {Target} target - the target being removed + * @fires Runtime#targetWasRemoved + */ + fireTargetWasRemoved (target) { + this.emit('targetWasRemoved', target); + } + + /** + * Get a target representing the Scratch stage, if one exists. + * @return {?Target} The target, if found. + */ + getTargetForStage () { + for (let i = 0; i < this.targets.length; i++) { + const target = this.targets[i]; + if (target.isStage) { + return target; + } + } + } + + /** + * Get the editing target. + * @return {?Target} The editing target. + */ + getEditingTarget () { + return this._editingTarget; + } + + getAllVarNamesOfType (varType) { + let varNames = []; + for (const target of this.targets) { + const targetVarNames = target.getAllVariableNamesInScopeByType(varType, true); + varNames = varNames.concat(targetVarNames); + } + return varNames; + } + + /** + * Get the label or label function for an opcode + * @param {string} extendedOpcode - the opcode you want a label for + * @return {object} - object with label and category + * @property {string} category - the category for this opcode + * @property {Function} [labelFn] - function to generate the label for this opcode + * @property {string} [label] - the label for this opcode if `labelFn` is absent + */ + getLabelForOpcode (extendedOpcode) { + const [category, opcode] = StringUtil.splitFirst(extendedOpcode, '_'); + if (!(category && opcode)) return; + + const categoryInfo = this._blockInfo.find(ci => ci.id === category); + if (!categoryInfo) return; + + const block = categoryInfo.blocks.find(b => b.info.opcode === opcode); + if (!block) return; + + // TODO: we may want to format the label in a locale-specific way. + return { + category: 'extension', // This assumes that all extensions have the same monitor color. + label: `${categoryInfo.name}: ${block.info.text}` + }; + } + + /** + * Create a new global variable avoiding conflicts with other variable names. + * @param {string} variableName The desired variable name for the new global variable. + * This can be turned into a fresh name as necessary. + * @param {string} optVarId An optional ID to use for the variable. A new one will be generated + * if a falsey value for this parameter is provided. + * @param {string} optVarType The type of the variable to create. Defaults to Variable.SCALAR_TYPE. + * @return {Variable} The new variable that was created. + */ + createNewGlobalVariable (variableName, optVarId, optVarType) { + const varType = (typeof optVarType === 'string') ? optVarType : Variable.SCALAR_TYPE; + const allVariableNames = this.getAllVarNamesOfType(varType); + const newName = StringUtil.unusedName(variableName, allVariableNames); + const variable = new Variable(optVarId || uid(), newName, varType); + const stage = this.getTargetForStage(); + stage.variables[variable.id] = variable; + return variable; + } + + /** + * Tell the runtime to request a redraw. + * Use after a clone/sprite has completed some visible operation on the stage. + */ + requestRedraw () { + this.redrawRequested = true; + } + + /** + * Emit a targets update at the end of the step if the provided target is + * the original sprite + * @param {!Target} target Target requesting the targets update + */ + requestTargetsUpdate (target) { + if (!target.isOriginal) return; + this._refreshTargets = true; + } + + /** + * Emit an event that indicates that the blocks on the workspace need updating. + */ + requestBlocksUpdate () { + this.emit(Runtime.BLOCKS_NEED_UPDATE); + } + + /** + * Emit an event that indicates that the toolbox extension blocks need updating. + */ + requestToolboxExtensionsUpdate () { + this.emit(Runtime.TOOLBOX_EXTENSIONS_NEED_UPDATE); + } + + /** + * Set up timers to repeatedly step in a browser. + */ + start () { + // Do not start if we are already running + if (this._steppingInterval) return; + + let interval = Runtime.THREAD_STEP_INTERVAL; + if (this.compatibilityMode) { + interval = Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY; + } + this.currentStepTime = interval; + this._steppingInterval = setInterval(() => { + this._step(); + }, interval); + this.emit(Runtime.RUNTIME_STARTED); + } + + /** + * Quit the Runtime, clearing any handles which might keep the process alive. + * Do not use the runtime after calling this method. This method is meant for test shutdown. + */ + quit () { + clearInterval(this._steppingInterval); + this._steppingInterval = null; + } + + /** + * Turn on profiling. + * @param {Profiler/FrameCallback} onFrame A callback handle passed a + * profiling frame when the profiler reports its collected data. + */ + enableProfiling (onFrame) { + if (Profiler.available()) { + this.profiler = new Profiler(onFrame); + } + } + + /** + * Turn off profiling. + */ + disableProfiling () { + this.profiler = null; + } + + /** + * Update a millisecond timestamp value that is saved on the Runtime. + * This value is helpful in certain instances for compatibility with Scratch 2, + * which sometimes uses a `currentMSecs` timestamp value in Interpreter.as + */ + updateCurrentMSecs () { + this.currentMSecs = Date.now(); + } +} + +/** + * Event fired after a new target has been created, possibly by cloning an existing target. + * + * @event Runtime#targetWasCreated + * @param {Target} newTarget - the newly created target. + * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any. + */ + +module.exports = Runtime; diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js new file mode 100644 index 0000000000..a5e2c4ec8f --- /dev/null +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -0,0 +1,440 @@ +const dispatch = require('../dispatch/central-dispatch'); +const log = require('../util/log'); +const maybeFormatMessage = require('../util/maybe-format-message'); + +const BlockType = require('./block-type'); + +// These extensions are currently built into the VM repository but should not be loaded at startup. +// TODO: move these out into a separate repository? +// TODO: change extension spec so that library info, including extension ID, can be collected through static methods + +const builtinExtensions = { + // This is an example that isn't loaded with the other core blocks, + // but serves as a reference for loading core blocks as extensions. + coreExample: () => require('../blocks/scratch3_core_example'), + // These are the non-core built-in extensions. + pen: () => require('../extensions/scratch3_pen'), + wedo2: () => require('../extensions/scratch3_wedo2'), + music: () => require('../extensions/scratch3_music'), + microbit: () => require('../extensions/scratch3_microbit'), + text2speech: () => require('../extensions/scratch3_text2speech'), + translate: () => require('../extensions/scratch3_translate'), + videoSensing: () => require('../extensions/scratch3_video_sensing'), + ev3: () => require('../extensions/scratch3_ev3'), + makeymakey: () => require('../extensions/scratch3_makeymakey'), + boost: () => require('../extensions/scratch3_boost'), + gdxfor: () => require('../extensions/scratch3_gdx_for') +}; + +/** + * @typedef {object} ArgumentInfo - Information about an extension block argument + * @property {ArgumentType} type - the type of value this argument can take + * @property {*|undefined} default - the default value of this argument (default: blank) + */ + +/** + * @typedef {object} ConvertedBlockInfo - Raw extension block data paired with processed data ready for scratch-blocks + * @property {ExtensionBlockMetadata} info - the raw block info + * @property {object} json - the scratch-blocks JSON definition for this block + * @property {string} xml - the scratch-blocks XML definition for this block + */ + +/** + * @typedef {object} CategoryInfo - Information about a block category + * @property {string} id - the unique ID of this category + * @property {string} name - the human-readable name of this category + * @property {string|undefined} blockIconURI - optional URI for the block icon image + * @property {string} color1 - the primary color for this category, in '#rrggbb' format + * @property {string} color2 - the secondary color for this category, in '#rrggbb' format + * @property {string} color3 - the tertiary color for this category, in '#rrggbb' format + * @property {Array.} blocks - the blocks, separators, etc. in this category + * @property {Array.} menus - the menus provided by this category + */ + +/** + * @typedef {object} PendingExtensionWorker - Information about an extension worker still initializing + * @property {string} extensionURL - the URL of the extension to be loaded by this worker + * @property {Function} resolve - function to call on successful worker startup + * @property {Function} reject - function to call on failed worker startup + */ + +class ExtensionManager { + constructor (runtime) { + /** + * The ID number to provide to the next extension worker. + * @type {int} + */ + this.nextExtensionWorker = 0; + + /** + * FIFO queue of extensions which have been requested but not yet loaded in a worker, + * along with promise resolution functions to call once the worker is ready or failed. + * + * @type {Array.} + */ + this.pendingExtensions = []; + + /** + * Map of worker ID to workers which have been allocated but have not yet finished initialization. + * @type {Array.} + */ + this.pendingWorkers = []; + + /** + * Map of loaded extension URLs/IDs (equivalent for built-in extensions) to service name. + * @type {Map.} + * @private + */ + this._loadedExtensions = new Map(); + + /** + * Keep a reference to the runtime so we can construct internal extension objects. + * TODO: remove this in favor of extensions accessing the runtime as a service. + * @type {Runtime} + */ + this.runtime = runtime; + + dispatch.setService('extensions', this).catch(e => { + log.error(`ExtensionManager was unable to register extension service: ${JSON.stringify(e)}`); + }); + } + + /** + * Check whether an extension is registered or is in the process of loading. This is intended to control loading or + * adding extensions so it may return `true` before the extension is ready to be used. Use the promise returned by + * `loadExtensionURL` if you need to wait until the extension is truly ready. + * @param {string} extensionID - the ID of the extension. + * @returns {boolean} - true if loaded, false otherwise. + */ + isExtensionLoaded (extensionID) { + return this._loadedExtensions.has(extensionID); + } + + /** + * Synchronously load an internal extension (core or non-core) by ID. This call will + * fail if the provided id is not does not match an internal extension. + * @param {string} extensionId - the ID of an internal extension + */ + loadExtensionIdSync (extensionId) { + if (!builtinExtensions.hasOwnProperty(extensionId)) { + log.warn(`Could not find extension ${extensionId} in the built in extensions.`); + return; + } + + /** @TODO dupe handling for non-builtin extensions. See commit 670e51d33580e8a2e852b3b038bb3afc282f81b9 */ + if (this.isExtensionLoaded(extensionId)) { + const message = `Rejecting attempt to load a second extension with ID ${extensionId}`; + log.warn(message); + return; + } + + const extension = builtinExtensions[extensionId](); + const extensionInstance = new extension(this.runtime); + const serviceName = this._registerInternalExtension(extensionInstance); + this._loadedExtensions.set(extensionId, serviceName); + } + + /** + * Load an extension by URL or internal extension ID + * @param {string} extensionURL - the URL for the extension to load OR the ID of an internal extension + * @returns {Promise} resolved once the extension is loaded and initialized or rejected on failure + */ + loadExtensionURL (extensionURL) { + if (builtinExtensions.hasOwnProperty(extensionURL)) { + /** @TODO dupe handling for non-builtin extensions. See commit 670e51d33580e8a2e852b3b038bb3afc282f81b9 */ + if (this.isExtensionLoaded(extensionURL)) { + const message = `Rejecting attempt to load a second extension with ID ${extensionURL}`; + log.warn(message); + return Promise.resolve(); + } + + const extension = builtinExtensions[extensionURL](); + const extensionInstance = new extension(this.runtime); + const serviceName = this._registerInternalExtension(extensionInstance); + this._loadedExtensions.set(extensionURL, serviceName); + return Promise.resolve(); + } + + return new Promise((resolve, reject) => { + // If we `require` this at the global level it breaks non-webpack targets, including tests + const ExtensionWorker = require('worker-loader?name=extension-worker.js!./extension-worker'); + + this.pendingExtensions.push({extensionURL, resolve, reject}); + dispatch.addWorker(new ExtensionWorker()); + }); + } + + /** + * Regenerate blockinfo for any loaded extensions + * @returns {Promise} resolved once all the extensions have been reinitialized + */ + refreshBlocks () { + const allPromises = Array.from(this._loadedExtensions.values()).map(serviceName => + dispatch.call(serviceName, 'getInfo') + .then(info => { + info = this._prepareExtensionInfo(serviceName, info); + dispatch.call('runtime', '_refreshExtensionPrimitives', info); + }) + .catch(e => { + log.error(`Failed to refresh built-in extension primitives: ${JSON.stringify(e)}`); + }) + ); + return Promise.all(allPromises); + } + + allocateWorker () { + const id = this.nextExtensionWorker++; + const workerInfo = this.pendingExtensions.shift(); + this.pendingWorkers[id] = workerInfo; + return [id, workerInfo.extensionURL]; + } + + /** + * Synchronously collect extension metadata from the specified service and begin the extension registration process. + * @param {string} serviceName - the name of the service hosting the extension. + */ + registerExtensionServiceSync (serviceName) { + const info = dispatch.callSync(serviceName, 'getInfo'); + this._registerExtensionInfo(serviceName, info); + } + + /** + * Collect extension metadata from the specified service and begin the extension registration process. + * @param {string} serviceName - the name of the service hosting the extension. + */ + registerExtensionService (serviceName) { + dispatch.call(serviceName, 'getInfo').then(info => { + this._registerExtensionInfo(serviceName, info); + }); + } + + /** + * Called by an extension worker to indicate that the worker has finished initialization. + * @param {int} id - the worker ID. + * @param {*?} e - the error encountered during initialization, if any. + */ + onWorkerInit (id, e) { + const workerInfo = this.pendingWorkers[id]; + delete this.pendingWorkers[id]; + if (e) { + workerInfo.reject(e); + } else { + workerInfo.resolve(id); + } + } + + /** + * Register an internal (non-Worker) extension object + * @param {object} extensionObject - the extension object to register + * @returns {string} The name of the registered extension service + */ + _registerInternalExtension (extensionObject) { + const extensionInfo = extensionObject.getInfo(); + const fakeWorkerId = this.nextExtensionWorker++; + const serviceName = `extension_${fakeWorkerId}_${extensionInfo.id}`; + dispatch.setServiceSync(serviceName, extensionObject); + dispatch.callSync('extensions', 'registerExtensionServiceSync', serviceName); + return serviceName; + } + + /** + * Sanitize extension info then register its primitives with the VM. + * @param {string} serviceName - the name of the service hosting the extension + * @param {ExtensionInfo} extensionInfo - the extension's metadata + * @private + */ + _registerExtensionInfo (serviceName, extensionInfo) { + extensionInfo = this._prepareExtensionInfo(serviceName, extensionInfo); + dispatch.call('runtime', '_registerExtensionPrimitives', extensionInfo).catch(e => { + log.error(`Failed to register primitives for extension on service ${serviceName}:`, e); + }); + } + + /** + * Modify the provided text as necessary to ensure that it may be used as an attribute value in valid XML. + * @param {string} text - the text to be sanitized + * @returns {string} - the sanitized text + * @private + */ + _sanitizeID (text) { + return text.toString().replace(/[<"&]/, '_'); + } + + /** + * Apply minor cleanup and defaults for optional extension fields. + * TODO: make the ID unique in cases where two copies of the same extension are loaded. + * @param {string} serviceName - the name of the service hosting this extension block + * @param {ExtensionInfo} extensionInfo - the extension info to be sanitized + * @returns {ExtensionInfo} - a new extension info object with cleaned-up values + * @private + */ + _prepareExtensionInfo (serviceName, extensionInfo) { + extensionInfo = Object.assign({}, extensionInfo); + if (!/^[a-z0-9]+$/i.test(extensionInfo.id)) { + throw new Error('Invalid extension id'); + } + extensionInfo.name = extensionInfo.name || extensionInfo.id; + extensionInfo.blocks = extensionInfo.blocks || []; + extensionInfo.targetTypes = extensionInfo.targetTypes || []; + extensionInfo.blocks = extensionInfo.blocks.reduce((results, blockInfo) => { + try { + let result; + switch (blockInfo) { + case '---': // separator + result = '---'; + break; + default: // an ExtensionBlockMetadata object + result = this._prepareBlockInfo(serviceName, blockInfo); + break; + } + results.push(result); + } catch (e) { + // TODO: more meaningful error reporting + log.error(`Error processing block: ${e.message}, Block:\n${JSON.stringify(blockInfo)}`); + } + return results; + }, []); + extensionInfo.menus = extensionInfo.menus || {}; + extensionInfo.menus = this._prepareMenuInfo(serviceName, extensionInfo.menus); + return extensionInfo; + } + + /** + * Prepare extension menus. e.g. setup binding for dynamic menu functions. + * @param {string} serviceName - the name of the service hosting this extension block + * @param {Array.} menus - the menu defined by the extension. + * @returns {Array.} - a menuInfo object with all preprocessing done. + * @private + */ + _prepareMenuInfo (serviceName, menus) { + const menuNames = Object.getOwnPropertyNames(menus); + for (let i = 0; i < menuNames.length; i++) { + const menuName = menuNames[i]; + let menuInfo = menus[menuName]; + + // If the menu description is in short form (items only) then normalize it to general form: an object with + // its items listed in an `items` property. + if (!menuInfo.items) { + menuInfo = { + items: menuInfo + }; + menus[menuName] = menuInfo; + } + // If `items` is a string, it should be the name of a function in the extension object. Calling the + // function should return an array of items to populate the menu when it is opened. + if (typeof menuInfo.items === 'string') { + const menuItemFunctionName = menuInfo.items; + const serviceObject = dispatch.services[serviceName]; + // Bind the function here so we can pass a simple item generation function to Scratch Blocks later. + menuInfo.items = this._getExtensionMenuItems.bind(this, serviceObject, menuItemFunctionName); + } + } + return menus; + } + + /** + * Fetch the items for a particular extension menu, providing the target ID for context. + * @param {object} extensionObject - the extension object providing the menu. + * @param {string} menuItemFunctionName - the name of the menu function to call. + * @returns {Array} menu items ready for scratch-blocks. + * @private + */ + _getExtensionMenuItems (extensionObject, menuItemFunctionName) { + // Fetch the items appropriate for the target currently being edited. This assumes that menus only + // collect items when opened by the user while editing a particular target. + const editingTarget = this.runtime.getEditingTarget() || this.runtime.getTargetForStage(); + const editingTargetID = editingTarget ? editingTarget.id : null; + const extensionMessageContext = this.runtime.makeMessageContextForTarget(editingTarget); + + // TODO: Fix this to use dispatch.call when extensions are running in workers. + const menuFunc = extensionObject[menuItemFunctionName]; + const menuItems = menuFunc.call(extensionObject, editingTargetID).map( + item => { + item = maybeFormatMessage(item, extensionMessageContext); + switch (typeof item) { + case 'object': + return [ + maybeFormatMessage(item.text, extensionMessageContext), + item.value + ]; + case 'string': + return [item, item]; + default: + return item; + } + }); + + if (!menuItems || menuItems.length < 1) { + throw new Error(`Extension menu returned no items: ${menuItemFunctionName}`); + } + return menuItems; + } + + /** + * Apply defaults for optional block fields. + * @param {string} serviceName - the name of the service hosting this extension block + * @param {ExtensionBlockMetadata} blockInfo - the block info from the extension + * @returns {ExtensionBlockMetadata} - a new block info object which has values for all relevant optional fields. + * @private + */ + _prepareBlockInfo (serviceName, blockInfo) { + blockInfo = Object.assign({}, { + blockType: BlockType.COMMAND, + terminal: false, + blockAllThreads: false, + arguments: {} + }, blockInfo); + blockInfo.opcode = blockInfo.opcode && this._sanitizeID(blockInfo.opcode); + blockInfo.text = blockInfo.text || blockInfo.opcode; + + switch (blockInfo.blockType) { + case BlockType.EVENT: + if (blockInfo.func) { + log.warn(`Ignoring function "${blockInfo.func}" for event block ${blockInfo.opcode}`); + } + break; + case BlockType.BUTTON: + if (blockInfo.opcode) { + log.warn(`Ignoring opcode "${blockInfo.opcode}" for button with text: ${blockInfo.text}`); + } + break; + default: { + if (!blockInfo.opcode) { + throw new Error('Missing opcode for block'); + } + + const funcName = blockInfo.func ? this._sanitizeID(blockInfo.func) : blockInfo.opcode; + + const getBlockInfo = blockInfo.isDynamic ? + args => args && args.mutation && args.mutation.blockInfo : + () => blockInfo; + const callBlockFunc = (() => { + if (dispatch._isRemoteService(serviceName)) { + return (args, util, realBlockInfo) => + dispatch.call(serviceName, funcName, args, util, realBlockInfo); + } + + // avoid promise latency if we can call direct + const serviceObject = dispatch.services[serviceName]; + if (!serviceObject[funcName]) { + // The function might show up later as a dynamic property of the service object + log.warn(`Could not find extension block function called ${funcName}`); + } + return (args, util, realBlockInfo) => + serviceObject[funcName](args, util, realBlockInfo); + })(); + + blockInfo.func = (args, util) => { + const realBlockInfo = getBlockInfo(args); + // TODO: filter args using the keys of realBlockInfo.arguments? maybe only if sandboxed? + return callBlockFunc(args, util, realBlockInfo); + }; + break; + } + } + + return blockInfo; + } +} + +module.exports = ExtensionManager; diff --git a/packages/scratch-vm/src/sprites/rendered-target.js b/packages/scratch-vm/src/sprites/rendered-target.js new file mode 100644 index 0000000000..b0c1ebfed9 --- /dev/null +++ b/packages/scratch-vm/src/sprites/rendered-target.js @@ -0,0 +1,1115 @@ +const MathUtil = require('../util/math-util'); +const StringUtil = require('../util/string-util'); +const Cast = require('../util/cast'); +const Clone = require('../util/clone'); +const Target = require('../engine/target'); +const StageLayering = require('../engine/stage-layering'); + +/** + * Rendered target: instance of a sprite (clone), or the stage. + */ +class RenderedTarget extends Target { + /** + * @param {!Sprite} sprite Reference to the parent sprite. + * @param {Runtime} runtime Reference to the runtime. + * @constructor + */ + constructor (sprite, runtime) { + super(runtime, sprite.blocks); + + /** + * Reference to the sprite that this is a render of. + * @type {!Sprite} + */ + this.sprite = sprite; + /** + * Reference to the global renderer for this VM, if one exists. + * @type {?RenderWebGL} + */ + this.renderer = null; + if (this.runtime) { + this.renderer = this.runtime.renderer; + } + /** + * ID of the drawable for this rendered target, + * returned by the renderer, if rendered. + * @type {?Number} + */ + this.drawableID = null; + + /** + * Drag state of this rendered target. If true, x/y position can't be + * changed by blocks. + * @type {boolean} + */ + this.dragging = false; + + /** + * Map of current graphic effect values. + * @type {!Object.} + */ + this.effects = { + color: 0, + fisheye: 0, + whirl: 0, + pixelate: 0, + mosaic: 0, + brightness: 0, + ghost: 0 + }; + + /** + * Whether this represents an "original" non-clone rendered-target for a sprite, + * i.e., created by the editor and not clone blocks. + * @type {boolean} + */ + this.isOriginal = true; + + /** + * Whether this rendered target represents the Scratch stage. + * @type {boolean} + */ + this.isStage = false; + + /** + * Scratch X coordinate. Currently should range from -240 to 240. + * @type {Number} + */ + this.x = 0; + + /** + * Scratch Y coordinate. Currently should range from -180 to 180. + * @type {number} + */ + this.y = 0; + + /** + * Scratch direction. Currently should range from -179 to 180. + * @type {number} + */ + this.direction = 90; + + /** + * Whether the rendered target is draggable on the stage + * @type {boolean} + */ + this.draggable = false; + + /** + * Whether the rendered target is currently visible. + * @type {boolean} + */ + this.visible = true; + + /** + * Size of rendered target as a percent of costume size. + * @type {number} + */ + this.size = 100; + + /** + * Currently selected costume index. + * @type {number} + */ + this.currentCostume = 0; + + /** + * Current rotation style. + * @type {!string} + */ + this.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND; + + /** + * Loudness for sound playback for this target, as a percentage. + * @type {number} + */ + this.volume = 100; + + /** + * Current tempo (used by the music extension). + * This property is global to the project and stored in the stage. + * @type {number} + */ + this.tempo = 60; + + /** + * The transparency of the video (used by extensions with camera input). + * This property is global to the project and stored in the stage. + * @type {number} + */ + this.videoTransparency = 50; + + /** + * The state of the video input (used by extensions with camera input). + * This property is global to the project and stored in the stage. + * + * Defaults to ON. This setting does not turn the video by itself. A + * video extension once loaded will set the video device to this + * setting. Set to ON when a video extension is added in the editor the + * video will start ON. If the extension is loaded as part of loading a + * saved project the extension will see the value set when the stage + * was loaded from the saved values including the video state. + * + * @type {string} + */ + this.videoState = RenderedTarget.VIDEO_STATE.ON; + + /** + * The language to use for speech synthesis, in the text2speech extension. + * It is initialized to null so that on extension load, we can check for + * this and try setting it using the editor locale. + * @type {string} + */ + this.textToSpeechLanguage = null; + } + + /** + * Create a drawable with the this.renderer. + * @param {boolean} layerGroup The layer group this drawable should be added to + */ + initDrawable (layerGroup) { + if (this.renderer) { + this.drawableID = this.renderer.createDrawable(layerGroup); + } + // If we're a clone, start the hats. + if (!this.isOriginal) { + this.runtime.startHats( + 'control_start_as_clone', null, this + ); + } + } + + get audioPlayer () { + /* eslint-disable no-console */ + console.warn('get audioPlayer deprecated, please update to use .sprite.soundBank methods'); + console.warn(new Error('stack for debug').stack); + /* eslint-enable no-console */ + const bank = this.sprite.soundBank; + const audioPlayerProxy = { + playSound: soundId => bank.play(this, soundId) + }; + + Object.defineProperty(this, 'audioPlayer', { + configurable: false, + enumerable: true, + writable: false, + value: audioPlayerProxy + }); + + return audioPlayerProxy; + } + + /** + * Initialize the audio player for this sprite or clone. + */ + initAudio () { + } + + /** + * Event which fires when a target moves. + * @type {string} + */ + static get EVENT_TARGET_MOVED () { + return 'TARGET_MOVED'; + } + + /** + * Event which fires when a target changes visually, for updating say bubbles. + * @type {string} + */ + static get EVENT_TARGET_VISUAL_CHANGE () { + return 'EVENT_TARGET_VISUAL_CHANGE'; + } + + /** + * Rotation style for "all around"/spinning. + * @type {string} + */ + static get ROTATION_STYLE_ALL_AROUND () { + return 'all around'; + } + + /** + * Rotation style for "left-right"/flipping. + * @type {string} + */ + static get ROTATION_STYLE_LEFT_RIGHT () { + return 'left-right'; + } + + /** + * Rotation style for "no rotation." + * @type {string} + */ + static get ROTATION_STYLE_NONE () { + return "don't rotate"; + } + + /** + * Available states for video input. + * @enum {string} + */ + static get VIDEO_STATE () { + return { + OFF: 'off', + ON: 'on', + ON_FLIPPED: 'on-flipped' + }; + } + + /** + * Set the X and Y coordinates. + * @param {!number} x New X coordinate, in Scratch coordinates. + * @param {!number} y New Y coordinate, in Scratch coordinates. + * @param {?boolean} force Force setting X/Y, in case of dragging + */ + setXY (x, y, force) { + if (this.isStage) return; + if (this.dragging && !force) return; + const oldX = this.x; + const oldY = this.y; + if (this.renderer) { + const position = this.renderer.getFencedPositionOfDrawable(this.drawableID, [x, y]); + this.x = position[0]; + this.y = position[1]; + + this.renderer.updateDrawablePosition(this.drawableID, position); + if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); + this.runtime.requestRedraw(); + } + } else { + this.x = x; + this.y = y; + } + this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY, force); + this.runtime.requestTargetsUpdate(this); + } + + /** + * Get the rendered direction and scale, after applying rotation style. + * @return {object} Direction and scale to render. + */ + _getRenderedDirectionAndScale () { + // Default: no changes to `this.direction` or `this.scale`. + let finalDirection = this.direction; + let finalScale = [this.size, this.size]; + if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_NONE) { + // Force rendered direction to be 90. + finalDirection = 90; + } else if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_LEFT_RIGHT) { + // Force rendered direction to be 90, and flip drawable if needed. + finalDirection = 90; + const scaleFlip = (this.direction < 0) ? -1 : 1; + finalScale = [scaleFlip * this.size, this.size]; + } + return {direction: finalDirection, scale: finalScale}; + } + + /** + * Set the direction. + * @param {!number} direction New direction. + */ + setDirection (direction) { + if (this.isStage) { + return; + } + if (!isFinite(direction)) { + return; + } + // Keep direction between -179 and +180. + this.direction = MathUtil.wrapClamp(direction, -179, 180); + if (this.renderer) { + const {direction: renderedDirection, scale} = this._getRenderedDirectionAndScale(); + this.renderer.updateDrawableDirectionScale(this.drawableID, renderedDirection, scale); + if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); + this.runtime.requestRedraw(); + } + } + this.runtime.requestTargetsUpdate(this); + } + + /** + * Set draggability; i.e., whether it's able to be dragged in the player + * @param {!boolean} draggable True if should be draggable. + */ + setDraggable (draggable) { + if (this.isStage) return; + this.draggable = !!draggable; + this.runtime.requestTargetsUpdate(this); + } + + /** + * Set visibility; i.e., whether it's shown or hidden. + * @param {!boolean} visible True if should be shown. + */ + setVisible (visible) { + if (this.isStage) { + return; + } + this.visible = !!visible; + if (this.renderer) { + this.renderer.updateDrawableVisible(this.drawableID, this.visible); + if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); + this.runtime.requestRedraw(); + } + } + this.runtime.requestTargetsUpdate(this); + } + + /** + * Set size, as a percentage of the costume size. + * @param {!number} size Size of rendered target, as % of costume size. + */ + setSize (size) { + if (this.isStage) { + return; + } + if (this.renderer) { + // Clamp to scales relative to costume and stage size. + // See original ScratchSprite.as:setSize. + const costumeSize = this.renderer.getCurrentSkinSize(this.drawableID); + const origW = costumeSize[0]; + const origH = costumeSize[1]; + const minScale = Math.min(1, Math.max(5 / origW, 5 / origH)); + const maxScale = Math.min( + (1.5 * this.runtime.constructor.STAGE_WIDTH) / origW, + (1.5 * this.runtime.constructor.STAGE_HEIGHT) / origH + ); + this.size = MathUtil.clamp(size / 100, minScale, maxScale) * 100; + const {direction, scale} = this._getRenderedDirectionAndScale(); + this.renderer.updateDrawableDirectionScale(this.drawableID, direction, scale); + if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); + this.runtime.requestRedraw(); + } + } + this.runtime.requestTargetsUpdate(this); + } + + /** + * Set a particular graphic effect value. + * @param {!string} effectName Name of effect (see `RenderedTarget.prototype.effects`). + * @param {!number} value Numerical magnitude of effect. + */ + setEffect (effectName, value) { + if (!this.effects.hasOwnProperty(effectName)) return; + this.effects[effectName] = value; + if (this.renderer) { + this.renderer.updateDrawableEffect(this.drawableID, effectName, value); + if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); + this.runtime.requestRedraw(); + } + } + } + + /** + * Clear all graphic effects on this rendered target. + */ + clearEffects () { + for (const effectName in this.effects) { + if (!this.effects.hasOwnProperty(effectName)) continue; + this.effects[effectName] = 0; + } + if (this.renderer) { + for (const effectName in this.effects) { + if (!this.effects.hasOwnProperty(effectName)) continue; + this.renderer.updateDrawableEffect(this.drawableID, effectName, 0); + } + if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); + this.runtime.requestRedraw(); + } + } + } + + /** + * Set the current costume. + * @param {number} index New index of costume. + */ + setCostume (index) { + // Keep the costume index within possible values. + index = Math.round(index); + if ([Infinity, -Infinity, NaN].includes(index)) index = 0; + + this.currentCostume = MathUtil.wrapClamp( + index, 0, this.sprite.costumes.length - 1 + ); + if (this.renderer) { + const costume = this.getCostumes()[this.currentCostume]; + this.renderer.updateDrawableSkinId(this.drawableID, costume.skinId); + + if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); + this.runtime.requestRedraw(); + } + } + this.runtime.requestTargetsUpdate(this); + } + + /** + * Add a costume, taking care to avoid duplicate names. + * @param {!object} costumeObject Object representing the costume. + * @param {?int} index Index at which to add costume + */ + addCostume (costumeObject, index) { + if (typeof index === 'number' && !isNaN(index)) { + this.sprite.addCostumeAt(costumeObject, index); + } else { + this.sprite.addCostumeAt(costumeObject, this.sprite.costumes.length); + } + } + + /** + * Rename a costume, taking care to avoid duplicate names. + * @param {int} costumeIndex - the index of the costume to be renamed. + * @param {string} newName - the desired new name of the costume (will be modified if already in use). + */ + renameCostume (costumeIndex, newName) { + const usedNames = this.sprite.costumes + .filter((costume, index) => costumeIndex !== index) + .map(costume => costume.name); + const oldName = this.getCostumes()[costumeIndex].name; + const newUnusedName = StringUtil.unusedName(newName, usedNames); + this.getCostumes()[costumeIndex].name = newUnusedName; + + if (this.isStage) { + // Since this is a backdrop, go through all targets and + // update any blocks referencing the old backdrop name + const targets = this.runtime.targets; + for (let i = 0; i < targets.length; i++) { + const currTarget = targets[i]; + currTarget.blocks.updateAssetName(oldName, newUnusedName, 'backdrop'); + } + } else { + this.blocks.updateAssetName(oldName, newUnusedName, 'costume'); + } + + } + + /** + * Delete a costume by index. + * @param {number} index Costume index to be deleted + * @return {?object} The costume that was deleted or null + * if the index was out of bounds of the costumes list or + * this target only has one costume. + */ + deleteCostume (index) { + const originalCostumeCount = this.sprite.costumes.length; + if (originalCostumeCount === 1) return null; + + if (index < 0 || index >= originalCostumeCount) { + return null; + } + + const deletedCostume = this.sprite.deleteCostumeAt(index); + + if (index === this.currentCostume && index === originalCostumeCount - 1) { + this.setCostume(index - 1); + } else if (index < this.currentCostume) { + this.setCostume(this.currentCostume - 1); + } else { + this.setCostume(this.currentCostume); + } + + this.runtime.requestTargetsUpdate(this); + return deletedCostume; + } + + /** + * Add a sound, taking care to avoid duplicate names. + * @param {!object} soundObject Object representing the sound. + * @param {?int} index Index at which to add costume + */ + addSound (soundObject, index) { + const usedNames = this.sprite.sounds.map(sound => sound.name); + soundObject.name = StringUtil.unusedName(soundObject.name, usedNames); + if (typeof index === 'number' && !isNaN(index)) { + this.sprite.sounds.splice(index, 0, soundObject); + } else { + this.sprite.sounds.push(soundObject); + } + } + + /** + * Rename a sound, taking care to avoid duplicate names. + * @param {int} soundIndex - the index of the sound to be renamed. + * @param {string} newName - the desired new name of the sound (will be modified if already in use). + */ + renameSound (soundIndex, newName) { + const usedNames = this.sprite.sounds + .filter((sound, index) => soundIndex !== index) + .map(sound => sound.name); + const oldName = this.sprite.sounds[soundIndex].name; + const newUnusedName = StringUtil.unusedName(newName, usedNames); + this.sprite.sounds[soundIndex].name = newUnusedName; + this.blocks.updateAssetName(oldName, newUnusedName, 'sound'); + } + + /** + * Delete a sound by index. + * @param {number} index Sound index to be deleted + * @return {object} The deleted sound object, or null if no sound was deleted. + */ + deleteSound (index) { + // Make sure the sound index is not out of bounds + if (index < 0 || index >= this.sprite.sounds.length) { + return null; + } + // Delete the sound at the given index + const deletedSound = this.sprite.sounds.splice(index, 1)[0]; + this.runtime.requestTargetsUpdate(this); + return deletedSound; + } + + /** + * Update the rotation style. + * @param {!string} rotationStyle New rotation style. + */ + setRotationStyle (rotationStyle) { + if (rotationStyle === RenderedTarget.ROTATION_STYLE_NONE) { + this.rotationStyle = RenderedTarget.ROTATION_STYLE_NONE; + } else if (rotationStyle === RenderedTarget.ROTATION_STYLE_ALL_AROUND) { + this.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND; + } else if (rotationStyle === RenderedTarget.ROTATION_STYLE_LEFT_RIGHT) { + this.rotationStyle = RenderedTarget.ROTATION_STYLE_LEFT_RIGHT; + } + if (this.renderer) { + const {direction, scale} = this._getRenderedDirectionAndScale(); + this.renderer.updateDrawableDirectionScale(this.drawableID, direction, scale); + if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); + this.runtime.requestRedraw(); + } + } + this.runtime.requestTargetsUpdate(this); + } + + /** + * Get a costume index of this rendered target, by name of the costume. + * @param {?string} costumeName Name of a costume. + * @return {number} Index of the named costume, or -1 if not present. + */ + getCostumeIndexByName (costumeName) { + for (let i = 0; i < this.sprite.costumes.length; i++) { + if (this.getCostumes()[i].name === costumeName) { + return i; + } + } + return -1; + } + + /** + * Get a costume of this rendered target by id. + * @return {object} current costume + */ + getCurrentCostume () { + return this.getCostumes()[this.currentCostume]; + } + + /** + * Get full costume list + * @return {object[]} list of costumes + */ + getCostumes () { + return this.sprite.costumes; + } + + /** + * Reorder costume list by moving costume at costumeIndex to newIndex. + * @param {!number} costumeIndex Index of the costume to move. + * @param {!number} newIndex New index for that costume. + * @returns {boolean} If a change occurred (i.e. if the indices do not match) + */ + reorderCostume (costumeIndex, newIndex) { + newIndex = MathUtil.clamp(newIndex, 0, this.sprite.costumes.length - 1); + costumeIndex = MathUtil.clamp(costumeIndex, 0, this.sprite.costumes.length - 1); + + if (newIndex === costumeIndex) return false; + + const currentCostume = this.getCurrentCostume(); + const costume = this.sprite.costumes[costumeIndex]; + + // Use the sprite method for deleting costumes because setCostume is handled manually + this.sprite.deleteCostumeAt(costumeIndex); + + this.addCostume(costume, newIndex); + this.currentCostume = this.getCostumeIndexByName(currentCostume.name); + return true; + } + + /** + * Reorder sound list by moving sound at soundIndex to newIndex. + * @param {!number} soundIndex Index of the sound to move. + * @param {!number} newIndex New index for that sound. + * @returns {boolean} If a change occurred (i.e. if the indices do not match) + */ + reorderSound (soundIndex, newIndex) { + newIndex = MathUtil.clamp(newIndex, 0, this.sprite.sounds.length - 1); + soundIndex = MathUtil.clamp(soundIndex, 0, this.sprite.sounds.length - 1); + + if (newIndex === soundIndex) return false; + + const sound = this.sprite.sounds[soundIndex]; + this.deleteSound(soundIndex); + this.addSound(sound, newIndex); + return true; + } + + /** + * Get full sound list + * @return {object[]} list of sounds + */ + getSounds () { + return this.sprite.sounds; + } + + /** + * Update all drawable properties for this rendered target. + * Use when a batch has changed, e.g., when the drawable is first created. + */ + updateAllDrawableProperties () { + if (this.renderer) { + const {direction, scale} = this._getRenderedDirectionAndScale(); + this.renderer.updateDrawablePosition(this.drawableID, [this.x, this.y]); + this.renderer.updateDrawableDirectionScale(this.drawableID, direction, scale); + this.renderer.updateDrawableVisible(this.drawableID, this.visible); + + const costume = this.getCostumes()[this.currentCostume]; + this.renderer.updateDrawableSkinId(this.drawableID, costume.skinId); + + for (const effectName in this.effects) { + if (!this.effects.hasOwnProperty(effectName)) continue; + this.renderer.updateDrawableEffect(this.drawableID, effectName, this.effects[effectName]); + } + + if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); + this.runtime.requestRedraw(); + } + } + this.runtime.requestTargetsUpdate(this); + } + + /** + * Return the human-readable name for this rendered target, e.g., the sprite's name. + * @override + * @returns {string} Human-readable name. + */ + getName () { + return this.sprite.name; + } + + /** + * Return whether this rendered target is a sprite (not a clone, not the stage). + * @return {boolean} True if not a clone and not the stage. + */ + isSprite () { + return !this.isStage && this.isOriginal; + } + + /** + * Return the rendered target's tight bounding box. + * Includes top, left, bottom, right attributes in Scratch coordinates. + * @return {?object} Tight bounding box, or null. + */ + getBounds () { + if (this.renderer) { + return this.runtime.renderer.getBounds(this.drawableID); + } + return null; + } + + /** + * Return the bounding box around a slice of the top 8px of the rendered target. + * Includes top, left, bottom, right attributes in Scratch coordinates. + * @return {?object} Tight bounding box, or null. + */ + getBoundsForBubble () { + if (this.renderer) { + return this.runtime.renderer.getBoundsForBubble(this.drawableID); + } + return null; + } + + /** + * Return whether this target is touching the mouse, an edge, or a sprite. + * @param {string} requestedObject an id for mouse or edge, or a sprite name. + * @return {boolean} True if the sprite is touching the object. + */ + isTouchingObject (requestedObject) { + if (requestedObject === '_mouse_') { + if (!this.runtime.ioDevices.mouse) return false; + const mouseX = this.runtime.ioDevices.mouse.getClientX(); + const mouseY = this.runtime.ioDevices.mouse.getClientY(); + return this.isTouchingPoint(mouseX, mouseY); + } else if (requestedObject === '_edge_') { + return this.isTouchingEdge(); + } + return this.isTouchingSprite(requestedObject); + } + + /** + * Return whether touching a point. + * @param {number} x X coordinate of test point. + * @param {number} y Y coordinate of test point. + * @return {boolean} True iff the rendered target is touching the point. + */ + isTouchingPoint (x, y) { + if (this.renderer) { + return this.renderer.drawableTouching(this.drawableID, x, y); + } + return false; + } + + /** + * Return whether touching a stage edge. + * @return {boolean} True iff the rendered target is touching the stage edge. + */ + isTouchingEdge () { + if (this.renderer) { + const stageWidth = this.runtime.constructor.STAGE_WIDTH; + const stageHeight = this.runtime.constructor.STAGE_HEIGHT; + const bounds = this.getBounds(); + if (bounds.left < -stageWidth / 2 || + bounds.right > stageWidth / 2 || + bounds.top > stageHeight / 2 || + bounds.bottom < -stageHeight / 2) { + return true; + } + } + return false; + } + + /** + * Return whether touching any of a named sprite's clones. + * @param {string} spriteName Name of the sprite. + * @return {boolean} True iff touching a clone of the sprite. + */ + isTouchingSprite (spriteName) { + spriteName = Cast.toString(spriteName); + const firstClone = this.runtime.getSpriteTargetByName(spriteName); + if (!firstClone || !this.renderer) { + return false; + } + // Filter out dragging targets. This means a sprite that is being dragged + // can detect other sprites using touching , but cannot be detected + // by other sprites while it is being dragged. This matches Scratch 2.0 behavior. + const drawableCandidates = firstClone.sprite.clones.filter(clone => !clone.dragging) + .map(clone => clone.drawableID); + return this.renderer.isTouchingDrawables( + this.drawableID, drawableCandidates); + } + + /** + * Return whether touching a color. + * @param {Array.} rgb [r,g,b], values between 0-255. + * @return {Promise.} True iff the rendered target is touching the color. + */ + isTouchingColor (rgb) { + if (this.renderer) { + return this.renderer.isTouchingColor(this.drawableID, rgb); + } + return false; + } + + /** + * Return whether rendered target's color is touching a color. + * @param {object} targetRgb {Array.} [r,g,b], values between 0-255. + * @param {object} maskRgb {Array.} [r,g,b], values between 0-255. + * @return {Promise.} True iff the color is touching the color. + */ + colorIsTouchingColor (targetRgb, maskRgb) { + if (this.renderer) { + return this.renderer.isTouchingColor( + this.drawableID, + targetRgb, + maskRgb + ); + } + return false; + } + + getLayerOrder () { + if (this.renderer) { + return this.renderer.getDrawableOrder(this.drawableID); + } + return null; + } + + /** + * Move to the front layer. + */ + goToFront () { // This should only ever be used for sprites + if (this.renderer) { + // Let the renderer re-order the sprite based on its knowledge + // of what layers are present + this.renderer.setDrawableOrder(this.drawableID, Infinity, StageLayering.SPRITE_LAYER); + } + + this.runtime.setExecutablePosition(this, Infinity); + } + + /** + * Move to the back layer. + */ + goToBack () { // This should only ever be used for sprites + if (this.renderer) { + // Let the renderer re-order the sprite based on its knowledge + // of what layers are present + this.renderer.setDrawableOrder(this.drawableID, -Infinity, StageLayering.SPRITE_LAYER, false); + } + + this.runtime.setExecutablePosition(this, -Infinity); + } + + /** + * Move forward a number of layers. + * @param {number} nLayers How many layers to go forward. + */ + goForwardLayers (nLayers) { + if (this.renderer) { + this.renderer.setDrawableOrder(this.drawableID, nLayers, StageLayering.SPRITE_LAYER, true); + } + + this.runtime.moveExecutable(this, nLayers); + } + + /** + * Move backward a number of layers. + * @param {number} nLayers How many layers to go backward. + */ + goBackwardLayers (nLayers) { + if (this.renderer) { + this.renderer.setDrawableOrder(this.drawableID, -nLayers, StageLayering.SPRITE_LAYER, true); + } + + this.runtime.moveExecutable(this, -nLayers); + } + + /** + * Move behind some other rendered target. + * @param {!RenderedTarget} other Other rendered target to move behind. + */ + goBehindOther (other) { + if (this.renderer) { + const otherLayer = this.renderer.setDrawableOrder( + other.drawableID, 0, StageLayering.SPRITE_LAYER, true); + this.renderer.setDrawableOrder(this.drawableID, otherLayer, StageLayering.SPRITE_LAYER); + } + + const executionPosition = this.runtime.executableTargets.indexOf(other); + this.runtime.setExecutablePosition(this, executionPosition); + } + + /** + * Keep a desired position within a fence. + * @param {number} newX New desired X position. + * @param {number} newY New desired Y position. + * @param {object=} optFence Optional fence with left, right, top bottom. + * @return {Array.} Fenced X and Y coordinates. + */ + keepInFence (newX, newY, optFence) { + let fence = optFence; + if (!fence) { + fence = { + left: -this.runtime.constructor.STAGE_WIDTH / 2, + right: this.runtime.constructor.STAGE_WIDTH / 2, + top: this.runtime.constructor.STAGE_HEIGHT / 2, + bottom: -this.runtime.constructor.STAGE_HEIGHT / 2 + }; + } + const bounds = this.getBounds(); + if (!bounds) return; + // Adjust the known bounds to the target position. + bounds.left += (newX - this.x); + bounds.right += (newX - this.x); + bounds.top += (newY - this.y); + bounds.bottom += (newY - this.y); + // Find how far we need to move the target position. + let dx = 0; + let dy = 0; + if (bounds.left < fence.left) { + dx += fence.left - bounds.left; + } + if (bounds.right > fence.right) { + dx += fence.right - bounds.right; + } + if (bounds.top > fence.top) { + dy += fence.top - bounds.top; + } + if (bounds.bottom < fence.bottom) { + dy += fence.bottom - bounds.bottom; + } + return [newX + dx, newY + dy]; + } + + /** + * Make a clone, copying any run-time properties. + * If we've hit the global clone limit, returns null. + * @return {RenderedTarget} New clone. + */ + makeClone () { + if (!this.runtime.clonesAvailable() || this.isStage) { + return null; // Hit max clone limit, or this is the stage. + } + this.runtime.changeCloneCounter(1); + const newClone = this.sprite.createClone(); + // Copy all properties. + newClone.x = this.x; + newClone.y = this.y; + newClone.direction = this.direction; + newClone.draggable = this.draggable; + newClone.visible = this.visible; + newClone.size = this.size; + newClone.currentCostume = this.currentCostume; + newClone.rotationStyle = this.rotationStyle; + newClone.effects = Clone.simple(this.effects); + newClone.variables = this.duplicateVariables(); + newClone._edgeActivatedHatValues = Clone.simple(this._edgeActivatedHatValues); + newClone.initDrawable(StageLayering.SPRITE_LAYER); + newClone.updateAllDrawableProperties(); + return newClone; + } + + /** + * Make a duplicate using a duplicate sprite. + * @return {RenderedTarget} New clone. + */ + duplicate () { + return this.sprite.duplicate().then(newSprite => { + const newTarget = newSprite.createClone(); + // Copy all properties. + // @todo refactor with clone methods + newTarget.x = (Math.random() - 0.5) * 400 / 2; + newTarget.y = (Math.random() - 0.5) * 300 / 2; + newTarget.direction = this.direction; + newTarget.draggable = this.draggable; + newTarget.visible = this.visible; + newTarget.size = this.size; + newTarget.currentCostume = this.currentCostume; + newTarget.rotationStyle = this.rotationStyle; + newTarget.effects = JSON.parse(JSON.stringify(this.effects)); + newTarget.variables = this.duplicateVariables(newTarget.blocks); + newTarget.updateAllDrawableProperties(); + return newTarget; + }); + } + + /** + * Called when the project receives a "green flag." + * For a rendered target, this clears graphic effects. + */ + onGreenFlag () { + this.clearEffects(); + } + + /** + * Called when the project receives a "stop all" + * Stop all sounds and clear graphic effects. + */ + onStopAll () { + this.clearEffects(); + } + + /** + * Post/edit sprite info. + * @param {object} data An object with sprite info data to set. + */ + postSpriteInfo (data) { + const force = data.hasOwnProperty('force') ? data.force : null; + const isXChanged = data.hasOwnProperty('x'); + const isYChanged = data.hasOwnProperty('y'); + if (isXChanged || isYChanged) { + this.setXY(isXChanged ? data.x : this.x, isYChanged ? data.y : this.y, force); + } + if (data.hasOwnProperty('direction')) { + this.setDirection(data.direction); + } + if (data.hasOwnProperty('draggable')) { + this.setDraggable(data.draggable); + } + if (data.hasOwnProperty('rotationStyle')) { + this.setRotationStyle(data.rotationStyle); + } + if (data.hasOwnProperty('visible')) { + this.setVisible(data.visible); + } + if (data.hasOwnProperty('size')) { + this.setSize(data.size); + } + } + + /** + * Put the sprite into the drag state. While in effect, setXY must be forced + */ + startDrag () { + this.dragging = true; + } + + /** + * Remove the sprite from the drag state. + */ + stopDrag () { + this.dragging = false; + } + + + /** + * Serialize sprite info, used when emitting events about the sprite + * @returns {object} Sprite data as a simple object + */ + toJSON () { + const costumes = this.getCostumes(); + return { + id: this.id, + name: this.getName(), + isStage: this.isStage, + x: this.x, + y: this.y, + size: this.size, + direction: this.direction, + draggable: this.draggable, + currentCostume: this.currentCostume, + costume: costumes[this.currentCostume], + costumeCount: costumes.length, + visible: this.visible, + rotationStyle: this.rotationStyle, + comments: this.comments, + blocks: this.blocks._blocks, + variables: this.variables, + costumes: costumes, + sounds: this.getSounds(), + textToSpeechLanguage: this.textToSpeechLanguage, + tempo: this.tempo, + volume: this.volume, + videoTransparency: this.videoTransparency, + videoState: this.videoState + + }; + } + + /** + * Dispose, destroying any run-time properties. + */ + dispose () { + this.runtime.changeCloneCounter(-1); + this.runtime.stopForTarget(this); + this.runtime.removeExecutable(this); + this.sprite.removeClone(this); + if (this.renderer && this.drawableID !== null) { + this.renderer.destroyDrawable(this.drawableID, this.isStage ? + StageLayering.BACKGROUND_LAYER : + StageLayering.SPRITE_LAYER); + if (this.visible) { + this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this); + this.runtime.requestRedraw(); + } + } + } +} + +module.exports = RenderedTarget; diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js new file mode 100644 index 0000000000..8094efbf88 --- /dev/null +++ b/packages/scratch-vm/src/virtual-machine.js @@ -0,0 +1,1574 @@ +let _TextEncoder; +if (typeof TextEncoder === 'undefined') { + _TextEncoder = require('text-encoding').TextEncoder; +} else { + /* global TextEncoder */ + _TextEncoder = TextEncoder; +} +const EventEmitter = require('events'); +const JSZip = require('jszip'); + +const Buffer = require('buffer').Buffer; +const centralDispatch = require('./dispatch/central-dispatch'); +const ExtensionManager = require('./extension-support/extension-manager'); +const log = require('./util/log'); +const MathUtil = require('./util/math-util'); +const Runtime = require('./engine/runtime'); +const StringUtil = require('./util/string-util'); +const formatMessage = require('format-message'); + +const Variable = require('./engine/variable'); +const newBlockIds = require('./util/new-block-ids'); + +const {loadCostume} = require('./import/load-costume.js'); +const {loadSound} = require('./import/load-sound.js'); +const {serializeSounds, serializeCostumes} = require('./serialization/serialize-assets'); +require('canvas-toBlob'); + +const RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_']; + +const CORE_EXTENSIONS = [ + // 'motion', + // 'looks', + // 'sound', + // 'events', + // 'control', + // 'sensing', + // 'operators', + // 'variables', + // 'myBlocks' +]; + +/** + * Handles connections between blocks, stage, and extensions. + * @constructor + */ +class VirtualMachine extends EventEmitter { + constructor () { + super(); + + /** + * VM runtime, to store blocks, I/O devices, sprites/targets, etc. + * @type {!Runtime} + */ + this.runtime = new Runtime(); + centralDispatch.setService('runtime', this.runtime).catch(e => { + log.error(`Failed to register runtime service: ${JSON.stringify(e)}`); + }); + + /** + * The "currently editing"/selected target ID for the VM. + * Block events from any Blockly workspace are routed to this target. + * @type {Target} + */ + this.editingTarget = null; + + /** + * The currently dragging target, for redirecting IO data. + * @type {Target} + */ + this._dragTarget = null; + + // Runtime emits are passed along as VM emits. + this.runtime.on(Runtime.SCRIPT_GLOW_ON, glowData => { + this.emit(Runtime.SCRIPT_GLOW_ON, glowData); + }); + this.runtime.on(Runtime.SCRIPT_GLOW_OFF, glowData => { + this.emit(Runtime.SCRIPT_GLOW_OFF, glowData); + }); + this.runtime.on(Runtime.BLOCK_GLOW_ON, glowData => { + this.emit(Runtime.BLOCK_GLOW_ON, glowData); + }); + this.runtime.on(Runtime.BLOCK_GLOW_OFF, glowData => { + this.emit(Runtime.BLOCK_GLOW_OFF, glowData); + }); + this.runtime.on(Runtime.PROJECT_START, () => { + this.emit(Runtime.PROJECT_START); + }); + this.runtime.on(Runtime.PROJECT_RUN_START, () => { + this.emit(Runtime.PROJECT_RUN_START); + }); + this.runtime.on(Runtime.PROJECT_RUN_STOP, () => { + this.emit(Runtime.PROJECT_RUN_STOP); + }); + this.runtime.on(Runtime.PROJECT_CHANGED, () => { + this.emit(Runtime.PROJECT_CHANGED); + }); + this.runtime.on(Runtime.VISUAL_REPORT, visualReport => { + this.emit(Runtime.VISUAL_REPORT, visualReport); + }); + this.runtime.on(Runtime.TARGETS_UPDATE, emitProjectChanged => { + this.emitTargetsUpdate(emitProjectChanged); + }); + this.runtime.on(Runtime.MONITORS_UPDATE, monitorList => { + this.emit(Runtime.MONITORS_UPDATE, monitorList); + }); + this.runtime.on(Runtime.BLOCK_DRAG_UPDATE, areBlocksOverGui => { + this.emit(Runtime.BLOCK_DRAG_UPDATE, areBlocksOverGui); + }); + this.runtime.on(Runtime.BLOCK_DRAG_END, (blocks, topBlockId) => { + this.emit(Runtime.BLOCK_DRAG_END, blocks, topBlockId); + }); + this.runtime.on(Runtime.EXTENSION_ADDED, categoryInfo => { + this.emit(Runtime.EXTENSION_ADDED, categoryInfo); + }); + this.runtime.on(Runtime.EXTENSION_FIELD_ADDED, (fieldName, fieldImplementation) => { + this.emit(Runtime.EXTENSION_FIELD_ADDED, fieldName, fieldImplementation); + }); + this.runtime.on(Runtime.BLOCKSINFO_UPDATE, categoryInfo => { + this.emit(Runtime.BLOCKSINFO_UPDATE, categoryInfo); + }); + this.runtime.on(Runtime.BLOCKS_NEED_UPDATE, () => { + this.emitWorkspaceUpdate(); + }); + this.runtime.on(Runtime.TOOLBOX_EXTENSIONS_NEED_UPDATE, () => { + this.extensionManager.refreshBlocks(); + }); + this.runtime.on(Runtime.PERIPHERAL_LIST_UPDATE, info => { + this.emit(Runtime.PERIPHERAL_LIST_UPDATE, info); + }); + this.runtime.on(Runtime.USER_PICKED_PERIPHERAL, info => { + this.emit(Runtime.USER_PICKED_PERIPHERAL, info); + }); + this.runtime.on(Runtime.PERIPHERAL_CONNECTED, () => + this.emit(Runtime.PERIPHERAL_CONNECTED) + ); + this.runtime.on(Runtime.PERIPHERAL_REQUEST_ERROR, () => + this.emit(Runtime.PERIPHERAL_REQUEST_ERROR) + ); + this.runtime.on(Runtime.PERIPHERAL_DISCONNECTED, () => + this.emit(Runtime.PERIPHERAL_DISCONNECTED) + ); + this.runtime.on(Runtime.PERIPHERAL_CONNECTION_LOST_ERROR, data => + this.emit(Runtime.PERIPHERAL_CONNECTION_LOST_ERROR, data) + ); + this.runtime.on(Runtime.PERIPHERAL_SCAN_TIMEOUT, () => + this.emit(Runtime.PERIPHERAL_SCAN_TIMEOUT) + ); + this.runtime.on(Runtime.MIC_LISTENING, listening => { + this.emit(Runtime.MIC_LISTENING, listening); + }); + this.runtime.on(Runtime.RUNTIME_STARTED, () => { + this.emit(Runtime.RUNTIME_STARTED); + }); + this.runtime.on(Runtime.HAS_CLOUD_DATA_UPDATE, hasCloudData => { + this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, hasCloudData); + }); + + this.extensionManager = new ExtensionManager(this.runtime); + + // Load core extensions + for (const id of CORE_EXTENSIONS) { + this.extensionManager.loadExtensionIdSync(id); + } + + this.blockListener = this.blockListener.bind(this); + this.flyoutBlockListener = this.flyoutBlockListener.bind(this); + this.monitorBlockListener = this.monitorBlockListener.bind(this); + this.variableListener = this.variableListener.bind(this); + } + + /** + * Start running the VM - do this before anything else. + */ + start () { + this.runtime.start(); + } + + /** + * Quit the VM, clearing any handles which might keep the process alive. + * Do not use the runtime after calling this method. This method is meant for test shutdown. + */ + quit () { + this.runtime.quit(); + } + + /** + * "Green flag" handler - start all threads starting with a green flag. + */ + greenFlag () { + this.runtime.greenFlag(); + } + + /** + * Set whether the VM is in "turbo mode." + * When true, loops don't yield to redraw. + * @param {boolean} turboModeOn Whether turbo mode should be set. + */ + setTurboMode (turboModeOn) { + this.runtime.turboMode = !!turboModeOn; + if (this.runtime.turboMode) { + this.emit(Runtime.TURBO_MODE_ON); + } else { + this.emit(Runtime.TURBO_MODE_OFF); + } + } + + /** + * Set whether the VM is in 2.0 "compatibility mode." + * When true, ticks go at 2.0 speed (30 TPS). + * @param {boolean} compatibilityModeOn Whether compatibility mode is set. + */ + setCompatibilityMode (compatibilityModeOn) { + this.runtime.setCompatibilityMode(!!compatibilityModeOn); + } + + /** + * Stop all threads and running activities. + */ + stopAll () { + this.runtime.stopAll(); + } + + /** + * Clear out current running project data. + */ + clear () { + this.runtime.dispose(); + this.editingTarget = null; + this.emitTargetsUpdate(false /* Don't emit project change */); + } + + /** + * Get data for playground. Data comes back in an emitted event. + */ + getPlaygroundData () { + const instance = this; + // Only send back thread data for the current editingTarget. + const threadData = this.runtime.threads.filter(thread => thread.target === instance.editingTarget); + // Remove the target key, since it's a circular reference. + const filteredThreadData = JSON.stringify(threadData, (key, value) => { + if (key === 'target' || key === 'blockContainer') return; + return value; + }, 2); + this.emit('playgroundData', { + blocks: this.editingTarget.blocks, + threads: filteredThreadData + }); + } + + /** + * Post I/O data to the virtual devices. + * @param {?string} device Name of virtual I/O device. + * @param {object} data Any data object to post to the I/O device. + */ + postIOData (device, data) { + if (this.runtime.ioDevices[device]) { + this.runtime.ioDevices[device].postData(data); + } + } + + setVideoProvider (videoProvider) { + this.runtime.ioDevices.video.setProvider(videoProvider); + } + + setCloudProvider (cloudProvider) { + this.runtime.ioDevices.cloud.setProvider(cloudProvider); + } + + /** + * Tell the specified extension to scan for a peripheral. + * @param {string} extensionId - the id of the extension. + */ + scanForPeripheral (extensionId) { + this.runtime.scanForPeripheral(extensionId); + } + + /** + * Connect to the extension's specified peripheral. + * @param {string} extensionId - the id of the extension. + * @param {number} peripheralId - the id of the peripheral. + */ + connectPeripheral (extensionId, peripheralId) { + this.runtime.connectPeripheral(extensionId, peripheralId); + } + + /** + * Disconnect from the extension's connected peripheral. + * @param {string} extensionId - the id of the extension. + */ + disconnectPeripheral (extensionId) { + this.runtime.disconnectPeripheral(extensionId); + } + + /** + * Returns whether the extension has a currently connected peripheral. + * @param {string} extensionId - the id of the extension. + * @return {boolean} - whether the extension has a connected peripheral. + */ + getPeripheralIsConnected (extensionId) { + return this.runtime.getPeripheralIsConnected(extensionId); + } + + /** + * Load a Scratch project from a .sb, .sb2, .sb3 or json string. + * @param {string | object} input A json string, object, or ArrayBuffer representing the project to load. + * @return {!Promise} Promise that resolves after targets are installed. + */ + loadProject (input) { + if (typeof input === 'object' && !(input instanceof ArrayBuffer) && + !ArrayBuffer.isView(input)) { + // If the input is an object and not any ArrayBuffer + // or an ArrayBuffer view (this includes all typed arrays and DataViews) + // turn the object into a JSON string, because we suspect + // this is a project.json as an object + // validate expects a string or buffer as input + // TODO not sure if we need to check that it also isn't a data view + input = JSON.stringify(input); + } + + const validationPromise = new Promise((resolve, reject) => { + const validate = require('scratch-parser'); + // The second argument of false below indicates to the validator that the + // input should be parsed/validated as an entire project (and not a single sprite) + validate(input, false, (error, res) => { + if (error) return reject(error); + resolve(res); + }); + }) + .catch(error => { + const {SB1File, ValidationError} = require('scratch-sb1-converter'); + + try { + const sb1 = new SB1File(input); + const json = sb1.json; + json.projectVersion = 2; + return Promise.resolve([json, sb1.zip]); + } catch (sb1Error) { + if (sb1Error instanceof ValidationError) { + // The input does not validate as a Scratch 1 file. + } else { + // The project appears to be a Scratch 1 file but it + // could not be successfully translated into a Scratch 2 + // project. + return Promise.reject(sb1Error); + } + } + // Throw original error since the input does not appear to be + // an SB1File. + return Promise.reject(error); + }); + + return validationPromise + .then(validatedInput => this.deserializeProject(validatedInput[0], validatedInput[1])) + .then(() => this.runtime.emitProjectLoaded()) + .catch(error => { + // Intentionally rejecting here (want errors to be handled by caller) + if (error.hasOwnProperty('validationError')) { + return Promise.reject(JSON.stringify(error)); + } + return Promise.reject(error); + }); + } + + /** + * Load a project from the Scratch web site, by ID. + * @param {string} id - the ID of the project to download, as a string. + */ + downloadProjectId (id) { + const storage = this.runtime.storage; + if (!storage) { + log.error('No storage module present; cannot load project: ', id); + return; + } + const vm = this; + const promise = storage.load(storage.AssetType.Project, id); + promise.then(projectAsset => { + if (!projectAsset) { + log.error(`Failed to fetch project with id: ${id}`); + return null; + } + return vm.loadProject(projectAsset.data); + }); + } + + /** + * @returns {string} Project in a Scratch 3.0 JSON representation. + */ + saveProjectSb3 () { + const soundDescs = serializeSounds(this.runtime); + const costumeDescs = serializeCostumes(this.runtime); + const projectJson = this.toJSON(); + + // TODO want to eventually move zip creation out of here, and perhaps + // into scratch-storage + const zip = new JSZip(); + + // Put everything in a zip file + zip.file('project.json', projectJson); + this._addFileDescsToZip(soundDescs.concat(costumeDescs), zip); + + return zip.generateAsync({ + type: 'blob', + mimeType: 'application/x.scratch.sb3', + compression: 'DEFLATE', + compressionOptions: { + level: 6 // Tradeoff between best speed (1) and best compression (9) + } + }); + } + + /* + * @type {Array} Array of all costumes and sounds currently in the runtime + */ + get assets () { + return this.runtime.targets.reduce((acc, target) => ( + acc + .concat(target.sprite.sounds.map(sound => sound.asset)) + .concat(target.sprite.costumes.map(costume => costume.asset)) + ), []); + } + + _addFileDescsToZip (fileDescs, zip) { + for (let i = 0; i < fileDescs.length; i++) { + const currFileDesc = fileDescs[i]; + zip.file(currFileDesc.fileName, currFileDesc.fileContent); + } + } + + /** + * Exports a sprite in the sprite3 format. + * @param {string} targetId ID of the target to export + * @param {string=} optZipType Optional type that the resulting + * zip should be outputted in. Options are: base64, binarystring, + * array, uint8array, arraybuffer, blob, or nodebuffer. Defaults to + * blob if argument not provided. + * See https://stuk.github.io/jszip/documentation/api_jszip/generate_async.html#type-option + * for more information about these options. + * @return {object} A generated zip of the sprite and its assets in the format + * specified by optZipType or blob by default. + */ + exportSprite (targetId, optZipType) { + const soundDescs = serializeSounds(this.runtime, targetId); + const costumeDescs = serializeCostumes(this.runtime, targetId); + const spriteJson = this.toJSON(targetId); + + const zip = new JSZip(); + zip.file('sprite.json', spriteJson); + this._addFileDescsToZip(soundDescs.concat(costumeDescs), zip); + + return zip.generateAsync({ + type: typeof optZipType === 'string' ? optZipType : 'blob', + mimeType: 'application/x.scratch.sprite3', + compression: 'DEFLATE', + compressionOptions: { + level: 6 + } + }); + } + + /** + * Export project or sprite as a Scratch 3.0 JSON representation. + * @param {string=} optTargetId - Optional id of a sprite to serialize + * @return {string} Serialized state of the runtime. + */ + toJSON (optTargetId) { + const sb3 = require('./serialization/sb3'); + return StringUtil.stringify(sb3.serialize(this.runtime, optTargetId)); + } + + // TODO do we still need this function? Keeping it here so as not to introduce + // a breaking change. + /** + * Load a project from a Scratch JSON representation. + * @param {string} json JSON string representing a project. + * @returns {Promise} Promise that resolves after the project has loaded + */ + fromJSON (json) { + log.warning('fromJSON is now just a wrapper around loadProject, please use that function instead.'); + return this.loadProject(json); + } + + /** + * Load a project from a Scratch JSON representation. + * @param {string} projectJSON JSON string representing a project. + * @param {?JSZip} zip Optional zipped project containing assets to be loaded. + * @returns {Promise} Promise that resolves after the project has loaded + */ + deserializeProject (projectJSON, zip) { + // Clear the current runtime + this.clear(); + + if (typeof performance !== 'undefined') { + performance.mark('scratch-vm-deserialize-start'); + } + const runtime = this.runtime; + const deserializePromise = function () { + const projectVersion = projectJSON.projectVersion; + if (projectVersion === 2) { + const sb2 = require('./serialization/sb2'); + return sb2.deserialize(projectJSON, runtime, false, zip); + } + if (projectVersion === 3) { + const sb3 = require('./serialization/sb3'); + return sb3.deserialize(projectJSON, runtime, zip); + } + return Promise.reject('Unable to verify Scratch Project version.'); + }; + return deserializePromise() + .then(({targets, extensions}) => { + if (typeof performance !== 'undefined') { + performance.mark('scratch-vm-deserialize-end'); + performance.measure('scratch-vm-deserialize', + 'scratch-vm-deserialize-start', 'scratch-vm-deserialize-end'); + } + return this.installTargets(targets, extensions, true); + }); + } + + /** + * Install `deserialize` results: zero or more targets after the extensions (if any) used by those targets. + * @param {Array.} targets - the targets to be installed + * @param {ImportedExtensionsInfo} extensions - metadata about extensions used by these targets + * @param {boolean} wholeProject - set to true if installing a whole project, as opposed to a single sprite. + * @returns {Promise} resolved once targets have been installed + */ + installTargets (targets, extensions, wholeProject) { + const extensionPromises = []; + + extensions.extensionIDs.forEach(extensionID => { + if (!this.extensionManager.isExtensionLoaded(extensionID)) { + const extensionURL = extensions.extensionURLs.get(extensionID) || extensionID; + extensionPromises.push(this.extensionManager.loadExtensionURL(extensionURL)); + } + }); + + targets = targets.filter(target => !!target); + + return Promise.all(extensionPromises).then(() => { + targets.forEach(target => { + this.runtime.addTarget(target); + (/** @type RenderedTarget */ target).updateAllDrawableProperties(); + // Ensure unique sprite name + if (target.isSprite()) this.renameSprite(target.id, target.getName()); + }); + // Sort the executable targets by layerOrder. + // Remove layerOrder property after use. + this.runtime.executableTargets.sort((a, b) => a.layerOrder - b.layerOrder); + targets.forEach(target => { + delete target.layerOrder; + }); + + // Select the first target for editing, e.g., the first sprite. + if (wholeProject && (targets.length > 1)) { + this.editingTarget = targets[1]; + } else { + this.editingTarget = targets[0]; + } + + if (!wholeProject) { + this.editingTarget.fixUpVariableReferences(); + } + + // Update the VM user's knowledge of targets and blocks on the workspace. + this.emitTargetsUpdate(false /* Don't emit project change */); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); + this.runtime.ioDevices.cloud.setStage(this.runtime.getTargetForStage()); + }); + } + + /** + * Add a sprite, this could be .sprite2 or .sprite3. Unpack and validate + * such a file first. + * @param {string | object} input A json string, object, or ArrayBuffer representing the project to load. + * @return {!Promise} Promise that resolves after targets are installed. + */ + addSprite (input) { + const errorPrefix = 'Sprite Upload Error:'; + if (typeof input === 'object' && !(input instanceof ArrayBuffer) && + !ArrayBuffer.isView(input)) { + // If the input is an object and not any ArrayBuffer + // or an ArrayBuffer view (this includes all typed arrays and DataViews) + // turn the object into a JSON string, because we suspect + // this is a project.json as an object + // validate expects a string or buffer as input + // TODO not sure if we need to check that it also isn't a data view + input = JSON.stringify(input); + } + + const validationPromise = new Promise((resolve, reject) => { + const validate = require('scratch-parser'); + // The second argument of true below indicates to the parser/validator + // that the given input should be treated as a single sprite and not + // an entire project + validate(input, true, (error, res) => { + if (error) return reject(error); + resolve(res); + }); + }); + + return validationPromise + .then(validatedInput => { + const projectVersion = validatedInput[0].projectVersion; + if (projectVersion === 2) { + return this._addSprite2(validatedInput[0], validatedInput[1]); + } + if (projectVersion === 3) { + return this._addSprite3(validatedInput[0], validatedInput[1]); + } + return Promise.reject(`${errorPrefix} Unable to verify sprite version.`); + }) + .then(() => this.runtime.emitProjectChanged()) + .catch(error => { + // Intentionally rejecting here (want errors to be handled by caller) + if (error.hasOwnProperty('validationError')) { + return Promise.reject(JSON.stringify(error)); + } + return Promise.reject(`${errorPrefix} ${error}`); + }); + } + + /** + * Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format. + * @param {object} sprite Object representing 2.0 sprite to be added. + * @param {?ArrayBuffer} zip Optional zip of assets being referenced by json + * @returns {Promise} Promise that resolves after the sprite is added + */ + _addSprite2 (sprite, zip) { + // Validate & parse + + const sb2 = require('./serialization/sb2'); + return sb2.deserialize(sprite, this.runtime, true, zip) + .then(({targets, extensions}) => + this.installTargets(targets, extensions, false)); + } + + /** + * Add a single sb3 sprite. + * @param {object} sprite Object rperesenting 3.0 sprite to be added. + * @param {?ArrayBuffer} zip Optional zip of assets being referenced by target json + * @returns {Promise} Promise that resolves after the sprite is added + */ + _addSprite3 (sprite, zip) { + // Validate & parse + const sb3 = require('./serialization/sb3'); + return sb3 + .deserialize(sprite, this.runtime, zip, true) + .then(({targets, extensions}) => this.installTargets(targets, extensions, false)); + } + + /** + * Add a costume to the current editing target. + * @param {string} md5ext - the MD5 and extension of the costume to be loaded. + * @param {!object} costumeObject Object representing the costume. + * @property {int} skinId - the ID of the costume's render skin, once installed. + * @property {number} rotationCenterX - the X component of the costume's origin. + * @property {number} rotationCenterY - the Y component of the costume's origin. + * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume. + * @param {string} optTargetId - the id of the target to add to, if not the editing target. + * @param {string} optVersion - if this is 2, load costume as sb2, otherwise load costume as sb3. + * @returns {?Promise} - a promise that resolves when the costume has been added + */ + addCostume (md5ext, costumeObject, optTargetId, optVersion) { + const target = optTargetId ? this.runtime.getTargetById(optTargetId) : + this.editingTarget; + if (target) { + return loadCostume(md5ext, costumeObject, this.runtime, optVersion).then(() => { + target.addCostume(costumeObject); + target.setCostume( + target.getCostumes().length - 1 + ); + this.runtime.emitProjectChanged(); + }); + } + // If the target cannot be found by id, return a rejected promise + return Promise.reject(); + } + + /** + * Add a costume loaded from the library to the current editing target. + * @param {string} md5ext - the MD5 and extension of the costume to be loaded. + * @param {!object} costumeObject Object representing the costume. + * @property {int} skinId - the ID of the costume's render skin, once installed. + * @property {number} rotationCenterX - the X component of the costume's origin. + * @property {number} rotationCenterY - the Y component of the costume's origin. + * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume. + * @returns {?Promise} - a promise that resolves when the costume has been added + */ + addCostumeFromLibrary (md5ext, costumeObject) { + if (!this.editingTarget) return Promise.reject(); + return this.addCostume(md5ext, costumeObject, this.editingTarget.id, 2 /* optVersion */); + } + + /** + * Duplicate the costume at the given index. Add it at that index + 1. + * @param {!int} costumeIndex Index of costume to duplicate + * @returns {?Promise} - a promise that resolves when the costume has been decoded and added + */ + duplicateCostume (costumeIndex) { + const originalCostume = this.editingTarget.getCostumes()[costumeIndex]; + const clone = Object.assign({}, originalCostume); + const md5ext = `${clone.assetId}.${clone.dataFormat}`; + return loadCostume(md5ext, clone, this.runtime).then(() => { + this.editingTarget.addCostume(clone, costumeIndex + 1); + this.editingTarget.setCostume(costumeIndex + 1); + this.emitTargetsUpdate(); + }); + } + + /** + * Duplicate the sound at the given index. Add it at that index + 1. + * @param {!int} soundIndex Index of sound to duplicate + * @returns {?Promise} - a promise that resolves when the sound has been decoded and added + */ + duplicateSound (soundIndex) { + const originalSound = this.editingTarget.getSounds()[soundIndex]; + const clone = Object.assign({}, originalSound); + return loadSound(clone, this.runtime, this.editingTarget.sprite.soundBank).then(() => { + this.editingTarget.addSound(clone, soundIndex + 1); + this.emitTargetsUpdate(); + }); + } + + /** + * Rename a costume on the current editing target. + * @param {int} costumeIndex - the index of the costume to be renamed. + * @param {string} newName - the desired new name of the costume (will be modified if already in use). + */ + renameCostume (costumeIndex, newName) { + this.editingTarget.renameCostume(costumeIndex, newName); + this.emitTargetsUpdate(); + } + + /** + * Delete a costume from the current editing target. + * @param {int} costumeIndex - the index of the costume to be removed. + * @return {?function} A function to restore the deleted costume, or null, + * if no costume was deleted. + */ + deleteCostume (costumeIndex) { + const deletedCostume = this.editingTarget.deleteCostume(costumeIndex); + if (deletedCostume) { + const target = this.editingTarget; + this.runtime.emitProjectChanged(); + return () => { + target.addCostume(deletedCostume); + this.emitTargetsUpdate(); + }; + } + return null; + } + + /** + * Add a sound to the current editing target. + * @param {!object} soundObject Object representing the costume. + * @param {string} optTargetId - the id of the target to add to, if not the editing target. + * @returns {?Promise} - a promise that resolves when the sound has been decoded and added + */ + addSound (soundObject, optTargetId) { + const target = optTargetId ? this.runtime.getTargetById(optTargetId) : + this.editingTarget; + if (target) { + return loadSound(soundObject, this.runtime, target.sprite.soundBank).then(() => { + target.addSound(soundObject); + this.emitTargetsUpdate(); + }); + } + // If the target cannot be found by id, return a rejected promise + return new Promise.reject(); + } + + /** + * Rename a sound on the current editing target. + * @param {int} soundIndex - the index of the sound to be renamed. + * @param {string} newName - the desired new name of the sound (will be modified if already in use). + */ + renameSound (soundIndex, newName) { + this.editingTarget.renameSound(soundIndex, newName); + this.emitTargetsUpdate(); + } + + /** + * Get a sound buffer from the audio engine. + * @param {int} soundIndex - the index of the sound to be got. + * @return {AudioBuffer} the sound's audio buffer. + */ + getSoundBuffer (soundIndex) { + const id = this.editingTarget.sprite.sounds[soundIndex].soundId; + if (id && this.runtime && this.runtime.audioEngine) { + return this.editingTarget.sprite.soundBank.getSoundPlayer(id).buffer; + } + return null; + } + + /** + * Update a sound buffer. + * @param {int} soundIndex - the index of the sound to be updated. + * @param {AudioBuffer} newBuffer - new audio buffer for the audio engine. + * @param {ArrayBuffer} soundEncoding - the new (wav) encoded sound to be stored + */ + updateSoundBuffer (soundIndex, newBuffer, soundEncoding) { + const sound = this.editingTarget.sprite.sounds[soundIndex]; + if (sound && sound.broken) delete sound.broken; + const id = sound ? sound.soundId : null; + if (id && this.runtime && this.runtime.audioEngine) { + this.editingTarget.sprite.soundBank.getSoundPlayer(id).buffer = newBuffer; + } + // Update sound in runtime + if (soundEncoding) { + // Now that we updated the sound, the format should also be updated + // so that the sound can eventually be decoded the right way. + // Sounds that were formerly 'adpcm', but were updated in sound editor + // will not get decoded by the audio engine correctly unless the format + // is updated as below. + sound.format = ''; + const storage = this.runtime.storage; + sound.asset = storage.createAsset( + storage.AssetType.Sound, + storage.DataFormat.WAV, + soundEncoding, + null, + true // generate md5 + ); + sound.assetId = sound.asset.assetId; + sound.dataFormat = storage.DataFormat.WAV; + sound.md5 = `${sound.assetId}.${sound.dataFormat}`; + sound.sampleCount = newBuffer.length; + sound.rate = newBuffer.sampleRate; + } + // If soundEncoding is null, it's because gui had a problem + // encoding the updated sound. We don't want to store anything in this + // case, and gui should have logged an error. + + this.emitTargetsUpdate(); + } + + /** + * Delete a sound from the current editing target. + * @param {int} soundIndex - the index of the sound to be removed. + * @return {?Function} A function to restore the sound that was deleted, + * or null, if no sound was deleted. + */ + deleteSound (soundIndex) { + const target = this.editingTarget; + const deletedSound = this.editingTarget.deleteSound(soundIndex); + if (deletedSound) { + this.runtime.emitProjectChanged(); + const restoreFun = () => { + target.addSound(deletedSound); + this.emitTargetsUpdate(); + }; + return restoreFun; + } + return null; + } + + /** + * Get a string representation of the image from storage. + * @param {int} costumeIndex - the index of the costume to be got. + * @return {string} the costume's SVG string if it's SVG, + * a dataURI if it's a PNG or JPG, or null if it couldn't be found or decoded. + */ + getCostume (costumeIndex) { + const asset = this.editingTarget.getCostumes()[costumeIndex].asset; + if (!asset || !this.runtime || !this.runtime.storage) return null; + const format = asset.dataFormat; + if (format === this.runtime.storage.DataFormat.SVG) { + return asset.decodeText(); + } else if (format === this.runtime.storage.DataFormat.PNG || + format === this.runtime.storage.DataFormat.JPG) { + return asset.encodeDataURI(); + } + log.error(`Unhandled format: ${asset.dataFormat}`); + return null; + } + + /** + * Update a costume with the given bitmap + * @param {!int} costumeIndex - the index of the costume to be updated. + * @param {!ImageData} bitmap - new bitmap for the renderer. + * @param {!number} rotationCenterX x of point about which the costume rotates, relative to its upper left corner + * @param {!number} rotationCenterY y of point about which the costume rotates, relative to its upper left corner + * @param {!number} bitmapResolution 1 for bitmaps that have 1 pixel per unit of stage, + * 2 for double-resolution bitmaps + */ + updateBitmap (costumeIndex, bitmap, rotationCenterX, rotationCenterY, bitmapResolution) { + const costume = this.editingTarget.getCostumes()[costumeIndex]; + if (!(costume && this.runtime && this.runtime.renderer)) return; + if (costume && costume.broken) delete costume.broken; + + costume.rotationCenterX = rotationCenterX; + costume.rotationCenterY = rotationCenterY; + + // If the bitmap originally had a zero width or height, use that value + const bitmapWidth = bitmap.sourceWidth === 0 ? 0 : bitmap.width; + const bitmapHeight = bitmap.sourceHeight === 0 ? 0 : bitmap.height; + // @todo: updateBitmapSkin does not take ImageData + const canvas = document.createElement('canvas'); + canvas.width = bitmapWidth; + canvas.height = bitmapHeight; + const context = canvas.getContext('2d'); + context.putImageData(bitmap, 0, 0); + + // Divide by resolution because the renderer's definition of the rotation center + // is the rotation center divided by the bitmap resolution + this.runtime.renderer.updateBitmapSkin( + costume.skinId, + canvas, + bitmapResolution, + [rotationCenterX / bitmapResolution, rotationCenterY / bitmapResolution] + ); + + // @todo there should be a better way to get from ImageData to a decodable storage format + canvas.toBlob(blob => { + const reader = new FileReader(); + reader.addEventListener('loadend', () => { + const storage = this.runtime.storage; + costume.dataFormat = storage.DataFormat.PNG; + costume.bitmapResolution = bitmapResolution; + costume.size = [bitmapWidth, bitmapHeight]; + costume.asset = storage.createAsset( + storage.AssetType.ImageBitmap, + costume.dataFormat, + Buffer.from(reader.result), + null, // id + true // generate md5 + ); + costume.assetId = costume.asset.assetId; + costume.md5 = `${costume.assetId}.${costume.dataFormat}`; + this.emitTargetsUpdate(); + }); + // Bitmaps with a zero width or height return null for their blob + if (blob){ + reader.readAsArrayBuffer(blob); + } + }); + } + + /** + * Update a costume with the given SVG + * @param {int} costumeIndex - the index of the costume to be updated. + * @param {string} svg - new SVG for the renderer. + * @param {number} rotationCenterX x of point about which the costume rotates, relative to its upper left corner + * @param {number} rotationCenterY y of point about which the costume rotates, relative to its upper left corner + */ + updateSvg (costumeIndex, svg, rotationCenterX, rotationCenterY) { + const costume = this.editingTarget.getCostumes()[costumeIndex]; + if (costume && costume.broken) delete costume.broken; + if (costume && this.runtime && this.runtime.renderer) { + costume.rotationCenterX = rotationCenterX; + costume.rotationCenterY = rotationCenterY; + this.runtime.renderer.updateSVGSkin(costume.skinId, svg, [rotationCenterX, rotationCenterY]); + costume.size = this.runtime.renderer.getSkinSize(costume.skinId); + } + const storage = this.runtime.storage; + // If we're in here, we've edited an svg in the vector editor, + // so the dataFormat should be 'svg' + costume.dataFormat = storage.DataFormat.SVG; + costume.bitmapResolution = 1; + costume.asset = storage.createAsset( + storage.AssetType.ImageVector, + costume.dataFormat, + (new _TextEncoder()).encode(svg), + null, + true // generate md5 + ); + costume.assetId = costume.asset.assetId; + costume.md5 = `${costume.assetId}.${costume.dataFormat}`; + this.emitTargetsUpdate(); + } + + /** + * Add a backdrop to the stage. + * @param {string} md5ext - the MD5 and extension of the backdrop to be loaded. + * @param {!object} backdropObject Object representing the backdrop. + * @property {int} skinId - the ID of the backdrop's render skin, once installed. + * @property {number} rotationCenterX - the X component of the backdrop's origin. + * @property {number} rotationCenterY - the Y component of the backdrop's origin. + * @property {number} [bitmapResolution] - the resolution scale for a bitmap backdrop. + * @returns {?Promise} - a promise that resolves when the backdrop has been added + */ + addBackdrop (md5ext, backdropObject) { + return loadCostume(md5ext, backdropObject, this.runtime).then(() => { + const stage = this.runtime.getTargetForStage(); + stage.addCostume(backdropObject); + stage.setCostume(stage.getCostumes().length - 1); + this.runtime.emitProjectChanged(); + }); + } + + /** + * Rename a sprite. + * @param {string} targetId ID of a target whose sprite to rename. + * @param {string} newName New name of the sprite. + */ + renameSprite (targetId, newName) { + const target = this.runtime.getTargetById(targetId); + if (target) { + if (!target.isSprite()) { + throw new Error('Cannot rename non-sprite targets.'); + } + const sprite = target.sprite; + if (!sprite) { + throw new Error('No sprite associated with this target.'); + } + if (newName && RESERVED_NAMES.indexOf(newName) === -1) { + const names = this.runtime.targets + .filter(runtimeTarget => runtimeTarget.isSprite() && runtimeTarget.id !== target.id) + .map(runtimeTarget => runtimeTarget.sprite.name); + const oldName = sprite.name; + const newUnusedName = StringUtil.unusedName(newName, names); + sprite.name = newUnusedName; + const allTargets = this.runtime.targets; + for (let i = 0; i < allTargets.length; i++) { + const currTarget = allTargets[i]; + currTarget.blocks.updateAssetName(oldName, newName, 'sprite'); + } + + if (newUnusedName !== oldName) this.emitTargetsUpdate(); + } + } else { + throw new Error('No target with the provided id.'); + } + } + + /** + * Delete a sprite and all its clones. + * @param {string} targetId ID of a target whose sprite to delete. + * @return {Function} Returns a function to restore the sprite that was deleted + */ + deleteSprite (targetId) { + const target = this.runtime.getTargetById(targetId); + + if (target) { + const targetIndexBeforeDelete = this.runtime.targets.map(t => t.id).indexOf(target.id); + if (!target.isSprite()) { + throw new Error('Cannot delete non-sprite targets.'); + } + const sprite = target.sprite; + if (!sprite) { + throw new Error('No sprite associated with this target.'); + } + const spritePromise = this.exportSprite(targetId, 'uint8array'); + const restoreSprite = () => spritePromise.then(spriteBuffer => this.addSprite(spriteBuffer)); + // Remove monitors from the runtime state and remove the + // target-specific monitored blocks (e.g. local variables) + target.deleteMonitors(); + const currentEditingTarget = this.editingTarget; + for (let i = 0; i < sprite.clones.length; i++) { + const clone = sprite.clones[i]; + this.runtime.stopForTarget(sprite.clones[i]); + this.runtime.disposeTarget(sprite.clones[i]); + // Ensure editing target is switched if we are deleting it. + if (clone === currentEditingTarget) { + const nextTargetIndex = Math.min(this.runtime.targets.length - 1, targetIndexBeforeDelete); + if (this.runtime.targets.length > 0){ + this.setEditingTarget(this.runtime.targets[nextTargetIndex].id); + } else { + this.editingTarget = null; + } + } + } + // Sprite object should be deleted by GC. + this.emitTargetsUpdate(); + return restoreSprite; + } + + throw new Error('No target with the provided id.'); + } + + /** + * Duplicate a sprite. + * @param {string} targetId ID of a target whose sprite to duplicate. + * @returns {Promise} Promise that resolves when duplicated target has + * been added to the runtime. + */ + duplicateSprite (targetId) { + const target = this.runtime.getTargetById(targetId); + if (!target) { + throw new Error('No target with the provided id.'); + } else if (!target.isSprite()) { + throw new Error('Cannot duplicate non-sprite targets.'); + } else if (!target.sprite) { + throw new Error('No sprite associated with this target.'); + } + return target.duplicate().then(newTarget => { + this.runtime.addTarget(newTarget); + newTarget.goBehindOther(target); + this.setEditingTarget(newTarget.id); + }); + } + + /** + * Set the audio engine for the VM/runtime + * @param {!AudioEngine} audioEngine The audio engine to attach + */ + attachAudioEngine (audioEngine) { + this.runtime.attachAudioEngine(audioEngine); + } + + /** + * Set the renderer for the VM/runtime + * @param {!RenderWebGL} renderer The renderer to attach + */ + attachRenderer (renderer) { + this.runtime.attachRenderer(renderer); + } + + /** + * @returns {RenderWebGL} The renderer attached to the vm + */ + get renderer () { + return this.runtime && this.runtime.renderer; + } + + // @deprecated + attachV2SVGAdapter () { + } + + /** + * Set the bitmap adapter for the VM/runtime, which converts scratch 2 + * bitmaps to scratch 3 bitmaps. (Scratch 3 bitmaps are all bitmap resolution 2) + * @param {!function} bitmapAdapter The adapter to attach + */ + attachV2BitmapAdapter (bitmapAdapter) { + this.runtime.attachV2BitmapAdapter(bitmapAdapter); + } + + /** + * Set the storage module for the VM/runtime + * @param {!ScratchStorage} storage The storage module to attach + */ + attachStorage (storage) { + this.runtime.attachStorage(storage); + } + + /** + * set the current locale and builtin messages for the VM + * @param {!string} locale current locale + * @param {!object} messages builtin messages map for current locale + * @returns {Promise} Promise that resolves when all the blocks have been + * updated for a new locale (or empty if locale hasn't changed.) + */ + setLocale (locale, messages) { + if (locale !== formatMessage.setup().locale) { + formatMessage.setup({locale: locale, translations: {[locale]: messages}}); + } + return this.extensionManager.refreshBlocks(); + } + + /** + * get the current locale for the VM + * @returns {string} the current locale in the VM + */ + getLocale () { + return formatMessage.setup().locale; + } + + /** + * Handle a Blockly event for the current editing target. + * @param {!Blockly.Event} e Any Blockly event. + */ + blockListener (e) { + if (this.editingTarget) { + this.editingTarget.blocks.blocklyListen(e); + } + } + + /** + * Handle a Blockly event for the flyout. + * @param {!Blockly.Event} e Any Blockly event. + */ + flyoutBlockListener (e) { + this.runtime.flyoutBlocks.blocklyListen(e); + } + + /** + * Handle a Blockly event for the flyout to be passed to the monitor container. + * @param {!Blockly.Event} e Any Blockly event. + */ + monitorBlockListener (e) { + // Filter events by type, since monitor blocks only need to listen to these events. + // Monitor blocks shouldn't be destroyed when flyout blocks are deleted. + if (['create', 'change'].indexOf(e.type) !== -1) { + this.runtime.monitorBlocks.blocklyListen(e); + } + } + + /** + * Handle a Blockly event for the variable map. + * @param {!Blockly.Event} e Any Blockly event. + */ + variableListener (e) { + // Filter events by type, since blocks only needs to listen to these + // var events. + if (['var_create', 'var_rename', 'var_delete'].indexOf(e.type) !== -1) { + this.runtime.getTargetForStage().blocks.blocklyListen(e); + } + } + + /** + * Delete all of the flyout blocks. + */ + clearFlyoutBlocks () { + this.runtime.flyoutBlocks.deleteAllBlocks(); + } + + /** + * Set an editing target. An editor UI can use this function to switch + * between editing different targets, sprites, etc. + * After switching the editing target, the VM may emit updates + * to the list of targets and any attached workspace blocks + * (see `emitTargetsUpdate` and `emitWorkspaceUpdate`). + * @param {string} targetId Id of target to set as editing. + */ + setEditingTarget (targetId) { + // Has the target id changed? If not, exit. + if (this.editingTarget && targetId === this.editingTarget.id) { + return; + } + const target = this.runtime.getTargetById(targetId); + if (target) { + this.editingTarget = target; + // Emit appropriate UI updates. + this.emitTargetsUpdate(false /* Don't emit project change */); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(target); + } + } + + /** + * Called when blocks are dragged from one sprite to another. Adds the blocks to the + * workspace of the given target. + * @param {!Array} blocks Blocks to add. + * @param {!string} targetId Id of target to add blocks to. + * @param {?string} optFromTargetId Optional target id indicating that blocks are being + * shared from that target. This is needed for resolving any potential variable conflicts. + * @return {!Promise} Promise that resolves when the extensions and blocks have been added. + */ + shareBlocksToTarget (blocks, targetId, optFromTargetId) { + const sb3 = require('./serialization/sb3'); + + const copiedBlocks = JSON.parse(JSON.stringify(blocks)); + newBlockIds(copiedBlocks); + const target = this.runtime.getTargetById(targetId); + + if (optFromTargetId) { + // If the blocks are being shared from another target, + // resolve any possible variable conflicts that may arise. + const fromTarget = this.runtime.getTargetById(optFromTargetId); + fromTarget.resolveVariableSharingConflictsWithTarget(copiedBlocks, target); + } + + // Create a unique set of extensionIds that are not yet loaded + const extensionIDs = new Set(copiedBlocks + .map(b => sb3.getExtensionIdForOpcode(b.opcode)) + .filter(id => !!id) // Remove ids that do not exist + .filter(id => !this.extensionManager.isExtensionLoaded(id)) // and remove loaded extensions + ); + + // Create an array promises for extensions to load + const extensionPromises = Array.from(extensionIDs, + id => this.extensionManager.loadExtensionURL(id) + ); + + return Promise.all(extensionPromises).then(() => { + copiedBlocks.forEach(block => { + target.blocks.createBlock(block); + }); + target.blocks.updateTargetSpecificBlocks(target.isStage); + }); + } + + /** + * Called when costumes are dragged from editing target to another target. + * Sets the newly added costume as the current costume. + * @param {!number} costumeIndex Index of the costume of the editing target to share. + * @param {!string} targetId Id of target to add the costume. + * @return {Promise} Promise that resolves when the new costume has been loaded. + */ + shareCostumeToTarget (costumeIndex, targetId) { + const originalCostume = this.editingTarget.getCostumes()[costumeIndex]; + const clone = Object.assign({}, originalCostume); + const md5ext = `${clone.assetId}.${clone.dataFormat}`; + return loadCostume(md5ext, clone, this.runtime).then(() => { + const target = this.runtime.getTargetById(targetId); + if (target) { + target.addCostume(clone); + target.setCostume( + target.getCostumes().length - 1 + ); + } + }); + } + + /** + * Called when sounds are dragged from editing target to another target. + * @param {!number} soundIndex Index of the sound of the editing target to share. + * @param {!string} targetId Id of target to add the sound. + * @return {Promise} Promise that resolves when the new sound has been loaded. + */ + shareSoundToTarget (soundIndex, targetId) { + const originalSound = this.editingTarget.getSounds()[soundIndex]; + const clone = Object.assign({}, originalSound); + const target = this.runtime.getTargetById(targetId); + return loadSound(clone, this.runtime, target.sprite.soundBank).then(() => { + if (target) { + target.addSound(clone); + this.emitTargetsUpdate(); + } + }); + } + + /** + * Repopulate the workspace with the blocks of the current editingTarget. This + * allows us to get around bugs like gui#413. + */ + refreshWorkspace () { + if (this.editingTarget) { + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); + this.emitTargetsUpdate(false /* Don't emit project change */); + } + } + + /** + * Emit metadata about available targets. + * An editor UI could use this to display a list of targets and show + * the currently editing one. + * @param {bool} triggerProjectChange If true, also emit a project changed event. + * Disabled selectively by updates that don't affect project serialization. + * Defaults to true. + */ + emitTargetsUpdate (triggerProjectChange) { + if (typeof triggerProjectChange === 'undefined') triggerProjectChange = true; + this.emit('targetsUpdate', { + // [[target id, human readable target name], ...]. + targetList: this.runtime.targets + .filter( + // Don't report clones. + target => !target.hasOwnProperty('isOriginal') || target.isOriginal + ).map( + target => target.toJSON() + ), + // Currently editing target id. + editingTarget: this.editingTarget ? this.editingTarget.id : null + }); + if (triggerProjectChange) { + this.runtime.emitProjectChanged(); + } + } + + /** + * Emit an Blockly/scratch-blocks compatible XML representation + * of the current editing target's blocks. + */ + emitWorkspaceUpdate () { + // Create a list of broadcast message Ids according to the stage variables + const stageVariables = this.runtime.getTargetForStage().variables; + let messageIds = []; + for (const varId in stageVariables) { + if (stageVariables[varId].type === Variable.BROADCAST_MESSAGE_TYPE) { + messageIds.push(varId); + } + } + // Go through all blocks on all targets, removing referenced + // broadcast ids from the list. + for (let i = 0; i < this.runtime.targets.length; i++) { + const currTarget = this.runtime.targets[i]; + const currBlocks = currTarget.blocks._blocks; + for (const blockId in currBlocks) { + if (currBlocks[blockId].fields.BROADCAST_OPTION) { + const id = currBlocks[blockId].fields.BROADCAST_OPTION.id; + const index = messageIds.indexOf(id); + if (index !== -1) { + messageIds = messageIds.slice(0, index) + .concat(messageIds.slice(index + 1)); + } + } + } + } + // Anything left in messageIds is not referenced by a block, so delete it. + for (let i = 0; i < messageIds.length; i++) { + const id = messageIds[i]; + delete this.runtime.getTargetForStage().variables[id]; + } + const globalVarMap = Object.assign({}, this.runtime.getTargetForStage().variables); + const localVarMap = this.editingTarget.isStage ? + Object.create(null) : + Object.assign({}, this.editingTarget.variables); + + const globalVariables = Object.keys(globalVarMap).map(k => globalVarMap[k]); + const localVariables = Object.keys(localVarMap).map(k => localVarMap[k]); + const workspaceComments = Object.keys(this.editingTarget.comments) + .map(k => this.editingTarget.comments[k]) + .filter(c => c.blockId === null); + + const xmlString = ` + + ${globalVariables.map(v => v.toXML()).join()} + ${localVariables.map(v => v.toXML(true)).join()} + + ${workspaceComments.map(c => c.toXML()).join()} + ${this.editingTarget.blocks.toXML(this.editingTarget.comments)} + `; + + this.emit('workspaceUpdate', {xml: xmlString}); + } + + /** + * Get a target id for a drawable id. Useful for interacting with the renderer + * @param {int} drawableId The drawable id to request the target id for + * @returns {?string} The target id, if found. Will also be null if the target found is the stage. + */ + getTargetIdForDrawableId (drawableId) { + const target = this.runtime.getTargetByDrawableId(drawableId); + if (target && target.hasOwnProperty('id') && target.hasOwnProperty('isStage') && !target.isStage) { + return target.id; + } + return null; + } + + /** + * Reorder target by index. Return whether a change was made. + * @param {!string} targetIndex Index of the target. + * @param {!number} newIndex index that the target should be moved to. + * @returns {boolean} Whether a target was reordered. + */ + reorderTarget (targetIndex, newIndex) { + let targets = this.runtime.targets; + targetIndex = MathUtil.clamp(targetIndex, 0, targets.length - 1); + newIndex = MathUtil.clamp(newIndex, 0, targets.length - 1); + if (targetIndex === newIndex) return false; + const target = targets[targetIndex]; + targets = targets.slice(0, targetIndex).concat(targets.slice(targetIndex + 1)); + targets.splice(newIndex, 0, target); + this.runtime.targets = targets; + this.emitTargetsUpdate(); + return true; + } + + /** + * Reorder the costumes of a target if it exists. Return whether it succeeded. + * @param {!string} targetId ID of the target which owns the costumes. + * @param {!number} costumeIndex index of the costume to move. + * @param {!number} newIndex index that the costume should be moved to. + * @returns {boolean} Whether a costume was reordered. + */ + reorderCostume (targetId, costumeIndex, newIndex) { + const target = this.runtime.getTargetById(targetId); + if (target) { + const reorderSuccessful = target.reorderCostume(costumeIndex, newIndex); + if (reorderSuccessful) { + this.runtime.emitProjectChanged(); + } + return reorderSuccessful; + } + return false; + } + + /** + * Reorder the sounds of a target if it exists. Return whether it occured. + * @param {!string} targetId ID of the target which owns the sounds. + * @param {!number} soundIndex index of the sound to move. + * @param {!number} newIndex index that the sound should be moved to. + * @returns {boolean} Whether a sound was reordered. + */ + reorderSound (targetId, soundIndex, newIndex) { + const target = this.runtime.getTargetById(targetId); + if (target) { + const reorderSuccessful = target.reorderSound(soundIndex, newIndex); + if (reorderSuccessful) { + this.runtime.emitProjectChanged(); + } + return reorderSuccessful; + } + return false; + } + + /** + * Put a target into a "drag" state, during which its X/Y positions will be unaffected + * by blocks. + * @param {string} targetId The id for the target to put into a drag state + */ + startDrag (targetId) { + const target = this.runtime.getTargetById(targetId); + if (target) { + this._dragTarget = target; + target.startDrag(); + } + } + + /** + * Remove a target from a drag state, so blocks may begin affecting X/Y position again + * @param {string} targetId The id for the target to remove from the drag state + */ + stopDrag (targetId) { + const target = this.runtime.getTargetById(targetId); + if (target) { + this._dragTarget = null; + target.stopDrag(); + this.setEditingTarget(target.sprite && target.sprite.clones[0] ? + target.sprite.clones[0].id : target.id); + } + } + + /** + * Post/edit sprite info for the current editing target or the drag target. + * @param {object} data An object with sprite info data to set. + */ + postSpriteInfo (data) { + if (this._dragTarget) { + this._dragTarget.postSpriteInfo(data); + } else { + this.editingTarget.postSpriteInfo(data); + } + // Post sprite info means the gui has changed something about a sprite, + // either through the sprite info pane fields (e.g. direction, size) or + // through dragging a sprite on the stage + // Emit a project changed event. + this.runtime.emitProjectChanged(); + } + + /** + * Set a target's variable's value. Return whether it succeeded. + * @param {!string} targetId ID of the target which owns the variable. + * @param {!string} variableId ID of the variable to set. + * @param {!*} value The new value of that variable. + * @returns {boolean} whether the target and variable were found and updated. + */ + setVariableValue (targetId, variableId, value) { + const target = this.runtime.getTargetById(targetId); + if (target) { + const variable = target.lookupVariableById(variableId); + if (variable) { + variable.value = value; + + if (variable.isCloud) { + this.runtime.ioDevices.cloud.requestUpdateVariable(variable.name, variable.value); + } + + return true; + } + } + return false; + } + + /** + * Get a target's variable's value. Return null if the target or variable does not exist. + * @param {!string} targetId ID of the target which owns the variable. + * @param {!string} variableId ID of the variable to set. + * @returns {?*} The value of the variable, or null if it could not be looked up. + */ + getVariableValue (targetId, variableId) { + const target = this.runtime.getTargetById(targetId); + if (target) { + const variable = target.lookupVariableById(variableId); + if (variable) { + return variable.value; + } + } + return null; + } + + /** + * Allow VM consumer to configure the ScratchLink socket creator. + * @param {Function} factory The custom ScratchLink socket factory. + */ + configureScratchLinkSocketFactory (factory) { + this.runtime.configureScratchLinkSocketFactory(factory); + } +} + +module.exports = VirtualMachine; From 9472226e826bfa8bea921c3e841d3ac06496c1e3 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 24 Apr 2023 15:24:05 -0400 Subject: [PATCH 1961/1971] commit patched changes, starter extension and lerna linking change node version used in github workflow to v16 update readme for starter branch, remove template files update gh-pages and ubuntu versions for gh-pages build and deploy Updated gui package-lock --- packages/scratch-gui/package.json | 6 +- .../src/components/menu-bar/menu-bar.jsx | 66 ++++++++++++++++++- .../scratch-gui/src/containers/blocks.jsx | 7 ++ .../src/lib/libraries/extensions/index.jsx | 23 +++++++ .../extension-support/extension-manager.js | 1 + 5 files changed, 97 insertions(+), 6 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index b5b9adfc90..e1ef7a4182 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -10,11 +10,12 @@ "url": "https://github.com/LLK/scratch-gui.git" }, "main": "./dist/scratch-gui.js", + "browser": "./src/index.js", "scripts": { "build": "npm run clean && webpack --colors --bail", "clean": "rimraf ./build && mkdirp build && rimraf ./dist && mkdirp dist", "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"[skip ci] Build for $(git log --pretty=format:%H -n1)\"", - "prepare": "husky install", + "prepare": "", "prune": "./prune-gh-pages.sh", "i18n:push": "tx-push-src scratch-editor interface translations/en.json", "i18n:src": "rimraf ./translations/messages/src && babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/src ./translations/", @@ -122,9 +123,8 @@ "eslint-plugin-import": "2.23.4", "eslint-plugin-jest": "22.17.0", "eslint-plugin-react": "7.24.0", - "gh-pages": "3.2.3", + "gh-pages": "5.0.0", "html-webpack-plugin": "3.2.0", - "husky": "8.0.1", "jest": "21.2.1", "jest-junit": "7.0.0", "mkdirp": "1.0.3", diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 5aba0ffaf1..ec78fefc68 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -9,6 +9,7 @@ import React from 'react'; import VM from 'scratch-vm'; +import DownloadConfirmation from './download-confirmation.jsx'; import Box from '../box/box.jsx'; import Button from '../button/button.jsx'; import CommunityButton from './community-button.jsx'; @@ -76,6 +77,8 @@ import collectMetadata from '../../lib/collect-metadata'; import styles from './menu-bar.css'; +import feedbackIcon from './icon--feedback.svg'; +import questionIcon from '../../lib/assets/icon--help.svg'; import helpIcon from '../../lib/assets/icon--tutorials.svg'; import mystuffIcon from './icon--mystuff.png'; import profileIcon from './icon--profile.png'; @@ -186,9 +189,15 @@ class MenuBar extends React.Component { 'handleKeyPress', 'handleLanguageMouseUp', 'handleRestoreOption', + 'handleConfirmDownload', + 'handleRejectDownload', 'getSaveToComputerHandler', 'restoreOptionMessage' ]); + + this.state = { + downloadProjectCallback: null + }; } componentDidMount () { document.addEventListener('keydown', this.handleKeyPress); @@ -290,13 +299,25 @@ class MenuBar extends React.Component { getSaveToComputerHandler (downloadProjectCallback) { return () => { this.props.onRequestCloseFile(); + this.setState({downloadProjectCallback}); + /* downloadProjectCallback(); if (this.props.onProjectTelemetryEvent) { const metadata = collectMetadata(this.props.vm, this.props.projectTitle, this.props.locale); this.props.onProjectTelemetryEvent('projectDidSave', metadata); } + */ }; } + handleConfirmDownload () { + if (this.state.downloadProjectCallback) { + this.state.downloadProjectCallback(); + } + this.setState({downloadProjectCallback: null}); + } + handleRejectDownload () { + this.setState({downloadProjectCallback: null}); + } handleLanguageMouseUp (e) { if (!this.props.languageMenuOpen) { this.props.onClickLanguage(e); @@ -437,15 +458,29 @@ class MenuBar extends React.Component {
    Scratch +
    + +
    + Starter Blocks + Help
    + {(this.props.canChangeLanguage) && (
    @@ -626,6 +661,7 @@ class MenuBar extends React.Component { )}
    + {/*
    + */} {this.props.canEditTitle ? (
    ) : null)} + {/* // remove feedback button + + */}
    {this.props.canShare ? ( (this.props.isShowingProject || this.props.isUpdating) && ( @@ -843,6 +897,12 @@ class MenuBar extends React.Component {
    {aboutButton} + {this.state.downloadProjectCallback && ( + + )} ); } diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index 616d2e0900..17e3c6b012 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -142,6 +142,13 @@ class Blocks extends React.Component { if (this.props.isVisible) { this.setLocale(); } + + // load starter extension on load + setTimeout(() => { + this.props.vm.extensionManager.loadExtensionURL('starter').then(() => { + this.handleCategorySelected('starter'); + }); + }, 1000); } shouldComponentUpdate (nextProps, nextState) { return ( diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index ba18b916b5..bfbe86d76a 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -1,6 +1,9 @@ import React from 'react'; import {FormattedMessage} from 'react-intl'; +import starterIconURL from './starter/starter.png'; +import starterInsetIconURL from './starter/starter-small.svg'; + import musicIconURL from './music/music.png'; import musicInsetIconURL from './music/music-small.svg'; @@ -47,6 +50,26 @@ import gdxforConnectionIconURL from './gdxfor/gdxfor-illustration.svg'; import gdxforConnectionSmallIconURL from './gdxfor/gdxfor-small.svg'; export default [ + { + name: ( + + ), + extensionId: 'starter', + iconURL: starterIconURL, + insetIconURL: starterInsetIconURL, + description: ( + + ), + featured: true + }, { name: ( require('../blocks/scratch3_core_example'), // These are the non-core built-in extensions. + starter: () => require('../extensions/scratch3_starter'), pen: () => require('../extensions/scratch3_pen'), wedo2: () => require('../extensions/scratch3_wedo2'), music: () => require('../extensions/scratch3_music'), From 86f4566de4ee262dbe62a09c7bec72184e8e316c Mon Sep 17 00:00:00 2001 From: Bogomil-Stoyanov Date: Fri, 24 Jan 2025 09:50:15 +0200 Subject: [PATCH 1962/1971] uepr-127:changed face sensing model --- packages/scratch-gui/package.json | 11 +++--- .../extensions/scratch3_face_sensing/index.js | 38 +++++++++++-------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index 48f7f117a4..d3c8fa64b4 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -27,9 +27,8 @@ "url": "git+ssh://git@github.com/LLK/scratch-gui.git" }, "dependencies": { - "@tensorflow-models/blazeface": "0.0.5", - "@tensorflow/tfjs-converter": "^1.2.7", - "@tensorflow/tfjs-core": "^1.2.7", + "@tensorflow-models/face-detection": "^1.0.3", + "@tensorflow/tfjs": "^4.22.0", "arraybuffer-loader": "^1.0.6", "autoprefixer": "^9.0.1", "base64-loader": "1.0.0", @@ -61,9 +60,9 @@ "prop-types": "^15.5.10", "query-string": "^5.1.1", "raw-loader": "^0.5.1", - "react": "16.2.0", + "react": "^16.0.0", "react-contextmenu": "2.9.4", - "react-dom": "16.2.0", + "react-dom": "^16.0.0", "react-draggable": "3.0.5", "react-ga": "2.5.3", "react-intl": "2.9.0", @@ -80,7 +79,7 @@ "scratch-audio": "0.1.0-prerelease.20200528195344", "scratch-blocks": "0.1.0-prerelease.20200908141031", "scratch-l10n": "3.10.20200909030847", - "scratch-paint": "0.2.0-prerelease.20200831213104", + "scratch-paint": "1.1.51", "scratch-render": "0.1.0-prerelease.20200827214414", "scratch-storage": "1.3.3", "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index ddd868016c..f3e153ae9f 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -7,8 +7,7 @@ const Video = require('../../io/video'); const TargetType = require('../../extension-support/target-type'); // const Posenet = require('@tensorflow-models/posenet'); -const Blazeface = require('@tensorflow-models/blazeface'); -// import * as Blazeface from '@tensorflow-models/blazeface'; +const FaceDetection = require('@tensorflow-models/face-detection'); /** * Icon svg to be displayed in the blocks category menu, encoded as a data URI. @@ -39,10 +38,16 @@ class Scratch3FaceSensingBlocks { this.runtime.emit('EXTENSION_DATA_LOADING', true); - Blazeface.load().then(model => { - this.blazeface = model; + const model = FaceDetection.SupportedModels.MediaPipeFaceDetector; + const detectorConfig = { + runtime: 'mediapipe', + solutionPath: 'https://cdn.jsdelivr.net/npm/@mediapipe/face_detection', + maxFaces: 1 + }; + + FaceDetection.createDetector(model, detectorConfig).then(detector => { + this.faceDetector = detector; if (this.runtime.ioDevices) { - // Kick off looping the analysis logic. this._loop(); } }); @@ -102,15 +107,17 @@ class Scratch3FaceSensingBlocks { cacheTimeout: this.runtime.currentStepTime }); if (frame) { - this.blazeface.estimateFaces(frame, false).then(faces => { - if (faces) { + this.faceDetector.estimateFaces(frame).then(faces => { + if (faces && faces.length > 0) { if (!this.firstTime) { this.firstTime = true; this.runtime.emit('EXTENSION_DATA_LOADING', false); } this.currentFace = faces[0]; - this.updateIsDetected(); + } else { + this.currentFace = null; } + this.updateIsDetected(); }); } } @@ -354,7 +361,7 @@ class Scratch3FaceSensingBlocks { ], menus: { PART: [ - {text: 'nose', value: '2'}, + {text: 'noseTip', value: '2'}, {text: 'mouth', value: '3'}, {text: 'left eye', value: '0'}, {text: 'right eye', value: '1'}, @@ -436,7 +443,7 @@ class Scratch3FaceSensingBlocks { faceSize () { if (!this.currentFace) return this.cachedSize; - const size = Math.round(this.currentFace.bottomRight[0] - this.currentFace.topLeft[0]); + const size = Math.round(this.currentFace.box.height); this.cachedSize = size; return size; } @@ -444,24 +451,25 @@ class Scratch3FaceSensingBlocks { getPartPosition (part) { const defaultPos = {x: 0, y: 0}; if (!this.currentFace) return defaultPos; - if (!this.currentFace.landmarks) return defaultPos; + if (!this.currentFace.keypoints) return defaultPos; if (Number(part) === 6) { return this.getBetweenEyesPosition(); } if (Number(part) === 7) { return this.getTopOfHeadPosition(); } - const result = this.currentFace.landmarks[Number(part)]; + const result = this.currentFace.keypoints[Number(part)]; if (result) { - return this.toScratchCoords(result); + const res = this.toScratchCoords(result); + return res; } return defaultPos; } toScratchCoords (position) { return { - x: position[0] - 240, - y: 180 - position[1] + x: position.x - 240, + y: 180 - position.y }; } From 98fe22ae4242a92145dfac02b1142a32f7c61743 Mon Sep 17 00:00:00 2001 From: Kaloyan Manolov Date: Tue, 1 Apr 2025 16:16:56 +0300 Subject: [PATCH 1963/1971] Merge with starter branch changes --- packages/scratch-gui/package.json | 116 +++++---- .../src/components/menu-bar/menu-bar.jsx | 238 +++++++++++++++--- .../scratch-gui/src/containers/blocks.jsx | 42 +++- packages/scratch-gui/src/containers/stage.jsx | 2 - .../src/lib/libraries/extensions/index.jsx | 3 + packages/scratch-render/src/RenderWebGL.js | 160 +++--------- packages/scratch-render/src/SVGSkin.js | 117 +++++++-- packages/scratch-vm/src/engine/runtime.js | 71 +++++- .../extension-support/extension-manager.js | 5 +- packages/scratch-vm/src/virtual-machine.js | 56 +++-- 10 files changed, 541 insertions(+), 269 deletions(-) diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index d3c8fa64b4..73b0229385 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -1,30 +1,36 @@ { "name": "scratch-gui", - "version": "0.1.0", - "description": "GraphicaL User Interface for creating and running Scratch 3.0 projects", + "version": "1.8.40", + "description": "Graphical User Interface for creating and running Scratch 3.0 projects", + "author": "Massachusetts Institute of Technology", + "license": "BSD-3-Clause", + "homepage": "https://github.com/LLK/scratch-gui#readme", + "repository": { + "type": "git", + "url": "https://github.com/LLK/scratch-gui.git" + }, "main": "./dist/scratch-gui.js", "browser": "./src/index.js", "scripts": { "build": "npm run clean && webpack --colors --bail", "clean": "rimraf ./build && mkdirp build && rimraf ./dist && mkdirp dist", - "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1) [skip ci]\"", + "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"[skip ci] Build for $(git log --pretty=format:%H -n1)\"", + "prepare": "", "prune": "./prune-gh-pages.sh", "i18n:push": "tx-push-src scratch-editor interface translations/en.json", - "i18n:src": "rimraf ./translations/messages/src && babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/src ./translations/ && npm run i18n:push", + "i18n:src": "rimraf ./translations/messages/src && babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/src ./translations/", "start": "webpack-dev-server", "test": "npm run test:lint && npm run test:unit && npm run build && npm run test:integration", - "test:integration": "jest --runInBand test[\\\\/]integration", + "test:integration": "jest --maxWorkers=4 test[\\\\/]integration", "test:lint": "eslint . --ext .js,.jsx", "test:unit": "jest test[\\\\/]unit", "test:smoke": "jest --runInBand test[\\\\/]smoke", "watch": "webpack --colors --watch" }, - "author": "Massachusetts Institute of Technology", - "license": "BSD-3-Clause", - "homepage": "https://github.com/LLK/scratch-gui#readme", - "repository": { - "type": "git", - "url": "git+ssh://git@github.com/LLK/scratch-gui.git" + "config": { + "commitizen": { + "path": "cz-conventional-changelog" + } }, "dependencies": { "@tensorflow-models/face-detection": "^1.0.3", @@ -33,9 +39,10 @@ "autoprefixer": "^9.0.1", "base64-loader": "1.0.0", "bowser": "1.9.4", + "cat-blocks": "npm:scratch-blocks@0.1.0-prerelease.20220318143026", "classnames": "2.2.6", "computed-style-to-inline-style": "3.0.0", - "copy-webpack-plugin": "^4.5.1", + "copy-webpack-plugin": "6.4.1", "core-js": "2.5.7", "css-loader": "^1.0.0", "es6-object-assign": "1.1.0", @@ -48,12 +55,12 @@ "keymirror": "0.1.1", "lodash.bindall": "4.4.0", "lodash.debounce": "4.0.8", - "lodash.defaultsdeep": "4.6.0", + "lodash.defaultsdeep": "4.6.1", "lodash.omit": "4.5.0", "lodash.throttle": "4.0.1", "minilog": "3.1.0", "omggif": "1.0.9", - "papaparse": "5.1.1", + "papaparse": "5.3.0", "postcss-import": "^12.0.0", "postcss-loader": "^3.0.0", "postcss-simple-vars": "^5.0.1", @@ -76,14 +83,15 @@ "react-virtualized": "9.20.1", "redux": "3.7.2", "redux-throttle": "0.1.1", - "scratch-audio": "0.1.0-prerelease.20200528195344", - "scratch-blocks": "0.1.0-prerelease.20200908141031", - "scratch-l10n": "3.10.20200909030847", + "scratch-audio": "0.1.0-prerelease.20221123180128", + "scratch-blocks": "0.1.0-prerelease.20230424044823", + "scratch-l10n": "3.15.20230422032237", "scratch-paint": "1.1.51", - "scratch-render": "0.1.0-prerelease.20200827214414", - "scratch-storage": "1.3.3", - "scratch-svg-renderer": "0.2.0-prerelease.20200610220938", - "scratch-vm": "0.2.0-prerelease.20200903205543", + "scratch-render": "0.1.0-prerelease.20230318150639", + "scratch-render-fonts": "1.0.0-prerelease.20221102164332", + "scratch-storage": "2.2.1", + "scratch-svg-renderer": "0.2.0-prerelease.20230224194137", + "scratch-vm": "1.5.41", "startaudiocontext": "1.2.1", "style-loader": "^0.23.0", "text-encoding": "0.7.0", @@ -96,40 +104,44 @@ "react-dom": "^16.0.0" }, "devDependencies": { - "@babel/cli": "^7.1.2", - "@babel/core": "^7.1.2", - "@babel/plugin-proposal-object-rest-spread": "^7.0.0", - "@babel/plugin-syntax-dynamic-import": "^7.0.0", - "@babel/plugin-transform-async-to-generator": "^7.1.0", - "@babel/preset-env": "^7.1.0", - "@babel/preset-react": "^7.0.0", + "@babel/cli": "7.14.8", + "@babel/core": "7.14.8", + "@babel/plugin-proposal-object-rest-spread": "7.14.7", + "@babel/plugin-syntax-dynamic-import": "7.2.0", + "@babel/plugin-transform-async-to-generator": "7.14.5", + "@babel/preset-env": "7.14.8", + "@babel/preset-react": "7.14.5", + "@commitlint/cli": "17.1.2", + "@commitlint/config-conventional": "17.1.0", "babel-core": "7.0.0-bridge.0", - "babel-eslint": "^10.0.1", - "babel-loader": "^8.0.4", - "chromedriver": "84.0.1", - "enzyme": "^3.5.0", - "enzyme-adapter-react-16": "1.3.0", - "eslint": "^5.0.1", - "eslint-config-scratch": "^5.0.0", - "eslint-import-resolver-webpack": "^0.11.1", - "eslint-plugin-import": "^2.18.2", - "eslint-plugin-jest": "^22.14.1", - "eslint-plugin-react": "^7.12.4", - "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", - "html-webpack-plugin": "^3.2.0", - "jest": "^21.0.0", - "jest-junit": "^7.0.0", - "mkdirp": "^1.0.3", - "raf": "^3.4.0", + "babel-eslint": "10.0.3", + "babel-loader": "8.2.2", + "chromedriver": "112.0.0", + "enzyme": "3.10.0", + "enzyme-adapter-react-16": "1.15.7", + "eslint": "5.16.0", + "eslint-config-scratch": "6.0.0", + "eslint-import-resolver-webpack": "0.11.1", + "eslint-plugin-import": "2.23.4", + "eslint-plugin-jest": "22.17.0", + "eslint-plugin-react": "7.24.0", + "gh-pages": "5.0.0", + "html-webpack-plugin": "3.2.0", + "jest": "21.2.1", + "jest-junit": "7.0.0", + "mkdirp": "1.0.3", + "raf": "3.4.1", "react-test-renderer": "16.2.0", - "redux-mock-store": "^1.2.3", - "rimraf": "^2.6.1", + "redux-mock-store": "1.5.3", + "rimraf": "2.7.1", + "scratch-semantic-release-config": "1.0.7", "selenium-webdriver": "3.6.0", - "uglifyjs-webpack-plugin": "^1.2.5", - "web-audio-test-api": "^0.5.2", - "webpack": "^4.6.0", - "webpack-cli": "^3.1.0", - "webpack-dev-server": "^3.1.3" + "semantic-release": "19.0.5", + "uglifyjs-webpack-plugin": "1.3.0", + "web-audio-test-api": "0.5.2", + "webpack": "4.46.0", + "webpack-cli": "3.3.12", + "webpack-dev-server": "3.11.2" }, "jest": { "setupFiles": [ diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index f734c8cc17..c9fc4cfded 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -9,6 +9,7 @@ import React from 'react'; import VM from 'scratch-vm'; +import DownloadConfirmation from './download-confirmation.jsx'; import Box from '../box/box.jsx'; import Button from '../button/button.jsx'; import CommunityButton from './community-button.jsx'; @@ -17,7 +18,6 @@ import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx'; import Divider from '../divider/divider.jsx'; import LanguageSelector from '../../containers/language-selector.jsx'; import SaveStatus from './save-status.jsx'; -import SBFileUploader from '../../containers/sb-file-uploader.jsx'; import ProjectWatcher from '../../containers/project-watcher.jsx'; import MenuBarMenu from './menu-bar-menu.jsx'; import {MenuItem, MenuSection} from '../menu/menu.jsx'; @@ -29,10 +29,17 @@ import SB3Downloader from '../../containers/sb3-downloader.jsx'; import DeletionRestorer from '../../containers/deletion-restorer.jsx'; import TurboMode from '../../containers/turbo-mode.jsx'; import MenuBarHOC from '../../containers/menu-bar-hoc.jsx'; -import DownloadConfirmation from './download-confirmation.jsx'; import {openTipsLibrary} from '../../reducers/modals'; import {setPlayer} from '../../reducers/mode'; +import { + isTimeTravel220022BC, + isTimeTravel1920, + isTimeTravel1990, + isTimeTravel2020, + isTimeTravelNow, + setTimeTravel +} from '../../reducers/time-travel'; import { autoUpdateProject, getIsUpdating, @@ -43,6 +50,9 @@ import { saveProjectAsCopy } from '../../reducers/project-state'; import { + openAboutMenu, + closeAboutMenu, + aboutMenuOpen, openAccountMenu, closeAccountMenu, accountMenuOpen, @@ -57,24 +67,28 @@ import { languageMenuOpen, openLoginMenu, closeLoginMenu, - loginMenuOpen + loginMenuOpen, + openModeMenu, + closeModeMenu, + modeMenuOpen } from '../../reducers/menus'; -import collectMetadata from '../../lib/collect-metadata'; - import styles from './menu-bar.css'; -import helpIcon from '../../lib/assets/icon--tutorials.svg'; +import feedbackIcon from './icon--feedback.svg'; +import questionIcon from '../../lib/assets/icon--help.svg'; import mystuffIcon from './icon--mystuff.png'; import profileIcon from './icon--profile.png'; import remixIcon from './icon--remix.svg'; import dropdownCaret from './dropdown-caret.svg'; import languageIcon from '../language-selector/language-icon.svg'; import aboutIcon from './icon--about.svg'; -import feedbackIcon from './icon--feedback.svg'; -import questionIcon from '../../lib/assets/icon--help.svg'; import scratchLogo from './scratch-logo.svg'; +import ninetiesLogo from './nineties_logo.svg'; +import catLogo from './cat_logo.svg'; +import prehistoricLogo from './prehistoric-logo.svg'; +import oldtimeyLogo from './oldtimey-logo.svg'; import sharedMessages from '../../lib/shared-messages'; @@ -168,9 +182,12 @@ class MenuBar extends React.Component { 'handleClickSaveAsCopy', 'handleClickSeeCommunity', 'handleClickShare', + 'handleSetMode', 'handleKeyPress', 'handleLanguageMouseUp', 'handleRestoreOption', + 'handleConfirmDownload', + 'handleRejectDownload', 'getSaveToComputerHandler', 'restoreOptionMessage', 'handleConfirmDownload', @@ -235,6 +252,36 @@ class MenuBar extends React.Component { } } } + handleSetMode (mode) { + return () => { + // Turn on/off filters for modes. + if (mode === '1920') { + document.documentElement.style.filter = 'brightness(.9)contrast(.8)sepia(1.0)'; + document.documentElement.style.height = '100%'; + } else if (mode === '1990') { + document.documentElement.style.filter = 'hue-rotate(40deg)'; + document.documentElement.style.height = '100%'; + } else { + document.documentElement.style.filter = ''; + document.documentElement.style.height = ''; + } + + // Change logo for modes + if (mode === '1990') { + document.getElementById('logo_img').src = ninetiesLogo; + } else if (mode === '2020') { + document.getElementById('logo_img').src = catLogo; + } else if (mode === '1920') { + document.getElementById('logo_img').src = oldtimeyLogo; + } else if (mode === '220022BC') { + document.getElementById('logo_img').src = prehistoricLogo; + } else { + document.getElementById('logo_img').src = this.props.logo; + } + + this.props.onSetTimeTravelMode(mode); + }; + } handleRestoreOption (restoreFun) { return () => { restoreFun(); @@ -252,11 +299,13 @@ class MenuBar extends React.Component { return () => { this.props.onRequestCloseFile(); this.setState({downloadProjectCallback}); - // downloadProjectCallback(); - // if (this.props.onProjectTelemetryEvent) { - // const metadata = collectMetadata(this.props.vm, this.props.projectTitle, this.props.locale); - // this.props.onProjectTelemetryEvent('projectDidSave', metadata); - // } + /* + downloadProjectCallback(); + if (this.props.onProjectTelemetryEvent) { + const metadata = collectMetadata(this.props.vm, this.props.projectTitle, this.props.locale); + this.props.onProjectTelemetryEvent('projectDidSave', metadata); + } + */ }; } handleConfirmDownload () { @@ -302,6 +351,56 @@ class MenuBar extends React.Component { } } } + buildAboutMenu (onClickAbout) { + if (!onClickAbout) { + // hide the button + return null; + } + if (typeof onClickAbout === 'function') { + // make a button which calls a function + return ; + } + // assume it's an array of objects + // each item must have a 'title' FormattedMessage and a 'handleClick' function + // generate a menu with items for each object in the array + return ( +
    + + + { + onClickAbout.map(itemProps => ( + + {itemProps.title} + + )) + } + +
    + ); + } + wrapAboutMenuCallback (callback) { + return () => { + callback(); + this.props.onRequestCloseAbout(); + }; + } render () { const saveNowMessage = ( ); // Show the About button only if we have a handler for it (like in the desktop app) - const aboutButton = this.props.onClickAbout ? : null; + const aboutButton = this.buildAboutMenu(this.props.onClickAbout); return ( )} - - {(className, renderFileInput, handleLoadProject) => ( - - {/* eslint-disable max-len */} - {this.props.intl.formatMessage(sharedMessages.loadFromComputerTitle)} - {/* eslint-enable max-len */} - {renderFileInput()} - - )} - + {this.props.intl.formatMessage(sharedMessages.loadFromComputerTitle)} + {(className, downloadProjectCallback) => ( +
    + {this.props.isTotallyNormal && ( +
    +
    + +
    + + + + + {'✓'} + + {' '} + + + + + {'✓'} + + {' '} + + + + +
    + )} - {/*
    { const loadingState = state.scratchGui.projectState.loadingState; const user = state.session && state.session.session && state.session.session.user; return { + aboutMenuOpen: aboutMenuOpen(state), accountMenuOpen: accountMenuOpen(state), fileMenuOpen: fileMenuOpen(state), editMenuOpen: editMenuOpen(state), @@ -846,12 +1007,18 @@ const mapStateToProps = (state, ownProps) => { languageMenuOpen: languageMenuOpen(state), locale: state.locales.locale, loginMenuOpen: loginMenuOpen(state), + modeMenuOpen: modeMenuOpen(state), projectTitle: state.scratchGui.projectTitle, sessionExists: state.session && typeof state.session.session !== 'undefined', username: user ? user.username : null, userOwnsProject: ownProps.authorUsername && user && (ownProps.authorUsername === user.username), - vm: state.scratchGui.vm + vm: state.scratchGui.vm, + mode220022BC: isTimeTravel220022BC(state), + mode1920: isTimeTravel1920(state), + mode1990: isTimeTravel1990(state), + mode2020: isTimeTravel2020(state), + modeNow: isTimeTravelNow(state) }; }; @@ -868,11 +1035,16 @@ const mapDispatchToProps = dispatch => ({ onRequestCloseLanguage: () => dispatch(closeLanguageMenu()), onClickLogin: () => dispatch(openLoginMenu()), onRequestCloseLogin: () => dispatch(closeLoginMenu()), + onClickMode: () => dispatch(openModeMenu()), + onRequestCloseMode: () => dispatch(closeModeMenu()), + onRequestOpenAbout: () => dispatch(openAboutMenu()), + onRequestCloseAbout: () => dispatch(closeAboutMenu()), onClickNew: needSave => dispatch(requestNewProject(needSave)), onClickRemix: () => dispatch(remixProject()), onClickSave: () => dispatch(manualUpdateProject()), onClickSaveAsCopy: () => dispatch(saveProjectAsCopy()), - onSeeCommunity: () => dispatch(setPlayer(true)) + onSeeCommunity: () => dispatch(setPlayer(true)), + onSetTimeTravelMode: mode => dispatch(setTimeTravel(mode)) }); export default compose( diff --git a/packages/scratch-gui/src/containers/blocks.jsx b/packages/scratch-gui/src/containers/blocks.jsx index df44a85343..6df0a17ce6 100644 --- a/packages/scratch-gui/src/containers/blocks.jsx +++ b/packages/scratch-gui/src/containers/blocks.jsx @@ -26,6 +26,7 @@ import {closeExtensionLibrary, openSoundRecorder, openConnectionModal} from '../ import {activateCustomProcedures, deactivateCustomProcedures} from '../reducers/custom-procedures'; import {setConnectionModalExtensionId} from '../reducers/connection-modal'; import {updateMetrics} from '../reducers/workspace-metrics'; +import {isTimeTravel2020} from '../reducers/time-travel'; import { activateTab, @@ -34,8 +35,8 @@ import { const addFunctionListener = (object, property, callback) => { const oldFn = object[property]; - object[property] = function () { - const result = oldFn.apply(this, arguments); + object[property] = function (...args) { + const result = oldFn.apply(this, args); callback.apply(this, result); return result; }; @@ -48,7 +49,7 @@ const DroppableBlocks = DropAreaHOC([ class Blocks extends React.Component { constructor (props) { super(props); - this.ScratchBlocks = VMScratchBlocks(props.vm); + this.ScratchBlocks = VMScratchBlocks(props.vm, false); bindAll(this, [ 'attachVM', 'detachVM', @@ -66,6 +67,7 @@ class Blocks extends React.Component { 'onScriptGlowOff', 'onBlockGlowOn', 'onBlockGlowOff', + 'handleMonitorsUpdate', 'handleExtensionAdded', 'handleBlocksInfoUpdate', 'onTargetsUpdate', @@ -86,6 +88,11 @@ class Blocks extends React.Component { this.toolboxUpdateQueue = []; } componentDidMount () { + this.ScratchBlocks = VMScratchBlocks(this.props.vm, this.props.useCatBlocks); + this.ScratchBlocks.prompt = this.handlePromptStart; + this.ScratchBlocks.statusButtonCallback = this.handleConnectionModalStart; + this.ScratchBlocks.recordSoundCallback = this.handleOpenSoundRecorder; + this.ScratchBlocks.FieldColourSlider.activateEyedropper_ = this.props.onActivateColorPicker; this.ScratchBlocks.Procedures.externalProcedureDefCallback = this.props.onActivateCustomProcedures; this.ScratchBlocks.ScratchMsgs.setLocale(this.props.locale); @@ -198,6 +205,9 @@ class Blocks extends React.Component { this.detachVM(); this.workspace.dispose(); clearTimeout(this.toolboxUpdateTimeout); + + // Clear the flyout blocks so that they can be recreated on mount. + this.props.vm.clearFlyoutBlocks(); } requestToolboxUpdate () { clearTimeout(this.toolboxUpdateTimeout); @@ -267,6 +277,7 @@ class Blocks extends React.Component { this.props.vm.addListener('VISUAL_REPORT', this.onVisualReport); this.props.vm.addListener('workspaceUpdate', this.onWorkspaceUpdate); this.props.vm.addListener('targetsUpdate', this.onTargetsUpdate); + this.props.vm.addListener('MONITORS_UPDATE', this.handleMonitorsUpdate); this.props.vm.addListener('EXTENSION_ADDED', this.handleExtensionAdded); this.props.vm.addListener('BLOCKSINFO_UPDATE', this.handleBlocksInfoUpdate); this.props.vm.addListener('PERIPHERAL_CONNECTED', this.handleStatusButtonUpdate); @@ -280,6 +291,7 @@ class Blocks extends React.Component { this.props.vm.removeListener('VISUAL_REPORT', this.onVisualReport); this.props.vm.removeListener('workspaceUpdate', this.onWorkspaceUpdate); this.props.vm.removeListener('targetsUpdate', this.onTargetsUpdate); + this.props.vm.removeListener('MONITORS_UPDATE', this.handleMonitorsUpdate); this.props.vm.removeListener('EXTENSION_ADDED', this.handleExtensionAdded); this.props.vm.removeListener('BLOCKSINFO_UPDATE', this.handleBlocksInfoUpdate); this.props.vm.removeListener('PERIPHERAL_CONNECTED', this.handleStatusButtonUpdate); @@ -405,6 +417,24 @@ class Blocks extends React.Component { // workspace to be 'undone' here. this.workspace.clearUndo(); } + handleMonitorsUpdate (monitors) { + // Update the checkboxes of the relevant monitors. + // TODO: What about monitors that have fields? See todo in scratch-vm blocks.js changeBlock: + // https://github.com/LLK/scratch-vm/blob/2373f9483edaf705f11d62662f7bb2a57fbb5e28/src/engine/blocks.js#L569-L576 + const flyout = this.workspace.getFlyout(); + for (const monitor of monitors.values()) { + const blockId = monitor.get('id'); + const isVisible = monitor.get('visible'); + flyout.setCheckboxState(blockId, isVisible); + // We also need to update the isMonitored flag for this block on the VM, since it's used to determine + // whether the checkbox is activated or not when the checkbox is re-displayed (e.g. local variables/blocks + // when switching between sprites). + const block = this.props.vm.runtime.monitorBlocks.getBlock(blockId); + if (block) { + block.isMonitored = isVisible; + } + } + } handleExtensionAdded (categoryInfo) { const defineBlocks = blockInfoArray => { if (blockInfoArray && blockInfoArray.length > 0) { @@ -537,6 +567,7 @@ class Blocks extends React.Component { onRequestCloseCustomProcedures, toolboxXML, updateMetrics: updateMetricsProp, + useCatBlocks, workspaceMetrics, ...props } = this.props; @@ -552,6 +583,7 @@ class Blocks extends React.Component { ({ messages: state.locales.messages, toolboxXML: state.scratchGui.toolbox.toolboxXML, customProceduresVisible: state.scratchGui.customProcedures.active, - workspaceMetrics: state.scratchGui.workspaceMetrics + workspaceMetrics: state.scratchGui.workspaceMetrics, + useCatBlocks: isTimeTravel2020(state) }); const mapDispatchToProps = dispatch => ({ diff --git a/packages/scratch-gui/src/containers/stage.jsx b/packages/scratch-gui/src/containers/stage.jsx index 79a41db7ea..611e84fe20 100644 --- a/packages/scratch-gui/src/containers/stage.jsx +++ b/packages/scratch-gui/src/containers/stage.jsx @@ -8,7 +8,6 @@ import {connect} from 'react-redux'; import {STAGE_DISPLAY_SIZES} from '../lib/layout-constants'; import {getEventXY} from '../lib/touch-utils'; import VideoProvider from '../lib/video/video-provider'; -import {SVGRenderer as V2SVGAdapter} from 'scratch-svg-renderer'; import {BitmapAdapter as V2BitmapAdapter} from 'scratch-svg-renderer'; import StageComponent from '../components/stage/stage.jsx'; @@ -69,7 +68,6 @@ class Stage extends React.Component { // default color this.props.vm.renderer.draw(); } - this.props.vm.attachV2SVGAdapter(new V2SVGAdapter()); this.props.vm.attachV2BitmapAdapter(new V2BitmapAdapter()); } componentDidMount () { diff --git a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx index e791fc934b..61d996c041 100644 --- a/packages/scratch-gui/src/lib/libraries/extensions/index.jsx +++ b/packages/scratch-gui/src/lib/libraries/extensions/index.jsx @@ -1,6 +1,9 @@ import React from 'react'; import {FormattedMessage} from 'react-intl'; +import starterIconURL from './starter/starter.png'; +import starterInsetIconURL from './starter/starter-small.svg'; + import musicIconURL from './music/music.png'; import musicInsetIconURL from './music/music-small.svg'; diff --git a/packages/scratch-render/src/RenderWebGL.js b/packages/scratch-render/src/RenderWebGL.js index d8ba0c2f9d..6f7a4b2c9c 100644 --- a/packages/scratch-render/src/RenderWebGL.js +++ b/packages/scratch-render/src/RenderWebGL.js @@ -111,7 +111,12 @@ class RenderWebGL extends EventEmitter { * @private */ static _getContext (canvas) { - return twgl.getWebGLContext(canvas, {alpha: false, stencil: true, antialias: false}); + const contextAttribs = {alpha: false, stencil: true, antialias: false}; + // getWebGLContext = try WebGL 1.0 only + // getContext = try WebGL 2.0 and if that doesn't work, try WebGL 1.0 + // getWebGLContext || getContext = try WebGL 1.0 and if that doesn't work, try WebGL 2.0 + return twgl.getWebGLContext(canvas, contextAttribs) || + twgl.getContext(canvas, contextAttribs); } /** @@ -653,7 +658,10 @@ class RenderWebGL extends EventEmitter { gl.clearColor(...this._backgroundColor4f); gl.clear(gl.COLOR_BUFFER_BIT); - this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, this._projection); + this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, this._projection, { + framebufferWidth: gl.canvas.width, + framebufferHeight: gl.canvas.height + }); if (this._snapshotCallbacks.length > 0) { const snapshot = gl.canvas.toDataURL(); this._snapshotCallbacks.forEach(cb => cb(snapshot)); @@ -1162,114 +1170,6 @@ class RenderWebGL extends EventEmitter { return Number(hit); } - /** - * @typedef DrawableExtractionOld - * @property {Uint8Array} data Raw pixel data for the drawable - * @property {int} width Drawable bounding box width - * @property {int} height Drawable bounding box height - * @property {Array} scratchOffset [x, y] offset in Scratch coordinates - * from the drawable position to the client x, y coordinate - * @property {int} x The x coordinate relative to drawable bounding box - * @property {int} y The y coordinate relative to drawable bounding box - */ - - /** - * Return drawable pixel data and picking coordinates relative to the drawable bounds - * @param {int} drawableID The ID of the drawable to get pixel data for - * @param {int} x The client x coordinate of the picking location. - * @param {int} y The client y coordinate of the picking location. - * @return {?DrawableExtractionOld} Data about the picked drawable - * @deprecated Use {@link extractDrawableScreenSpace} instead. - */ - extractDrawable (drawableID, x, y) { - this._doExitDrawRegion(); - - const drawable = this._allDrawables[drawableID]; - if (!drawable) return null; - - // Convert client coordinates into absolute scratch units - const scratchX = this._nativeSize[0] * ((x / this._gl.canvas.clientWidth) - 0.5); - const scratchY = this._nativeSize[1] * ((y / this._gl.canvas.clientHeight) - 0.5); - - const gl = this._gl; - - const bounds = drawable.getFastBounds(); - bounds.snapToInt(); - - // Set a reasonable max limit width and height for the bufferInfo bounds - const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); - const clampedWidth = Math.min(2048, bounds.width, maxTextureSize); - const clampedHeight = Math.min(2048, bounds.height, maxTextureSize); - - // Make a new bufferInfo since this._queryBufferInfo is limited to 480x360 - const attachments = [ - {format: gl.RGBA}, - {format: gl.DEPTH_STENCIL} - ]; - const bufferInfo = twgl.createFramebufferInfo(gl, attachments, clampedWidth, clampedHeight); - - try { - // If the new bufferInfo is invalid, fall back to using the smaller _queryBufferInfo - twgl.bindFramebufferInfo(gl, bufferInfo); - if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) { - twgl.bindFramebufferInfo(gl, this._queryBufferInfo); - } - - // Translate to scratch units relative to the drawable - const pickX = scratchX - bounds.left; - const pickY = scratchY + bounds.top; - - // Limit size of viewport to the bounds around the target Drawable, - // and create the projection matrix for the draw. - gl.viewport(0, 0, bounds.width, bounds.height); - const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); - - gl.clearColor(0, 0, 0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - try { - gl.disable(gl.BLEND); - // ImageData objects store alpha un-premultiplied, so draw with the `straightAlpha` draw mode. - this._drawThese([drawableID], ShaderManager.DRAW_MODE.straightAlpha, projection, - {effectMask: ~ShaderManager.EFFECT_INFO.ghost.mask}); - } finally { - gl.enable(gl.BLEND); - } - - const data = new Uint8Array(Math.floor(bounds.width * bounds.height * 4)); - gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, data); - - if (this._debugCanvas) { - this._debugCanvas.width = bounds.width; - this._debugCanvas.height = bounds.height; - const ctx = this._debugCanvas.getContext('2d'); - const imageData = ctx.createImageData(bounds.width, bounds.height); - imageData.data.set(data); - ctx.putImageData(imageData, 0, 0); - ctx.beginPath(); - ctx.arc(pickX, pickY, 3, 0, 2 * Math.PI, false); - ctx.fillStyle = 'white'; - ctx.fill(); - ctx.lineWidth = 1; - ctx.strokeStyle = 'black'; - ctx.stroke(); - } - - return { - data: data, - width: bounds.width, - height: bounds.height, - scratchOffset: [ - -scratchX + drawable._position[0], - -scratchY - drawable._position[1] - ], - x: pickX, - y: pickY - }; - } finally { - gl.deleteFramebuffer(bufferInfo.framebuffer); - } - } - /** * @typedef DrawableExtraction * @property {ImageData} data Raw pixel data for the drawable @@ -1347,9 +1247,15 @@ class RenderWebGL extends EventEmitter { gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); - // Don't apply the ghost effect. TODO: is this an intentional design decision? this._drawThese([drawableID], ShaderManager.DRAW_MODE.straightAlpha, projection, - {effectMask: ~ShaderManager.EFFECT_INFO.ghost.mask}); + { + // Don't apply the ghost effect. TODO: is this an intentional design decision? + effectMask: ~ShaderManager.EFFECT_INFO.ghost.mask, + // We're doing this in screen-space, so the framebuffer dimensions should be those of the canvas in + // screen-space. This is used to ensure SVG skins are rendered at the proper resolution. + framebufferWidth: canvas.width, + framebufferHeight: canvas.height + }); const data = new Uint8Array(Math.floor(clampedWidth * clampedHeight * 4)); gl.readPixels(0, 0, clampedWidth, clampedHeight, gl.RGBA, gl.UNSIGNED_BYTE, data); @@ -1851,18 +1757,6 @@ class RenderWebGL extends EventEmitter { this._regionId = null; } - /** - * Get the screen-space scale of a drawable, as percentages of the drawable's "normal" size. - * @param {Drawable} drawable The drawable whose screen-space scale we're fetching. - * @returns {Array} The screen-space X and Y dimensions of the drawable's scale, as percentages. - */ - _getDrawableScreenSpaceScale (drawable) { - return [ - drawable.scale[0] * this._gl.canvas.width / this._nativeSize[0], - drawable.scale[1] * this._gl.canvas.height / this._nativeSize[1] - ]; - } - /** * Draw a set of Drawables, by drawable ID * @param {Array} drawables The Drawable IDs to draw, possibly this._drawList. @@ -1873,6 +1767,8 @@ class RenderWebGL extends EventEmitter { * @param {object.} opts.extraUniforms Extra uniforms for the shaders. * @param {int} opts.effectMask Bitmask for effects to allow * @param {boolean} opts.ignoreVisibility Draw all, despite visibility (e.g. stamping, touching color) + * @param {int} opts.framebufferWidth The width of the framebuffer being drawn onto. Defaults to "native" width + * @param {int} opts.framebufferHeight The height of the framebuffer being drawn onto. Defaults to "native" height * @private */ _drawThese (drawables, drawMode, projection, opts = {}) { @@ -1880,6 +1776,11 @@ class RenderWebGL extends EventEmitter { const gl = this._gl; let currentShader = null; + const framebufferSpaceScaleDiffers = ( + 'framebufferWidth' in opts && 'framebufferHeight' in opts && + opts.framebufferWidth !== this._nativeSize[0] && opts.framebufferHeight !== this._nativeSize[1] + ); + const numDrawables = drawables.length; for (let drawableIndex = 0; drawableIndex < numDrawables; ++drawableIndex) { const drawableID = drawables[drawableIndex]; @@ -1894,8 +1795,13 @@ class RenderWebGL extends EventEmitter { // the ignoreVisibility flag is used (e.g. for stamping or touchingColor). if (!drawable.getVisible() && !opts.ignoreVisibility) continue; - // Combine drawable scale with the native vs. backing pixel ratio - const drawableScale = this._getDrawableScreenSpaceScale(drawable); + // drawableScale is the "framebuffer-pixel-space" scale of the drawable, as percentages of the drawable's + // "native size" (so 100 = same as skin's "native size", 200 = twice "native size"). + // If the framebuffer dimensions are the same as the stage's "native" size, there's no need to calculate it. + const drawableScale = framebufferSpaceScaleDiffers ? [ + drawable.scale[0] * opts.framebufferWidth / this._nativeSize[0], + drawable.scale[1] * opts.framebufferHeight / this._nativeSize[1] + ] : drawable.scale; // If the skin or texture isn't ready yet, skip it. if (!drawable.skin || !drawable.skin.getTexture(drawableScale)) continue; @@ -2127,7 +2033,7 @@ class RenderWebGL extends EventEmitter { } // :3 -RenderWebGL.prototype.canHazPixels = RenderWebGL.prototype.extractDrawable; +RenderWebGL.prototype.canHazPixels = RenderWebGL.prototype.extractDrawableScreenSpace; /** * Values for setUseGPU() diff --git a/packages/scratch-render/src/SVGSkin.js b/packages/scratch-render/src/SVGSkin.js index bf1aeca888..8f60506222 100644 --- a/packages/scratch-render/src/SVGSkin.js +++ b/packages/scratch-render/src/SVGSkin.js @@ -1,7 +1,7 @@ const twgl = require('twgl.js'); const Skin = require('./Skin'); -const SvgRenderer = require('scratch-svg-renderer').SVGRenderer; +const {loadSvgString, serializeSvgToString} = require('scratch-svg-renderer'); const ShaderManager = require('./ShaderManager'); const MAX_TEXTURE_DIMENSION = 2048; @@ -28,8 +28,20 @@ class SVGSkin extends Skin { /** @type {RenderWebGL} */ this._renderer = renderer; - /** @type {SvgRenderer} */ - this._svgRenderer = new SvgRenderer(); + /** @type {HTMLImageElement} */ + this._svgImage = document.createElement('img'); + + /** @type {boolean} */ + this._svgImageLoaded = false; + + /** @type {Array} */ + this._size = [0, 0]; + + /** @type {HTMLCanvasElement} */ + this._canvas = document.createElement('canvas'); + + /** @type {CanvasRenderingContext2D} */ + this._context = this._canvas.getContext('2d'); /** @type {Array} */ this._scaledMIPs = []; @@ -56,7 +68,36 @@ class SVGSkin extends Skin { * @return {Array} the natural size, in Scratch units, of this skin. */ get size () { - return this._svgRenderer.size; + return [this._size[0], this._size[1]]; + } + + useNearest (scale, drawable) { + // If the effect bits for mosaic, pixelate, whirl, or fisheye are set, use linear + if ((drawable.enabledEffects & ( + ShaderManager.EFFECT_INFO.fisheye.mask | + ShaderManager.EFFECT_INFO.whirl.mask | + ShaderManager.EFFECT_INFO.pixelate.mask | + ShaderManager.EFFECT_INFO.mosaic.mask + )) !== 0) { + return false; + } + + // We can't use nearest neighbor unless we are a multiple of 90 rotation + if (drawable._direction % 90 !== 0) { + return false; + } + + // Because SVG skins' bounding boxes are currently not pixel-aligned, the idea here is to hide blurriness + // by using nearest-neighbor scaling if one screen-space pixel is "close enough" to one texture pixel. + // If the scale of the skin is very close to 100 (0.99999 variance is okay I guess) + // TODO: Make this check more precise. We should use nearest if there's less than one pixel's difference + // between the screen-space and texture-space sizes of the skin. Mipmaps make this harder because there are + // multiple textures (and hence multiple texture spaces) and we need to know which one to choose. + if (Math.abs(scale[0]) > 99 && Math.abs(scale[0]) < 101 && + Math.abs(scale[1]) > 99 && Math.abs(scale[1]) < 101) { + return true; + } + return false; } useNearest (scale, drawable) { @@ -94,18 +135,27 @@ class SVGSkin extends Skin { * @return {SVGMIP} An object that handles creating and updating SVG textures. */ createMIP (scale) { - this._svgRenderer.draw(scale); + const [width, height] = this._size; + this._canvas.width = width * scale; + this._canvas.height = height * scale; + if ( + this._canvas.width <= 0 || + this._canvas.height <= 0 || + // Even if the canvas at the current scale has a nonzero size, the image's dimensions are floored + // pre-scaling; e.g. if an image has a width of 0.4 and is being rendered at 3x scale, the canvas will have + // a width of 1, but the image's width will be rounded down to 0 on some browsers (Firefox) prior to being + // drawn at that scale, resulting in an IndexSizeError if we attempt to draw it. + this._svgImage.naturalWidth <= 0 || + this._svgImage.naturalHeight <= 0 + ) return super.getTexture(); + this._context.clearRect(0, 0, this._canvas.width, this._canvas.height); + this._context.setTransform(scale, 0, 0, scale, 0, 0); + this._context.drawImage(this._svgImage, 0, 0); // Pull out the ImageData from the canvas. ImageData speeds up // updating Silhouette and is better handled by more browsers in // regards to memory. - const canvas = this._svgRenderer.canvas; - // If one of the canvas dimensions is 0, set this MIP to an empty image texture. - // This avoids an IndexSizeError from attempting to getImageData when one of the dimensions is 0. - if (canvas.width === 0 || canvas.height === 0) return super.getTexture(); - - const context = canvas.getContext('2d'); - const textureData = context.getImageData(0, 0, canvas.width, canvas.height); + const textureData = this._context.getImageData(0, 0, this._canvas.width, this._canvas.height); const textureOptions = { auto: false, @@ -147,7 +197,7 @@ class SVGSkin extends Skin { // Can't use bitwise stuff here because we need to handle negative exponents const mipScale = Math.pow(2, mipLevel - INDEX_OFFSET); - if (this._svgRenderer.loaded && !this._scaledMIPs[mipLevel]) { + if (this._svgImageLoaded && !this._scaledMIPs[mipLevel]) { this._scaledMIPs[mipLevel] = this.createMIP(mipScale); } @@ -156,9 +206,6 @@ class SVGSkin extends Skin { /** * Do a hard reset of the existing MIPs by deleting them. - * @param {Array} [rotationCenter] - Optional rotation center for the SVG. If not supplied, it will be - * calculated from the bounding box - * @fires Skin.event:WasAltered */ resetMIPs () { this._scaledMIPs.forEach(oldMIP => this._renderer.gl.deleteTexture(oldMIP)); @@ -169,17 +216,32 @@ class SVGSkin extends Skin { /** * Set the contents of this skin to a snapshot of the provided SVG data. * @param {string} svgData - new SVG to use. - * @param {Array} [rotationCenter] - Optional rotation center for the SVG. + * @param {Array} [rotationCenter] - Optional rotation center for the SVG. If not supplied, it will be + * calculated from the bounding box + * @fires Skin.event:WasAltered */ setSVG (svgData, rotationCenter) { - this._svgRenderer.loadSVG(svgData, false, () => { - const svgSize = this._svgRenderer.size; - if (svgSize[0] === 0 || svgSize[1] === 0) { + const svgTag = loadSvgString(svgData); + const svgText = serializeSvgToString(svgTag, true /* shouldInjectFonts */); + this._svgImageLoaded = false; + + const {x, y, width, height} = svgTag.viewBox.baseVal; + // While we're setting the size before the image is loaded, this doesn't cause the skin to appear with the wrong + // size for a few frames while the new image is loading, because we don't emit the `WasAltered` event, telling + // drawables using this skin to update, until the image is loaded. + // We need to do this because the VM reads the skin's `size` directly after calling `setSVG`. + // TODO: return a Promise so that the VM can read the skin's `size` after the image is loaded. + this._size[0] = width; + this._size[1] = height; + + // If there is another load already in progress, replace the old onload to effectively cancel the old load + this._svgImage.onload = () => { + if (width === 0 || height === 0) { super.setEmptyImageData(); return; } - const maxDimension = Math.ceil(Math.max(this.size[0], this.size[1])); + const maxDimension = Math.ceil(Math.max(width, height)); let testScale = 2; for (testScale; maxDimension * testScale <= MAX_TEXTURE_DIMENSION; testScale *= 2) { this._maxTextureScale = testScale; @@ -188,12 +250,17 @@ class SVGSkin extends Skin { this.resetMIPs(); if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); - const viewOffset = this._svgRenderer.viewOffset; - this._rotationCenter[0] = rotationCenter[0] - viewOffset[0]; - this._rotationCenter[1] = rotationCenter[1] - viewOffset[1]; + // Compensate for viewbox offset. + // See https://github.com/LLK/scratch-render/pull/90. + this._rotationCenter[0] = rotationCenter[0] - x; + this._rotationCenter[1] = rotationCenter[1] - y; + + this._svgImageLoaded = true; this.emit(Skin.Events.WasAltered); - }); + }; + + this._svgImage.src = `data:image/svg+xml;utf8,${encodeURIComponent(svgText)}`; } } diff --git a/packages/scratch-vm/src/engine/runtime.js b/packages/scratch-vm/src/engine/runtime.js index 6ae31adb78..c23ae7d0da 100644 --- a/packages/scratch-vm/src/engine/runtime.js +++ b/packages/scratch-vm/src/engine/runtime.js @@ -392,6 +392,15 @@ class Runtime extends EventEmitter { * @type {function} */ this.removeCloudVariable = this._initializeRemoveCloudVariable(newCloudDataManager); + + /** + * A string representing the origin of the current project from outside of the + * Scratch community, such as CSFirst. + * @type {?string} + */ + this.origin = null; + + this._initScratchLink(); } /** @@ -611,6 +620,15 @@ class Runtime extends EventEmitter { return 'USER_PICKED_PERIPHERAL'; } + /** + * Event name for when the user picks a bluetooth device to connect to + * via Companion Device Manager (CDM) + * @const {string} + */ + static get USER_PICKED_PERIPHERAL () { + return 'USER_PICKED_PERIPHERAL'; + } + /** * Event name for reporting that a peripheral has connected. * This causes the status button in the blocks menu to indicate 'connected'. @@ -1434,6 +1452,36 @@ class Runtime extends EventEmitter { (result, categoryInfo) => result.concat(categoryInfo.blocks.map(blockInfo => blockInfo.json)), []); } + /** + * One-time initialization for Scratch Link support. + */ + _initScratchLink () { + /* global globalThis */ + // Check that we're actually in a real browser, not Node.js or JSDOM, and we have a valid-looking origin. + if (globalThis.document && + globalThis.document.getElementById && + globalThis.origin && + globalThis.origin !== 'null' && + globalThis.navigator && + globalThis.navigator.userAgent && + globalThis.navigator.userAgent.includes && + !globalThis.navigator.userAgent.includes('Node.js') && + !globalThis.navigator.userAgent.includes('jsdom') + ) { + // Create a script tag for the Scratch Link browser extension, unless one already exists + const scriptElement = document.getElementById('scratch-link-extension-script'); + if (!scriptElement) { + const script = document.createElement('script'); + script.id = 'scratch-link-extension-script'; + document.body.appendChild(script); + + // Tell the browser extension to inject its script. + // If the extension isn't present or isn't active, this will do nothing. + globalThis.postMessage('inject-scratch-link-script', globalThis.origin); + } + } + } + /** * Get a scratch link socket. * @param {string} type Either BLE or BT @@ -1459,7 +1507,11 @@ class Runtime extends EventEmitter { * @returns {ScratchLinkSocket} The new scratch link socket (a WebSocket object) */ _defaultScratchLinkSocketFactory (type) { - return new ScratchLinkWebSocket(type); + const Scratch = self.Scratch; + const ScratchLinkSafariSocket = Scratch && Scratch.ScratchLinkSafariSocket; + // detect this every time in case the user turns on the extension after loading the page + const useSafariSocket = ScratchLinkSafariSocket && ScratchLinkSafariSocket.isSafariHelperCompatible(); + return useSafariSocket ? new ScratchLinkSafariSocket(type) : new ScratchLinkWebSocket(type); } /** @@ -1574,14 +1626,6 @@ class Runtime extends EventEmitter { this.renderer.setLayerGroupOrdering(StageLayering.LAYER_GROUPS); } - /** - * Set the svg adapter, which converts scratch 2 svgs to scratch 3 svgs - * @param {!SvgRenderer} svgAdapter The adapter to attach - */ - attachV2SVGAdapter (svgAdapter) { - this.v2SvgAdapter = svgAdapter; - } - /** * Set the bitmap adapter for the VM/runtime, which converts scratch 2 * bitmaps to scratch 3 bitmaps. (Scratch 3 bitmaps are all bitmap resolution 2) @@ -2587,6 +2631,15 @@ class Runtime extends EventEmitter { this.emit(Runtime.RUNTIME_STARTED); } + /** + * Quit the Runtime, clearing any handles which might keep the process alive. + * Do not use the runtime after calling this method. This method is meant for test shutdown. + */ + quit () { + clearInterval(this._steppingInterval); + this._steppingInterval = null; + } + /** * Turn on profiling. * @param {Profiler/FrameCallback} onFrame A callback handle passed a diff --git a/packages/scratch-vm/src/extension-support/extension-manager.js b/packages/scratch-vm/src/extension-support/extension-manager.js index 8e687217ed..46ce906adc 100644 --- a/packages/scratch-vm/src/extension-support/extension-manager.js +++ b/packages/scratch-vm/src/extension-support/extension-manager.js @@ -13,6 +13,7 @@ const builtinExtensions = { // but serves as a reference for loading core blocks as extensions. coreExample: () => require('../blocks/scratch3_core_example'), // These are the non-core built-in extensions. + starter: () => require('../extensions/scratch3_starter'), pen: () => require('../extensions/scratch3_pen'), wedo2: () => require('../extensions/scratch3_wedo2'), music: () => require('../extensions/scratch3_music'), @@ -82,8 +83,8 @@ class ExtensionManager { this.pendingWorkers = []; /** - * Set of loaded extension URLs/IDs (equivalent for built-in extensions). - * @type {Set.} + * Map of loaded extension URLs/IDs (equivalent for built-in extensions) to service name. + * @type {Map.} * @private */ this._loadedExtensions = new Map(); diff --git a/packages/scratch-vm/src/virtual-machine.js b/packages/scratch-vm/src/virtual-machine.js index 0630e46d53..873af5089c 100644 --- a/packages/scratch-vm/src/virtual-machine.js +++ b/packages/scratch-vm/src/virtual-machine.js @@ -178,6 +178,14 @@ class VirtualMachine extends EventEmitter { this.runtime.start(); } + /** + * Quit the VM, clearing any handles which might keep the process alive. + * Do not use the runtime after calling this method. This method is meant for test shutdown. + */ + quit () { + this.runtime.quit(); + } + /** * "Green flag" handler - start all threads starting with a green flag. */ @@ -369,7 +377,11 @@ class VirtualMachine extends EventEmitter { const vm = this; const promise = storage.load(storage.AssetType.Project, id); promise.then(projectAsset => { - vm.loadProject(projectAsset.data); + if (!projectAsset) { + log.error(`Failed to fetch project with id: ${id}`); + return null; + } + return vm.loadProject(projectAsset.data); }); } @@ -430,11 +442,9 @@ class VirtualMachine extends EventEmitter { * specified by optZipType or blob by default. */ exportSprite (targetId, optZipType) { - const sb3 = require('./serialization/sb3'); - const soundDescs = serializeSounds(this.runtime, targetId); const costumeDescs = serializeCostumes(this.runtime, targetId); - const spriteJson = StringUtil.stringify(sb3.serialize(this.runtime, targetId)); + const spriteJson = this.toJSON(targetId); const zip = new JSZip(); zip.file('sprite.json', spriteJson); @@ -451,12 +461,13 @@ class VirtualMachine extends EventEmitter { } /** - * Export project as a Scratch 3.0 JSON representation. + * Export project or sprite as a Scratch 3.0 JSON representation. + * @param {string=} optTargetId - Optional id of a sprite to serialize * @return {string} Serialized state of the runtime. */ - toJSON () { + toJSON (optTargetId) { const sb3 = require('./serialization/sb3'); - return StringUtil.stringify(sb3.serialize(this.runtime)); + return StringUtil.stringify(sb3.serialize(this.runtime, optTargetId)); } // TODO do we still need this function? Keeping it here so as not to introduce @@ -481,6 +492,9 @@ class VirtualMachine extends EventEmitter { // Clear the current runtime this.clear(); + if (typeof performance !== 'undefined') { + performance.mark('scratch-vm-deserialize-start'); + } const runtime = this.runtime; const deserializePromise = function () { const projectVersion = projectJSON.projectVersion; @@ -495,8 +509,14 @@ class VirtualMachine extends EventEmitter { return Promise.reject('Unable to verify Scratch Project version.'); }; return deserializePromise() - .then(({targets, extensions}) => - this.installTargets(targets, extensions, true)); + .then(({targets, extensions}) => { + if (typeof performance !== 'undefined') { + performance.mark('scratch-vm-deserialize-end'); + performance.measure('scratch-vm-deserialize', + 'scratch-vm-deserialize-start', 'scratch-vm-deserialize-end'); + } + return this.installTargets(targets, extensions, true); + }); } /** @@ -783,6 +803,7 @@ class VirtualMachine extends EventEmitter { */ updateSoundBuffer (soundIndex, newBuffer, soundEncoding) { const sound = this.editingTarget.sprite.sounds[soundIndex]; + if (sound && sound.broken) delete sound.broken; const id = sound ? sound.soundId : null; if (id && this.runtime && this.runtime.audioEngine) { this.editingTarget.sprite.soundBank.getSoundPlayer(id).buffer = newBuffer; @@ -868,6 +889,7 @@ class VirtualMachine extends EventEmitter { updateBitmap (costumeIndex, bitmap, rotationCenterX, rotationCenterY, bitmapResolution) { const costume = this.editingTarget.getCostumes()[costumeIndex]; if (!(costume && this.runtime && this.runtime.renderer)) return; + if (costume && costume.broken) delete costume.broken; costume.rotationCenterX = rotationCenterX; costume.rotationCenterY = rotationCenterY; @@ -926,6 +948,7 @@ class VirtualMachine extends EventEmitter { */ updateSvg (costumeIndex, svg, rotationCenterX, rotationCenterY) { const costume = this.editingTarget.getCostumes()[costumeIndex]; + if (costume && costume.broken) delete costume.broken; if (costume && this.runtime && this.runtime.renderer) { costume.rotationCenterX = rotationCenterX; costume.rotationCenterY = rotationCenterY; @@ -1093,12 +1116,8 @@ class VirtualMachine extends EventEmitter { return this.runtime && this.runtime.renderer; } - /** - * Set the svg adapter for the VM/runtime, which converts scratch 2 svgs to scratch 3 svgs - * @param {!SvgRenderer} svgAdapter The adapter to attach - */ - attachV2SVGAdapter (svgAdapter) { - this.runtime.attachV2SVGAdapter(svgAdapter); + // @deprecated + attachV2SVGAdapter () { } /** @@ -1182,6 +1201,13 @@ class VirtualMachine extends EventEmitter { } } + /** + * Delete all of the flyout blocks. + */ + clearFlyoutBlocks () { + this.runtime.flyoutBlocks.deleteAllBlocks(); + } + /** * Set an editing target. An editor UI can use this function to switch * between editing different targets, sprites, etc. From a4516850193b0a805f387fb2e8cf939ded42bd49 Mon Sep 17 00:00:00 2001 From: MiroslavDionisiev Date: Tue, 22 Jul 2025 17:59:11 +0300 Subject: [PATCH 1964/1971] chore: [UEPR-282] update package-lock --- package-lock.json | 341 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 323 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2e8498e8da..a88cad13ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3151,6 +3151,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@mediapipe/face_detection": { + "version": "0.4.1646425229", + "resolved": "https://registry.npmjs.org/@mediapipe/face_detection/-/face_detection-0.4.1646425229.tgz", + "integrity": "sha512-aeCN+fRAojv9ch3NXorP6r5tcGVLR3/gC1HmtqB0WEZBRXrdP6/3W/sGR0dHr1iT6ueiK95G9PVjbzFosf/hrg==", + "peer": true + }, "node_modules/@microbit/microbit-universal-hex": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@microbit/microbit-universal-hex/-/microbit-universal-hex-0.2.2.tgz", @@ -6311,6 +6317,267 @@ "node": ">=4" } }, + "node_modules/@tensorflow-models/face-detection": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tensorflow-models/face-detection/-/face-detection-1.0.3.tgz", + "integrity": "sha512-4Ld/vFF8MrdFdrMWhlLKZD4hMW0PNY9OkYeqoCPNZ+LwFyenxAqVaNaWrR8JKp37vw9Nuzp4ILbkal5zPUnA0g==", + "dependencies": { + "rimraf": "^3.0.2", + "tslib": "2.4.0" + }, + "peerDependencies": { + "@mediapipe/face_detection": "~0.4.0", + "@tensorflow/tfjs-backend-webgl": "^4.21.0", + "@tensorflow/tfjs-converter": "^4.21.0", + "@tensorflow/tfjs-core": "^4.21.0" + } + }, + "node_modules/@tensorflow-models/face-detection/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@tensorflow-models/face-detection/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@tensorflow/tfjs": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs/-/tfjs-4.22.0.tgz", + "integrity": "sha512-0TrIrXs6/b7FLhLVNmfh8Sah6JgjBPH4mZ8JGb7NU6WW+cx00qK5BcAZxw7NCzxj6N8MRAIfHq+oNbPUNG5VAg==", + "dependencies": { + "@tensorflow/tfjs-backend-cpu": "4.22.0", + "@tensorflow/tfjs-backend-webgl": "4.22.0", + "@tensorflow/tfjs-converter": "4.22.0", + "@tensorflow/tfjs-core": "4.22.0", + "@tensorflow/tfjs-data": "4.22.0", + "@tensorflow/tfjs-layers": "4.22.0", + "argparse": "^1.0.10", + "chalk": "^4.1.0", + "core-js": "3.29.1", + "regenerator-runtime": "^0.13.5", + "yargs": "^16.0.3" + }, + "bin": { + "tfjs-custom-module": "dist/tools/custom_module/cli.js" + } + }, + "node_modules/@tensorflow/tfjs-backend-cpu": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-cpu/-/tfjs-backend-cpu-4.22.0.tgz", + "integrity": "sha512-1u0FmuLGuRAi8D2c3cocHTASGXOmHc/4OvoVDENJayjYkS119fcTcQf4iHrtLthWyDIPy3JiPhRrZQC9EwnhLw==", + "dependencies": { + "@types/seedrandom": "^2.4.28", + "seedrandom": "^3.0.5" + }, + "engines": { + "yarn": ">= 1.3.2" + }, + "peerDependencies": { + "@tensorflow/tfjs-core": "4.22.0" + } + }, + "node_modules/@tensorflow/tfjs-backend-webgl": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-webgl/-/tfjs-backend-webgl-4.22.0.tgz", + "integrity": "sha512-H535XtZWnWgNwSzv538czjVlbJebDl5QTMOth4RXr2p/kJ1qSIXE0vZvEtO+5EC9b00SvhplECny2yDewQb/Yg==", + "dependencies": { + "@tensorflow/tfjs-backend-cpu": "4.22.0", + "@types/offscreencanvas": "~2019.3.0", + "@types/seedrandom": "^2.4.28", + "seedrandom": "^3.0.5" + }, + "engines": { + "yarn": ">= 1.3.2" + }, + "peerDependencies": { + "@tensorflow/tfjs-core": "4.22.0" + } + }, + "node_modules/@tensorflow/tfjs-converter": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-4.22.0.tgz", + "integrity": "sha512-PT43MGlnzIo+YfbsjM79Lxk9lOq6uUwZuCc8rrp0hfpLjF6Jv8jS84u2jFb+WpUeuF4K33ZDNx8CjiYrGQ2trQ==", + "peerDependencies": { + "@tensorflow/tfjs-core": "4.22.0" + } + }, + "node_modules/@tensorflow/tfjs-core": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-4.22.0.tgz", + "integrity": "sha512-LEkOyzbknKFoWUwfkr59vSB68DMJ4cjwwHgicXN0DUi3a0Vh1Er3JQqCI1Hl86GGZQvY8ezVrtDIvqR1ZFW55A==", + "dependencies": { + "@types/long": "^4.0.1", + "@types/offscreencanvas": "~2019.7.0", + "@types/seedrandom": "^2.4.28", + "@webgpu/types": "0.1.38", + "long": "4.0.0", + "node-fetch": "~2.6.1", + "seedrandom": "^3.0.5" + }, + "engines": { + "yarn": ">= 1.3.2" + } + }, + "node_modules/@tensorflow/tfjs-core/node_modules/@types/offscreencanvas": { + "version": "2019.7.3", + "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz", + "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==" + }, + "node_modules/@tensorflow/tfjs-core/node_modules/node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@tensorflow/tfjs-core/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/@tensorflow/tfjs-core/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/@tensorflow/tfjs-core/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/@tensorflow/tfjs-data": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-data/-/tfjs-data-4.22.0.tgz", + "integrity": "sha512-dYmF3LihQIGvtgJrt382hSRH4S0QuAp2w1hXJI2+kOaEqo5HnUPG0k5KA6va+S1yUhx7UBToUKCBHeLHFQRV4w==", + "dependencies": { + "@types/node-fetch": "^2.1.2", + "node-fetch": "~2.6.1", + "string_decoder": "^1.3.0" + }, + "peerDependencies": { + "@tensorflow/tfjs-core": "4.22.0", + "seedrandom": "^3.0.5" + } + }, + "node_modules/@tensorflow/tfjs-data/node_modules/node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@tensorflow/tfjs-data/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/@tensorflow/tfjs-data/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/@tensorflow/tfjs-data/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/@tensorflow/tfjs-layers": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-layers/-/tfjs-layers-4.22.0.tgz", + "integrity": "sha512-lybPj4ZNj9iIAPUj7a8ZW1hg8KQGfqWLlCZDi9eM/oNKCCAgchiyzx8OrYoWmRrB+AM6VNEeIT+2gZKg5ReihA==", + "peerDependencies": { + "@tensorflow/tfjs-core": "4.22.0" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/core-js": { + "version": "3.29.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.1.tgz", + "integrity": "sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@transifex/api": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/@transifex/api/-/api-7.1.4.tgz", @@ -6569,6 +6836,11 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, "node_modules/@types/markdown-it": { "version": "12.2.3", "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", @@ -6610,6 +6882,30 @@ "undici-types": "~7.8.0" } }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/node-fetch/node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/@types/node-forge": { "version": "1.3.13", "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.13.tgz", @@ -6636,6 +6932,11 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/offscreencanvas": { + "version": "2019.3.0", + "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.3.0.tgz", + "integrity": "sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q==" + }, "node_modules/@types/parse-json": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", @@ -6690,6 +6991,11 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/seedrandom": { + "version": "2.4.34", + "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-2.4.34.tgz", + "integrity": "sha512-ytDiArvrn/3Xk6/vtylys5tlY6eo7Ane0hvcx++TKo6RxQXuVfW0AF/oeWqAj9dN29SyhtawuXstgmPlwNcv/A==" + }, "node_modules/@types/send": { "version": "0.17.5", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", @@ -7223,6 +7529,11 @@ "@xtuc/long": "4.2.2" } }, + "node_modules/@webgpu/types": { + "version": "0.1.38", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.38.tgz", + "integrity": "sha512-7LrhVKz2PRh+DD7+S+PVaFd5HxaWQvoMqBbsV9fNJO1pjUs1P8bM2vQVNfk+3URTqbuTI7gkXi0rfsN0IadoBA==" + }, "node_modules/@webpack-cli/configtest": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", @@ -7533,7 +7844,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -7543,7 +7853,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -10022,7 +10331,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -10323,7 +10631,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -10336,7 +10643,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/color-support": { @@ -12895,7 +13201,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/emojis-list": { @@ -13256,7 +13561,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -16241,7 +16545,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -18654,7 +18957,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -23440,6 +23742,11 @@ "dev": true, "license": "MIT" }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, "node_modules/lookup-closest-locale": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/lookup-closest-locale/-/lookup-closest-locale-6.2.0.tgz", @@ -30909,7 +31216,6 @@ "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true, "license": "MIT" }, "node_modules/regex-cache": { @@ -31324,7 +31630,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -32424,6 +32729,11 @@ "integrity": "sha512-sf7oGoLuaYAScB4VGr0tzetsYlS8EJH6qnTCfQ/WVEa89hALQ4RQfCKt5xCyPQKPDUbVUAIP1QsxAwfAjlDp7Q==", "dev": true }, + "node_modules/seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + }, "node_modules/seek-bzip": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", @@ -33704,7 +34014,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/sshpk": { @@ -34234,7 +34543,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -34347,7 +34655,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -34485,7 +34792,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -39115,7 +39421,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -39302,7 +39607,6 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -39346,7 +39650,6 @@ "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -39407,6 +39710,8 @@ "@scratch/scratch-render": "11.3.0", "@scratch/scratch-svg-renderer": "11.3.0", "@scratch/scratch-vm": "11.3.0", + "@tensorflow-models/face-detection": "^1.0.3", + "@tensorflow/tfjs": "^4.22.0", "arraybuffer-loader": "1.0.8", "autoprefixer": "9.8.8", "balance-text": "3.3.1", From b7ad4c14b9648d0d3ed9758c7c11c0fcac652b47 Mon Sep 17 00:00:00 2001 From: MiroslavDionisiev Date: Wed, 23 Jul 2025 15:10:32 +0300 Subject: [PATCH 1965/1971] feat: [UEPR-289] disconnect project from cloud --- .../scratch-gui/src/lib/cloud-manager-hoc.jsx | 19 ++++++++++++++++--- .../test/unit/util/cloud-manager-hoc.test.jsx | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/packages/scratch-gui/src/lib/cloud-manager-hoc.jsx b/packages/scratch-gui/src/lib/cloud-manager-hoc.jsx index 26ee48c1ff..7a144e0380 100644 --- a/packages/scratch-gui/src/lib/cloud-manager-hoc.jsx +++ b/packages/scratch-gui/src/lib/cloud-manager-hoc.jsx @@ -101,7 +101,11 @@ const cloudManagerHOC = function (WrappedComponent) { handleExtensionAdded (categoryInfo) { // Note that props.vm.extensionManager.isExtensionLoaded('videoSensing') is still false // at the point of this callback, so it is difficult to reuse the canModifyCloudData logic. - if (categoryInfo.id === 'videoSensing' && this.isConnected()) { + if ( + (categoryInfo.id === 'videoSensing' || + categoryInfo.id === 'faceSensing') && + this.isConnected() + ) { this.disconnectFromCloud(); } } @@ -157,9 +161,18 @@ const cloudManagerHOC = function (WrappedComponent) { isShowingWithId: getIsShowingWithId(loadingState), projectId: state.scratchGui.projectState.projectId, // if you're editing someone else's project, you can't modify cloud data - canModifyCloudData: (!state.scratchGui.mode.hasEverEnteredEditor || ownProps.canSave) && + canModifyCloudData: + (!state.scratchGui.mode.hasEverEnteredEditor || + ownProps.canSave) && // possible security concern if the program attempts to encode webcam data over cloud variables - !ownProps.vm.extensionManager.isExtensionLoaded('videoSensing') + !( + ownProps.vm.extensionManager.isExtensionLoaded( + 'videoSensing' + ) || + ownProps.vm.extensionManager.isExtensionLoaded( + 'faceSensing' + ) + ) }; }; diff --git a/packages/scratch-gui/test/unit/util/cloud-manager-hoc.test.jsx b/packages/scratch-gui/test/unit/util/cloud-manager-hoc.test.jsx index cda31e29fb..4267f81f82 100644 --- a/packages/scratch-gui/test/unit/util/cloud-manager-hoc.test.jsx +++ b/packages/scratch-gui/test/unit/util/cloud-manager-hoc.test.jsx @@ -162,6 +162,25 @@ describe('CloudManagerHOC', () => { expect(CloudProvider).not.toHaveBeenCalled(); }); + test('when faceSensing extension is active, the cloud provider is not set on the vm', () => { + const Component = () =>
    ; + const WrappedComponent = cloudManagerHOC(Component); + vm.extensionManager.isExtensionLoaded = jest.fn(extension => extension === 'faceSensing'); + + mount( + + ); + + expect(vm.setCloudProvider.mock.calls.length).toBe(0); + expect(CloudProvider).not.toHaveBeenCalled(); + }); + test('if the isShowingWithId prop becomes true, it sets the cloud provider on the vm', () => { const Component = () =>
    ; const WrappedComponent = cloudManagerHOC(Component); From 5f1eed01e161fda3a3a7f20d40d54a0b3e10268e Mon Sep 17 00:00:00 2001 From: MiroslavDionisiev Date: Thu, 31 Jul 2025 17:11:37 +0300 Subject: [PATCH 1966/1971] fix: [UEPR-282] change field name --- .../scratch-vm/src/extensions/scratch3_face_sensing/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index f3e153ae9f..03e58ea8ed 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -417,7 +417,7 @@ class Scratch3FaceSensingBlocks { whenSpriteTouchesPart (args, util) { if (!this.currentFace) return false; - if (!this.currentFace.landmarks) return false; + if (!this.currentFace.keypoints) return false; const pos = this.getPartPosition(args.PART); return util.target.isTouchingScratchPoint(pos.x, pos.y); } From 809a48e5f604f42bd0c708d1f74662317875a311 Mon Sep 17 00:00:00 2001 From: Ayshe Dzhindzhi Date: Fri, 8 Aug 2025 11:26:02 +0300 Subject: [PATCH 1967/1971] fix: [UEPR-302] Rename face sensing block to nose, instead of noseTip --- .../scratch-vm/src/extensions/scratch3_face_sensing/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index 03e58ea8ed..95beea325a 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -361,7 +361,7 @@ class Scratch3FaceSensingBlocks { ], menus: { PART: [ - {text: 'noseTip', value: '2'}, + {text: 'nose', value: '2'}, {text: 'mouth', value: '3'}, {text: 'left eye', value: '0'}, {text: 'right eye', value: '1'}, From cd4b79974eb82f1aa99ee0f810a2f2d1ec4d7e66 Mon Sep 17 00:00:00 2001 From: Ayshe Dzhindzhi Date: Mon, 11 Aug 2025 17:40:30 +0300 Subject: [PATCH 1968/1971] feat: install mediapipe via npm and add fallback to CDN - Install @mediapipe/face_detection package through npm - Copy mediapipe files into the output folder during build - Add fallback logic to load mediapipe from CDN if local files are missing --- package-lock.json | 3 ++- packages/scratch-gui/package.json | 1 + packages/scratch-gui/webpack.config.js | 4 ++++ .../extensions/scratch3_face_sensing/index.js | 24 +++++++++++++------ 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2d67bce1cb..4c3121bb14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3155,7 +3155,7 @@ "version": "0.4.1646425229", "resolved": "https://registry.npmjs.org/@mediapipe/face_detection/-/face_detection-0.4.1646425229.tgz", "integrity": "sha512-aeCN+fRAojv9ch3NXorP6r5tcGVLR3/gC1HmtqB0WEZBRXrdP6/3W/sGR0dHr1iT6ueiK95G9PVjbzFosf/hrg==", - "peer": true + "license": "Apache-2.0" }, "node_modules/@microbit/microbit-universal-hex": { "version": "0.2.2", @@ -39706,6 +39706,7 @@ "version": "11.6.0-gui-standalone", "license": "AGPL-3.0-only", "dependencies": { + "@mediapipe/face_detection": "0.4.1646425229", "@microbit/microbit-universal-hex": "0.2.2", "@scratch/scratch-render": "11.6.0-gui-standalone", "@scratch/scratch-svg-renderer": "11.6.0-gui-standalone", diff --git a/packages/scratch-gui/package.json b/packages/scratch-gui/package.json index c2ef7022dd..65da82a5e3 100644 --- a/packages/scratch-gui/package.json +++ b/packages/scratch-gui/package.json @@ -57,6 +57,7 @@ "watch": "webpack --watch" }, "dependencies": { + "@mediapipe/face_detection": "0.4.1646425229", "@microbit/microbit-universal-hex": "0.2.2", "@tensorflow-models/face-detection": "^1.0.3", "@tensorflow/tfjs": "^4.22.0", diff --git a/packages/scratch-gui/webpack.config.js b/packages/scratch-gui/webpack.config.js index 07277d0329..d0310a96d7 100644 --- a/packages/scratch-gui/webpack.config.js +++ b/packages/scratch-gui/webpack.config.js @@ -84,6 +84,10 @@ const baseConfig = new ScratchWebpackConfigBuilder( context: '../../node_modules/scratch-storage/dist/web', from: 'chunks/fetch-worker.*.{js,js.map}', noErrorOnMissing: true + }, + { + from: '../../node_modules/@mediapipe/face_detection', + to: 'chunks/mediapipe/face_detection' } ] })); diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index 03e58ea8ed..00c48e799b 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -41,16 +41,26 @@ class Scratch3FaceSensingBlocks { const model = FaceDetection.SupportedModels.MediaPipeFaceDetector; const detectorConfig = { runtime: 'mediapipe', - solutionPath: 'https://cdn.jsdelivr.net/npm/@mediapipe/face_detection', + solutionPath: '/chunks/mediapipe/face_detection', maxFaces: 1 }; - FaceDetection.createDetector(model, detectorConfig).then(detector => { - this.faceDetector = detector; - if (this.runtime.ioDevices) { - this._loop(); - } - }); + FaceDetection.createDetector(model, detectorConfig) + .catch(() => { + const fallbackConfig = { + runtime: 'mediapipe', + solutionPath: 'https://cdn.jsdelivr.net/npm/@mediapipe/face_detection@0.4.1646425229', + maxFaces: 1 + }; + + return FaceDetection.createDetector(model, fallbackConfig); + }) + .then(detector => { + this.faceDetector = detector; + if (this.runtime.ioDevices) { + this._loop(); + } + }); this.cachedSize = 100; this.cachedTilt = 90; From 38084d01f072a4334c526b071a9b27f4b71e5958 Mon Sep 17 00:00:00 2001 From: Ayshe Dzhindzhi Date: Tue, 12 Aug 2025 13:11:36 +0300 Subject: [PATCH 1969/1971] fix: use mediapipe version from the package.json instead of hardcoding it --- .../scratch-vm/src/extensions/scratch3_face_sensing/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index 00c48e799b..89d0a4b408 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -8,6 +8,7 @@ const TargetType = require('../../extension-support/target-type'); // const Posenet = require('@tensorflow-models/posenet'); const FaceDetection = require('@tensorflow-models/face-detection'); +const mediapipePackage = require('@mediapipe/face_detection/package.json'); /** * Icon svg to be displayed in the blocks category menu, encoded as a data URI. @@ -49,7 +50,7 @@ class Scratch3FaceSensingBlocks { .catch(() => { const fallbackConfig = { runtime: 'mediapipe', - solutionPath: 'https://cdn.jsdelivr.net/npm/@mediapipe/face_detection@0.4.1646425229', + solutionPath: `https://cdn.jsdelivr.net/npm/@mediapipe/face_detection@${mediapipePackage.version}`, maxFaces: 1 }; From 7fb6d8d036551cb62c1719617220a8a0e9cc92df Mon Sep 17 00:00:00 2001 From: Ayshe Dzhindzhi Date: Wed, 13 Aug 2025 11:15:38 +0300 Subject: [PATCH 1970/1971] fix: use a different icon for the face sensing extension in high contrast mode --- .../themes/high-contrast/extensions/faceSensingIcon.svg | 9 +++++++++ .../scratch-gui/src/lib/themes/high-contrast/index.js | 4 ++++ 2 files changed, 13 insertions(+) create mode 100644 packages/scratch-gui/src/lib/themes/high-contrast/extensions/faceSensingIcon.svg diff --git a/packages/scratch-gui/src/lib/themes/high-contrast/extensions/faceSensingIcon.svg b/packages/scratch-gui/src/lib/themes/high-contrast/extensions/faceSensingIcon.svg new file mode 100644 index 0000000000..0b20b4921d --- /dev/null +++ b/packages/scratch-gui/src/lib/themes/high-contrast/extensions/faceSensingIcon.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/packages/scratch-gui/src/lib/themes/high-contrast/index.js b/packages/scratch-gui/src/lib/themes/high-contrast/index.js index 8a5dd94155..419229f72d 100644 --- a/packages/scratch-gui/src/lib/themes/high-contrast/index.js +++ b/packages/scratch-gui/src/lib/themes/high-contrast/index.js @@ -3,6 +3,7 @@ import penIcon from './extensions/penIcon.svg'; import text2speechIcon from './extensions/text2speechIcon.svg'; import translateIcon from './extensions/translateIcon.svg'; import videoSensingIcon from './extensions/videoSensingIcon.svg'; +import faceSensingIcon from './extensions/faceSensingIcon.svg'; const blockColors = { motion: { @@ -101,6 +102,9 @@ const extensions = { }, videoSensing: { blockIconURI: videoSensingIcon + }, + faceSensing: { + blockIconURI: faceSensingIcon } }; From 1b7d02835cd3d91c96d06e48612ab9cbf3741b30 Mon Sep 17 00:00:00 2001 From: Ayshe Dzhindzhi Date: Wed, 13 Aug 2025 11:17:12 +0300 Subject: [PATCH 1971/1971] fix: replace the default face sensing block icon with the one without background --- .../scratch-vm/src/extensions/scratch3_face_sensing/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js index 03e58ea8ed..67680982ed 100644 --- a/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js +++ b/packages/scratch-vm/src/extensions/scratch3_face_sensing/index.js @@ -21,7 +21,7 @@ const menuIconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iN * @type {string} */ // eslint-disable-next-line max-len -const blockIconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3R5bGU9ImJhY2tncm91bmQ6IzBmYmQ4YyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBmaWxsPSIjMEZCRDhDIiBkPSJNMCAwaDQwdjQwSDB6Ii8+PGNpcmNsZSBmaWxsPSIjRkZGIiBjeD0iMTUuNSIgY3k9IjE3LjUiIHI9IjEuNSIvPjxjaXJjbGUgZmlsbD0iI0ZGRiIgY3g9IjI0LjUiIGN5PSIxNy41IiByPSIxLjUiLz48cGF0aCBkPSJNMjAgOUMxMy45MjUgOSA5IDEzLjkyNSA5IDIwczQuOTI1IDExIDExIDExIDExLTQuOTI1IDExLTExUzI2LjA3NSA5IDIwIDl6bTAgMmE5IDkgMCAxMTAgMTggOSA5IDAgMDEwLTE4eiIgZmlsbD0iI0ZGRiIgZmlsbC1ydWxlPSJub256ZXJvIi8+PHBhdGggZD0iTTM1IDRhMSAxIDAgMDEuOTkzLjg4M0wzNiA1djZhMSAxIDAgMDEtMS45OTMuMTE3TDM0IDExVjZoLTVhMSAxIDAgMDEtLjk5My0uODgzTDI4IDVhMSAxIDAgMDEuODgzLS45OTNMMjkgNGg2ek01IDM2YTEgMSAwIDAxLS45OTMtLjg4M0w0IDM1di02YTEgMSAwIDAxMS45OTMtLjExN0w2IDI5djVoNWExIDEgMCAwMS45OTMuODgzTDEyIDM1YTEgMSAwIDAxLS44ODMuOTkzTDExIDM2SDV6IiBmaWxsLW9wYWNpdHk9Ii4yNSIgZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJub256ZXJvIi8+PHBhdGggZD0iTTI5LjcyIDI0LjAyOGEyLjU1NyAyLjU1NyAwIDAwMS44MDgtMS44MDhsLjU0NC0yLjAwOWMuMjUyLS45NDggMS42LS45NDggMS44NTYgMGwuNTQgMi4wMDlhMi41NjMgMi41NjMgMCAwMDEuODEzIDEuODA4bDIuMDA4LjU0NGMuOTQ4LjI1Mi45NDggMS42IDAgMS44NTdsLTIuMDA4LjU0YTIuNTYzIDIuNTYzIDAgMDAtMS44MTMgMS44MDhsLS41NCAyLjAwOWMtLjI1Ni45NTItMS42MDQuOTUyLTEuODU2IDBsLS41NDQtMi4wMDlhMi41NTcgMi41NTcgMCAwMC0xLjgwOS0xLjgwOGwtMi4wMDgtLjU0Yy0uOTQ4LS4yNTYtLjk0OC0xLjYwNSAwLTEuODU3bDIuMDA4LS41NDR6TTUuMDQgNi4zOTZBMS45MTggMS45MTggMCAwMDYuMzk2IDUuMDRsLjQwOC0xLjUwN2MuMTg5LS43MSAxLjItLjcxIDEuMzkyIDBsLjQwNSAxLjUwN2ExLjkyMiAxLjkyMiAwIDAwMS4zNiAxLjM1NmwxLjUwNi40MDhjLjcxLjE5LjcxIDEuMiAwIDEuMzkzbC0xLjUwNy40MDVhMS45MjIgMS45MjIgMCAwMC0xLjM1OSAxLjM1NmwtLjQwNSAxLjUwNmMtLjE5Mi43MTUtMS4yMDMuNzE1LTEuMzkyIDBsLS40MDgtMS41MDZBMS45MTggMS45MTggMCAwMDUuMDQgOC42MDJsLTEuNTA3LS40MDVjLS43MS0uMTkyLS43MS0xLjIwNCAwLTEuMzkzbDEuNTA3LS40MDh6IiBmaWxsPSIjRkZCRjAwIi8+PHBhdGggZD0iTTIyLjE2OCAyMS45NDVhMSAxIDAgMTExLjY2NCAxLjExQzIyLjk3NCAyNC4zNDIgMjEuNjU4IDI1IDIwIDI1cy0yLjk3NC0uNjU4LTMuODMyLTEuOTQ1YTEgMSAwIDExMS42NjQtMS4xMUMxOC4zMDcgMjIuNjU4IDE4Ljk5MiAyMyAyMCAyM2MxLjAwOSAwIDEuNjkzLS4zNDIgMi4xNjgtMS4wNTV6IiBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48cGF0aCBkPSJNMzEuNTg5IDIwLjA4M2wtLjU0NCAyLjAwNmEyLjA1OCAyLjA1OCAwIDAxLTEuNDU3IDEuNDU3bC0yLjAwOC41NDRjLTEuNDQuMzgzLTEuNDQgMi40MzIgMCAyLjgyMWwyLjAxLjU0Yy43MS4xOSAxLjI2NC43NDYgMS40NTUgMS40NTZsLjU0NCAyLjAxYy4zODMgMS40NDUgMi40MzMgMS40NDUgMi44MjItLjAwMWwuNTQtMi4wMDlhMi4wNjMgMi4wNjMgMCAwMTEuNDU5LTEuNDU1bDIuMDA5LS41NGMxLjQ0Mi0uMzkgMS40NDItMi40NC0uMDAyLTIuODIzbC0yLjAwNi0uNTQzYTIuMDYyIDIuMDYyIDAgMDEtMS40Ni0xLjQ1NWwtLjU0LTIuMDFjLS4zOS0xLjQ0Mi0yLjQzOS0xLjQ0Mi0yLjgyMi4wMDJ6bTEuODU2LjI1OWwuNTQgMi4wMDhhMy4wNjIgMy4wNjIgMCAwMDIuMTY1IDIuMTZsMi4wMDguNTQ1Yy40NTYuMTIuNDU2Ljc2OCAwIC44OTFsLTIuMDA3LjU0YTMuMDYyIDMuMDYyIDAgMDAtMi4xNjYgMi4xNjJsLS41NCAyLjAwOGMtLjEyMy40NTgtLjc2OS40NTgtLjg5LjAwMmwtLjU0NS0yLjAxMWEzLjA1NyAzLjA1NyAwIDAwLTIuMTYyLTIuMTYxbC0yLjAwNy0uNTRjLS40NTUtLjEyMy0uNDU1LS43Ny0uMDAxLS44OWwyLjAxLS41NDVhMy4wNTcgMy4wNTcgMCAwMDIuMTYtMi4xNjJsLjU0NC0yLjAwN2MuMTIyLS40NTYuNzY5LS40NTYuODkxIDB6IiBmaWxsLW9wYWNpdHk9Ii41IiBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48cGF0aCBkPSJNNi4zMiAzLjQwNWwtLjQwNyAxLjUwNGMtLjEzLjQ5LS41MTEuODctMS4wMDQgMS4wMDVsLTEuNTA2LjQwOGMtMS4yMDQuMzItMS4yMDQgMi4wMzIgMCAyLjM1N2wxLjUwNy40MDVjLjQ5LjEzMS44NzIuNTE0IDEuMDAzIDEuMDAzbC40MDggMS41MDhjLjMyIDEuMjA3IDIuMDMzIDEuMjA3IDIuMzU4IDBsLjQwNS0xLjUwN2ExLjQyMiAxLjQyMiAwIDAxMS4wMDUtMS4wMDNsMS41MDgtLjQwNmMxLjIwNC0uMzI1IDEuMjA0LTIuMDM3LS4wMDItMi4zNThsLTEuNTA0LS40MDhhMS40MjIgMS40MjIgMCAwMS0xLjAwNy0xLjAwMkw4LjY4IDMuNDAzYy0uMzI1LTEuMjA0LTIuMDM4LTEuMjA0LTIuMzU4LjAwMnptMS4zOTMuMjU5bC40MDUgMS41MDZBMi40MjEgMi40MjEgMCAwMDkuODMgNi44NzlsMS41MDcuNDA4Yy4yMTguMDU4LjIxOC4zNjggMCAuNDI3bC0xLjUwNS40MDVhMi40MjIgMi40MjIgMCAwMC0xLjcxMyAxLjcxbC0uNDA1IDEuNTA2Yy0uMDU5LjIyLS4zNjguMjItLjQyNi4wMDFsLS40MDktMS41MDlhMi40MTcgMi40MTcgMCAwMC0xLjcxLTEuNzA4bC0xLjUwNS0uNDA1Yy0uMjE3LS4wNTktLjIxNy0uMzctLjAwMS0uNDI3TDUuMTcgNi44OGEyLjQxOCAyLjQxOCAwIDAwMS43MDktMS43MWwuNDA3LTEuNTA1Yy4wNTktLjIxOC4zNjktLjIxOC40MjcgMHoiIGZpbGwtb3BhY2l0eT0iLjQiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvZz48L3N2Zz4='; +const blockIconURI = 'data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjQwIiB3aWR0aD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDIzLjg0IDIxLjQ2Ij4KICAgIDxjaXJjbGUgZmlsbD0iI2ZmZiIgY3g9IjguMzUiIGN5PSI5LjY1IiByPSIuOTciLz4KICAgIDxjaXJjbGUgZmlsbD0iI2ZmZiIgY3g9IjE0LjE5IiBjeT0iOS42NSIgcj0iLjk3Ii8+CiAgICA8cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTEuMjcsNC4xNGMtMy45NCwwLTcuMTMsMy4xOS03LjEzLDcuMTNzMy4xOSw3LjEzLDcuMTMsNy4xMyw3LjEzLTMuMTksNy4xMy03LjEzLTMuMTktNy4xMy03LjEzLTcuMTNaTTExLjI3LDUuNDRjMy4yMiwwLDUuODQsMi42MSw1Ljg0LDUuODRzLTIuNjEsNS44NC01Ljg0LDUuODQtNS44NC0yLjYxLTUuODQtNS44NCwyLjYxLTUuODQsNS44NC01Ljg0WiIvPgogICAgPHBhdGggZmlsbD0iI2ZmYmYwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwYjhlNjkiIHN0cm9rZS1taXRlcmxpbWl0PSIyIiBzdHJva2Utd2lkdGg9Ii41cHgiIGQ9Ik0xNy41NywxMy44OGMuNTctLjE1LDEuMDItLjYsMS4xNy0xLjE3bC4zNS0xLjNjLjE2LS42MSwxLjA0LS42MSwxLjIsMGwuMzUsMS4zYy4xNS41Ny42LDEuMDIsMS4xOCwxLjE3bDEuMy4zNWMuNjEuMTYuNjEsMS4wNCwwLDEuMmwtMS4zLjM1Yy0uNTcuMTUtMS4wMi42LTEuMTgsMS4xN2wtLjM1LDEuM2MtLjE3LjYyLTEuMDQuNjItMS4yLDBsLS4zNS0xLjNjLS4xNS0uNTctLjYtMS4wMi0xLjE3LTEuMTdsLTEuMy0uMzVjLS42MS0uMTctLjYxLTEuMDQsMC0xLjJsMS4zLS4zNWgwWk0xLjU3LDIuNDVjLjQzLS4xMi43Ni0uNDUuODgtLjg4bC4yNi0uOThjLjEyLS40Ni43OC0uNDYuOSwwbC4yNi45OGMuMTIuNDMuNDUuNzYuODguODhsLjk4LjI2Yy40Ni4xMi40Ni43OCwwLC45bC0uOTguMjZjLS40My4xMS0uNzcuNDUtLjg4Ljg4bC0uMjYuOThjLS4xMi40Ni0uNzguNDYtLjksMGwtLjI2LS45OGMtLjEyLS40My0uNDUtLjc2LS44OC0uODhsLS45OC0uMjZjLS40Ni0uMTItLjQ2LS43OCwwLS45bC45OC0uMjZaIi8+CiAgICA8cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTIuNjgsMTIuNTNjLjItLjMuNi0uMzguOS0uMTguMy4yLjM4LjYuMTguOS0uNTYuODMtMS40MSwxLjI2LTIuNDgsMS4yNnMtMS45My0uNDMtMi40OC0xLjI2Yy0uMi0uMy0uMTItLjcuMTgtLjkuMy0uMi43LS4xMi45LjE4LjMxLjQ2Ljc1LjY4LDEuNDEuNjhzMS4xLS4yMiwxLjQxLS42OFoiLz4KICAgIDxwYXRoIGZpbGw9IiMwYjhlNjkiIGQ9Ik0yMC44OSw2LjA2Yy0uMzEsMC0uNTctLjI1LS41Ny0uNTd2LTMuMjloLTMuMzFjLS4zMSwwLS41Ny0uMjUtLjU3LS41N3MuMjUtLjU3LjU3LS41N2gzLjg4Yy4zMSwwLC41Ny4yNS41Ny41N3YzLjg2YzAsLjMxLS4yNS41Ny0uNTcuNTdaIi8+CiAgICA8cGF0aCBmaWxsPSIjMGI4ZTY5IiBkPSJNNS40NCwyMS40NkgxLjU5Yy0uMzEsMC0uNTctLjI1LS41Ny0uNTd2LTMuODJjMC0uMzEuMjUtLjU3LjU3LS41N3MuNTcuMjUuNTcuNTd2My4yNWgzLjI4Yy4zMSwwLC41Ny4yNS41Ny41N3MtLjI1LjU3LS41Ny41N1oiLz4KPC9zdmc+Cg=='; /** * Class for the motion-related blocks in Scratch 3.0