Compare commits

..
Author SHA1 Message Date
Paul Kaplan b4a0879584 Merge pull request #4953 from LLK/paulkaplan-patch-1
Bump l10n version
2019-06-26 13:59:33 -04:00
Paul Kaplan 8274a8c6c7 Bump l10n version 2019-06-26 13:59:19 -04:00
Paul Kaplan cdffd567b6 Merge pull request #4952 from LLK/paulkaplan-patch-1
Update blocks manually
2019-06-26 12:59:22 -04:00
Paul Kaplan c7003b4765 Update blocks manually
Seems like greenkeeper isn't keeping up?
2019-06-26 12:57:58 -04:00
Paul Kaplan 4e3f2160ae Merge pull request #4938 from LLK/touch-share
Allow sharing blocks/costumes/sounds on touch devices
2019-06-26 12:55:33 -04:00
Paul Kaplan 095fea939d Merge pull request #4941 from paulkaplan/proper-keys
Fix library images lagging behind filter changes
2019-06-26 12:53:44 -04:00
Paul Kaplan 26e4bc2ad2 Fix merge conflicts 2019-06-26 10:56:54 -04:00
Paul Kaplan 18241a5cbd Merge branch 'develop' into touch-share 2019-06-26 10:35:13 -04:00
Paul Kaplan d8cd3e38c7 Merge pull request #4931 from paulkaplan/fix-wistia-crash
Prevent crash when wistia API takes a while to load
2019-06-26 10:28:32 -04:00
Paul Kaplan 0bbb11012d Merge pull request #4935 from LLK/drag-angle
Allow vertical scrolling of sprite tile pane on touch devices
2019-06-24 14:05:32 -04:00
Paul Kaplan 212c998980 Merge pull request #4947 from LLK/update-paint
Bump scratch-paint version
2019-06-24 10:30:36 -04:00
Paul Kaplan e359efd97e Merge pull request #4948 from LLK/paulkaplan-patch-1
Fix PropType on Loupe component
2019-06-24 10:30:19 -04:00
Paul Kaplan 547640bfcc Add sprite prop to proptypes 2019-06-24 10:11:17 -04:00
Paul Kaplan d7a2264c0d Fix proptypes on Loupe component 2019-06-24 09:57:17 -04:00
Paul Kaplan 0bb91add74 Bump scratch-paint version 2019-06-24 09:42:09 -04:00
Paul Kaplan cea2dd9ab9 Fix spacing 2019-06-21 09:45:23 -04:00
Paul Kaplan c424f25da1 Fix proptypes 2019-06-21 09:33:26 -04:00
Paul Kaplan 7c1931749c Use name or url for library keys 2019-06-21 09:19:43 -04:00
Paul Kaplan 4708ccc7dc Be defensive about using the Wistia API 2019-06-21 09:19:18 -04:00
Paul Kaplan 9e0a239ec8 Merge pull request #4932 from paulkaplan/fix-tutorial-image-sync
Prevent previous tutorial images from hanging around on next/prev
2019-06-21 09:05:28 -04:00
Paul Kaplan 8a2c001531 Merge pull request #4921 from paulkaplan/on-vm-init
Add VM init callback
2019-06-21 09:05:10 -04:00
Paul Kaplan 044b958e08 Add vm init callback 2019-06-21 08:48:53 -04:00
Paul Kaplan a2bbed65d5 Force img node change to prevent previous tutorial images from hanging around 2019-06-21 08:41:01 -04:00
picklesrus cf62045e26 Merge pull request #4942 from LLK/revert-4940-revert-4790
Revert "Revert "Merge pull request #4790 from LLK/e16n""
2019-06-20 17:53:23 -04:00
chrisgarrity 164ddbe4f4 Revert "Revert "Merge pull request #4790 from LLK/e16n"" 2019-06-20 17:12:59 -04:00
picklesrus 6b5eb032ad Merge pull request #4940 from picklesrus/revert-4790
Revert "Merge pull request #4790 from LLK/e16n"
2019-06-20 11:40:30 -04:00
picklesrus 8d3a85860e Revert "Merge pull request #4790 from LLK/e16n"
This reverts commit c061f2e2f0, reversing
changes made to 6e007b1e4a.
2019-06-20 11:21:56 -04:00
Benjamin Wheeler cbb265e9f0 Merge pull request #4934 from benjiwheeler/remove-example-test
skip example change categories test
2019-06-19 18:13:05 -04:00
Eric Rosenbaum 948fb2f747 Merge pull request #4937 from zoebentley/tutorial-updates
Make Updates to Tutorials Library
2019-06-19 16:54:05 -04:00
Zoë Bentley b0ca6dfcaa Lint 2019-06-19 16:22:34 -04:00
Zoë Bentley 61eb31ea66 Optimize .png images 2019-06-19 15:53:48 -04:00
Paul Kaplan eed4c36b7b Emit hover on touch end to make sharing to sprites work on touch 2019-06-19 15:41:59 -04:00
Zoë Bentley b5a3a53780 Added 3 new tutorials, added pngs to tutorials that only had videos, reordered tutorials in library 2019-06-19 15:22:50 -04:00
Eric Rosenbaum a6302ecc14 Merge pull request #4936 from LLK/ericrosenbaum-patch-1
Update l10n
2019-06-19 15:04:01 -04:00
Eric Rosenbaum 2a9d74adb4 Update l10n 2019-06-19 14:45:01 -04:00
Eric Rosenbaum e5772a5902 Merge pull request #4922 from ericrosenbaum/feature/acw-assets
Updates to sprite and costume libraries
2019-06-19 11:36:51 -04:00
Paul Kaplan abdeb8dbdb Fix context menu coming up when dragging 2019-06-19 11:27:00 -04:00
Paul Kaplan 8611c9ff1a Refactor gesture recognizer with tests 2019-06-19 11:26:42 -04:00
Ben Wheeler f7244e6c89 skip example change categories test 2019-06-19 11:25:07 -04:00
Paul Kaplan 4b7f72c00c Only start tile drag for certain angles 2019-06-19 09:22:05 -04:00
Christopher Willis-Ford 1476c88a54 update VM for non-droppable extension menu support 2019-06-18 21:27:42 -07:00
Chris Willis-Ford c061f2e2f0 Merge pull request #4790 from LLK/e16n
Supporting GUI changes for extensionification
2019-06-18 16:22:25 -07:00
Chris Willis-Ford dc49c0ebe4 Merge branch 'develop' into e16n 2019-06-18 16:06:46 -07:00
Christopher Willis-Ford 11d4273982 update VM to an e16n-compatible version 2019-06-18 16:02:28 -07:00
Christopher Willis-Ford 7755f76215 simplify MockBlock construction 2019-06-18 14:15:43 -07:00
Christopher Willis-Ford 6e44bfea95 fix lint errors in define-dynamic-block.test.js 2019-06-18 11:37:17 -07:00
Christopher Willis-Ford fa8ceacbcf add comments suggested in code review 2019-06-18 11:31:53 -07:00
Christopher Willis-Ford d19c468336 allow per-block colors 2019-06-18 11:24:29 -07:00
Chris Willis-FordandKarishma Chadha 86edd829c0 Apply suggestions from code review
Comments slightly tweaked from kchadha's suggestions

Co-Authored-By: Karishma Chadha <kchadha@media.mit.edu>
2019-06-18 11:21:03 -07:00
Eric Rosenbaum bdc9c1e7de Update sprite and costume libraries 2019-06-17 17:56:28 -04:00
chrisgarrity 6e007b1e4a Merge pull request #4917 from chrisgarrity/update-l10n
Update l10n to the latest version
2019-06-12 12:05:32 -04:00
Chris Garrity 356c1d9811 Update l10n to the latest version 2019-06-12 11:47:38 -04:00
picklesrus 723a2bf117 Merge pull request #4913 from LLK/picklesrus-patch-1
Update scratch-vm
2019-06-10 13:22:50 -04:00
picklesrus 7b7e5e6e72 Update scratch-vm
Greenkeeper didn't do this for me... not sure why.
2019-06-10 11:59:41 -04:00
Paul Kaplan 3fcbf74c71 Merge pull request #4909 from LLK/progressive-libraries
Show libraries before loading their content
2019-06-10 09:29:56 -04:00
Paul Kaplan 8f4a6ce255 Fix proptype sorting 2019-06-07 09:19:26 -04:00
Paul Kaplan 9e0853aa9a Merge branch 'develop' of github.com:LLK/scratch-gui into progressive-libraries 2019-06-07 09:06:39 -04:00
Paul Kaplan c4480b9ade Show spinner before loading library content 2019-06-07 09:05:56 -04:00
Paul Kaplan 1a588880bf Merge pull request #4901 from paulkaplan/no-videos-on-desktop
Use a "title card" instead of video if using Scratch Desktop
2019-06-05 16:10:16 -04:00
Paul Kaplan e2c2392a70 Only show items in the library that would have image steps for offline
Because the video steps get replaced by an image step if offline, we should not show items that would result in having no content
2019-06-05 15:09:28 -04:00
Paul Kaplan 5160b5ae22 Make the cards images all the same size, and shrink to fit centered 2019-06-05 15:08:32 -04:00
Karishma Chadha 8be847b68a Merge pull request #4904 from LLK/kchadha-update-render
Update scratch-render
2019-06-05 14:51:29 -04:00
Karishma Chadha e7ed24f1ef Update scratch-render 2019-06-05 14:50:57 -04:00
Paul Kaplan 13e324b52d Use title card instead of video if using scratchDesktop flag 2019-06-05 12:53:32 -04:00
Karishma Chadha 5c7b221799 Merge pull request #4900 from LLK/kchadha-update-deps
Update scratch-vm and scratch-l10n.
2019-06-05 11:51:25 -04:00
Karishma Chadha 7398a30223 Update scratch-vm and scratch-l10n. 2019-06-05 11:34:33 -04:00
Paul Kaplan 637b9fd225 Merge pull request #4832 from paulkaplan/do-not-select-stage
Make surprise and library backdrop additions NOT select the stage
2019-06-05 10:45:34 -04:00
Benjamin Wheeler 88442708c2 Merge pull request #4883 from benjiwheeler/fix-project-loading-tests
fixed project loading tests
2019-06-05 10:35:26 -04:00
Paul Kaplan d7759317c4 Merge pull request #4896 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190604153446
chore(package): update scratch-vm to version 0.2.0-prerelease.2019060…
2019-06-04 14:10:38 -04:00
Ben Wheeler fd3045eb9d also turn off examples tests that load remote project 2019-06-04 11:40:03 -04:00
greenkeeper[bot] 09ae22f329 chore(package): update scratch-vm to version 0.2.0-prerelease.20190604153446
Closes #4894
2019-06-04 15:37:41 +00:00
Benjamin Wheeler 365e3b3bce Merge pull request #4881 from LLK/revert-4877-revert-4835-bugfix/4766
Revert "Revert "Pause video on tutorial window shrink""
2019-06-03 16:16:01 -04:00
Christopher Willis-Ford a6fe8585fb add unit tests for defineDynamicBlock 2019-06-03 13:04:15 -07:00
Christopher Willis-Ford 930b2ec05c setOutput takes a Boolean, not a type 2019-06-03 13:04:15 -07:00
Christopher Willis-Ford c9d1535967 move defineDynamicBlock into its own file 2019-06-03 13:04:15 -07:00
Evelyn Eastmond 550849c93f Reset video if a new one has been chosen. Added some comments. 2019-05-30 22:03:56 -04:00
Ben Wheeler 8128902cd6 fixed project loading test 2019-05-30 18:52:33 -04:00
Evelyn Eastmond b350049ca7 Testing a commit onto upstream branch for revert PR. 2019-05-30 17:17:01 -04:00
Evelyn Eastmond e0e07929e8 Revert "Revert "Pause video on tutorial window shrink"" 2019-05-30 17:10:58 -04:00
Benjamin Wheeler 128b196220 Merge pull request #4878 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190530160621
Update scratch-render to the latest version 🚀
2019-05-30 12:23:54 -04:00
greenkeeper[bot] 5b806dff27 chore(package): update scratch-render to version 0.1.0-prerelease.20190530160621 2019-05-30 16:13:40 +00:00
Benjamin Wheeler 9c5d358c3e Merge pull request #4877 from LLK/revert-4835-bugfix/4766
Revert "Pause video on tutorial window shrink"
2019-05-30 12:01:22 -04:00
Benjamin Wheeler ab89ee1a4f Revert "Pause video on tutorial window shrink" 2019-05-30 11:59:45 -04:00
Paul Kaplan dc82e5f653 Merge pull request #4873 from LLK/revert-4800-delay-gui
Revert "Delay loading and rendering gui"
2019-05-30 11:35:57 -04:00
Paul Kaplan 7fb3a81a66 Merge pull request #4874 from LLK/revert-4677-update-toolbox
Revert "render only new toolbox xmls"
2019-05-30 11:31:30 -04:00
Paul Kaplan d958b14b8d Revert "render only new toolbox xmls" 2019-05-30 10:50:41 -04:00
Paul Kaplan 0a6c75eb93 Revert "Delay loading and rendering gui" 2019-05-30 09:41:05 -04:00
Colby Gutierrez-Kraybill 7c5a4fc682 Merge pull request #4870 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190524155459
chore(package): update scratch-vm to version 0.2.0-prerelease.2019052…
2019-05-29 15:46:39 -04:00
Bryce Taylor ca8caa8504 Merge pull request #4871 from BryceLTaylor/Stabilize-Integration-Tests
Stabilize integration tests
2019-05-29 15:40:02 -04:00
Benjamin Wheeler 339dfe0b08 Merge pull request #4846 from paulkaplan/update-builtin-assets
Update the built-in SVG assets for the default project
2019-05-29 15:11:15 -04:00
BryceLTaylor b9cefd325b Remove unused reference to findByText in integration tests 2019-05-29 14:45:57 -04:00
BryceLTaylor fa974c68bd Remove WaitUntilGone function from integration tests
These were causing the tests to fail and should be fixed by the update to FindByXpath function.
2019-05-29 14:23:50 -04:00
BryceLTaylor 3c47e0e040 Make Find by Xpath ensure element is visible in integration tests 2019-05-29 14:23:50 -04:00
Colby Gutierrez-Kraybill 34a9a97201 Merge pull request #4868 from LLK/greenkeeper/scratch-l10n-3.3.20190529144051
Update scratch-l10n to the latest version 🚀
2019-05-29 13:23:10 -04:00
Colby Gutierrez-Kraybill f6847e3aea Merge branch 'develop' into greenkeeper/scratch-l10n-3.3.20190529144051 2019-05-29 13:08:47 -04:00
Evelyn Eastmond e27021cbf4 Merge pull request #4835 from evhan55/bugfix/4766
Pause video on tutorial window shrink
2019-05-29 12:39:50 -04:00
Colby Gutierrez-Kraybill 3dc6413ea4 Merge pull request #4867 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1559136797
Update scratch-blocks to the latest version 🚀
2019-05-29 11:38:53 -04:00
greenkeeper[bot] 53c04a1ad9 chore(package): update scratch-l10n to version 3.3.20190529144051 2019-05-29 14:45:06 +00:00
Evelyn Eastmond b2486380fb Disable dragging on video to allow for scrobbling. 2019-05-29 09:46:08 -04:00
greenkeeper[bot] 9f41ddaaa2 chore(package): update scratch-blocks to version 0.1.0-prerelease.1559136797 2019-05-29 13:35:43 +00:00
Paul Kaplan ef4d8c6e8d Merge pull request #4852 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1558719162
Update scratch-blocks to the latest version 🚀
2019-05-29 08:34:07 -04:00
Paul Kaplan a6245b87f6 Merge pull request #4866 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190528231524
chore(package): update scratch-render to version 0.1.0-prerelease.201…
2019-05-29 08:33:43 -04:00
Evelyn Eastmond fa4541e85c Adding dynamic removal of dynamically loaded scripts. 2019-05-29 08:27:20 -04:00
Evelyn Eastmond 376f5626a4 Remove auto-play on expansion. 2019-05-28 20:34:14 -04:00
Evelyn Eastmond e17dc90983 Fix Wistia access to use window. 2019-05-28 20:34:14 -04:00
Evelyn Eastmond a63385343a Remove useless props constructor. 2019-05-28 20:34:14 -04:00
Evelyn Eastmond 3c3206d8e2 Remove bindAll import. 2019-05-28 20:34:14 -04:00
Evelyn Eastmond f6c8feae6e Video play/pause test on video step expand/shrink. 2019-05-28 20:34:14 -04:00
Evelyn Eastmond 98a007a17e Pause video on component update if not expanded. 2019-05-28 20:34:14 -04:00
Evelyn Eastmond 3e0d5b9ede Clean up and get handle on video on load. 2019-05-28 20:34:14 -04:00
Evelyn Eastmond 555082bb1d Change Wistia embed so video API is accessible from current window. 2019-05-28 20:34:14 -04:00
Evelyn Eastmond 5c95c352c3 Trying VideoStep component. 2019-05-28 20:34:14 -04:00
greenkeeper[bot] 3b4758ca5c chore(package): update scratch-render to version 0.1.0-prerelease.20190528231524
Closes #4864
2019-05-28 23:16:58 +00:00
Christopher Willis-Ford 38455a7108 enable monitors for dynamic extension blocks 2019-05-28 11:45:57 -07:00
Karishma Chadha ebe310bc65 Add support for arguments on dynamic extension blocks. 2019-05-28 11:45:57 -07:00
Christopher Willis-Ford 5bc3198263 remove unused require 2019-05-28 11:45:57 -07:00
Christopher Willis-Ford 5d6669db6d add "scratch_extension" only if a block has an icon 2019-05-28 11:45:57 -07:00
Christopher Willis-Ford 519ffe3b91 weave extension categories into the output of makeToolboxXML 2019-05-28 11:45:57 -07:00
Christopher Willis-Ford 717c773800 add block type check in defineDynamicBlock 2019-05-28 11:45:57 -07:00
Christopher Willis-Ford 935a4520da WIP dynamic block support 2019-05-28 11:45:56 -07:00
Christopher Willis-Ford 7685dad40a update extension event handling for categoryInfo 2019-05-28 11:45:56 -07:00
Chris Willis-Ford 699671bde5 Merge pull request #4841 from cwillisf/test-failure-feedback
add timeouts & messages to all driver.wait calls
2019-05-28 09:43:39 -07:00
Paul Kaplan 7facbf35b2 Merge pull request #4851 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190524133325
Update scratch-paint to the latest version 🚀
2019-05-24 13:38:31 -04:00
greenkeeper[bot] b54fe54b4a chore(package): update scratch-blocks to version 0.1.0-prerelease.1558719162 2019-05-24 17:35:05 +00:00
Christopher Willis-Ford 796339dcfe simplify SeleniumHelper timeout options 2019-05-24 09:35:44 -07:00
greenkeeper[bot] 1721a322c2 chore(package): update scratch-vm to version 0.2.0-prerelease.20190524155459
Closes #4840
2019-05-24 15:57:43 +00:00
greenkeeper[bot] 3125097b83 chore(package): update scratch-paint to version 0.2.0-prerelease.20190524133325 2019-05-24 13:34:51 +00:00
Paul Kaplan aa0c40fca1 Merge pull request #4850 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190524125950
Update scratch-render to the latest version 🚀
2019-05-24 09:26:53 -04:00
Paul Kaplan 2d61027bbe Merge pull request #4848 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20190523193400
Update scratch-svg-renderer to the latest version 🚀
2019-05-24 09:26:33 -04:00
greenkeeper[bot] 2dac3cae81 chore(package): update scratch-render to version 0.1.0-prerelease.20190524125950 2019-05-24 13:02:23 +00:00
greenkeeper[bot] 43d3df2ffd chore(package): update scratch-svg-renderer to version 0.2.0-prerelease.20190523193400 2019-05-23 19:38:15 +00:00
Paul Kaplan 44ffaeee2c Merge pull request #4847 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190523183140
Update scratch-paint to the latest version 🚀
2019-05-23 15:33:33 -04:00
greenkeeper[bot] 0bf4e2c781 chore(package): update scratch-paint to version 0.2.0-prerelease.20190523183140 2019-05-23 18:56:29 +00:00
Paul Kaplan d342b82219 Merge pull request #4800 from mzgoddard/delay-gui
Delay loading and rendering gui
2019-05-23 14:14:37 -04:00
Paul Kaplan 227581ebcf Merge pull request #4677 from mzgoddard/update-toolbox
render only new toolbox xmls
2019-05-23 14:14:09 -04:00
Paul Kaplan eb74d6c033 Include a unit test to ensure default project assets are included 2019-05-23 11:22:36 -04:00
Paul Kaplan a2035a68e4 Update the built-in SVG assets for the default project 2019-05-23 09:59:41 -04:00
Christopher Willis-Ford 1b055af545 add timeouts & messages to all driver.wait calls 2019-05-22 18:25:45 -07:00
Karishma Chadha 5b79947178 Merge pull request #4842 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190522213620
Update scratch-paint to the latest version 🚀
2019-05-22 17:40:57 -04:00
Karishma Chadha 9e329b999e Merge branch 'develop' into greenkeeper/scratch-paint-0.2.0-prerelease.20190522213620 2019-05-22 17:40:44 -04:00
Karishma Chadha e5e0c3dd28 Merge pull request #4843 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190522213558
Update scratch-render to the latest version 🚀
2019-05-22 17:39:51 -04:00
greenkeeper[bot] e810422429 chore(package): update scratch-render to version 0.1.0-prerelease.20190522213558 2019-05-22 21:38:44 +00:00
greenkeeper[bot] 8b2c590309 chore(package): update scratch-paint to version 0.2.0-prerelease.20190522213620 2019-05-22 21:38:30 +00:00
Benjamin Wheeler d92ef0c807 Merge pull request #4833 from LLK/greenkeeper/scratch-storage-1.3.2
Update scratch-storage to the latest version 🚀
2019-05-22 14:42:44 -04:00
Benjamin Wheeler faa14b7add Merge branch 'develop' into greenkeeper/scratch-storage-1.3.2 2019-05-22 14:42:11 -04:00
Benjamin Wheeler abbc8ddad0 Merge pull request #4837 from LLK/greenkeeper/scratch-l10n-3.3.20190522144128
Update scratch-l10n to the latest version 🚀
2019-05-22 14:41:02 -04:00
Benjamin Wheeler 551e03b256 Merge pull request #4822 from LLK/greenkeeper/react-intl-2.9.0
Update react-intl to the latest version 🚀
2019-05-22 14:30:36 -04:00
Paul Kaplan f65f80160f Merge pull request #4834 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20190521170426
Update scratch-svg-renderer to the latest version 🚀
2019-05-22 14:05:17 -04:00
Paul Kaplan 5438c14bdb Merge branch 'develop' into greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20190521170426 2019-05-22 14:04:51 -04:00
Paul Kaplan 8f3d84ec60 Merge pull request #4830 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190520172914
Update scratch-vm to the latest version 🚀
2019-05-22 13:55:03 -04:00
greenkeeper[bot] c3df930bea chore(package): update scratch-l10n to version 3.3.20190522144128 2019-05-22 14:43:31 +00:00
greenkeeper[bot] 4513a45306 chore(package): update scratch-svg-renderer to version 0.2.0-prerelease.20190521170426 2019-05-21 17:06:21 +00:00
greenkeeper[bot] a94a83dc4f chore(package): update scratch-storage to version 1.3.2 2019-05-21 16:06:05 +00:00
Eric Rosenbaum e1e5c8352c Merge pull request #4806 from ericrosenbaum/bugfix/no-surprise-letters
Surprise sprite button does not give you letters
2019-05-21 11:55:00 -04:00
Paul Kaplan 98ceb00bb4 Fix lint 2019-05-21 11:01:48 -04:00
Paul Kaplan 77974839b0 Make surprise and library backdrop additions NOT select the stage 2019-05-21 10:46:05 -04:00
greenkeeper[bot] 706341011a chore(package): update scratch-vm to version 0.2.0-prerelease.20190520172914 2019-05-20 17:32:00 +00:00
greenkeeper[bot] fdea0a1620 chore(package): update react-intl to version 2.9.0 2019-05-15 18:01:27 +00:00
Eric Rosenbaum a11fcfe373 Fix typo in variable name 2019-05-09 15:12:13 -04:00
Eric Rosenbaum 52a3600819 Surprise sprites do not include letters and numbers 2019-05-09 15:06:26 -04:00
Michael "Z" Goddard bec6223e6e delay loading and rendering gui
- Create the scratch renderer earlier in the GUI container/HOC stack
- Delay loading and rendering GUI component until after project assets
  have been fetched
2019-05-08 16:58:07 -04:00
Michael "Z" Goddard d83ec5a0b5 replace blocks timeout with a promise
The timeout as a promise lets us update the toolbox during the same
render as the workspace so that the screen updates all together. This
also means the toolbox will update before the loading screen is
dismissed after loading, letting us avoid a second flash for the
toolbox to update.
2019-04-30 16:36:52 -04:00
Michael "Z" Goddard 866af2f766 render toolboxes once the workspace has rendered 2019-03-26 17:33:42 -04:00
Michael "Z" Goddard 656e8c2c77 render only new toolbox xmls 2019-03-21 14:57:22 -04:00
105 changed files with 2620 additions and 1088 deletions

No files matched your search

+8 -9
View File
@@ -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",
@@ -100,19 +100,18 @@
"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",
"rimraf": "^2.6.1",
"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.20190516171147",
"scratch-render": "0.1.0-prerelease.20190419202157",
"scratch-storage": "1.3.1",
"scratch-svg-renderer": "0.2.0-prerelease.20190419183947",
"scratch-vm": "0.2.0-prerelease.20190515153227",
"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",
"scratch-storage": "1.3.2",
"scratch-svg-renderer": "0.2.0-prerelease.20190523193400",
"scratch-vm": "0.2.0-prerelease.20190619042313",
"selenium-webdriver": "3.6.0",
"startaudiocontext": "1.2.1",
"style-loader": "^0.23.0",
+6 -8
View File
@@ -162,6 +162,7 @@
}
.step-body {
width: 100%;
background: $ui-white;
display: flex;
flex-direction: column;
@@ -169,6 +170,9 @@
align-items: center;
position: relative;
text-align: center;
/* Min height prevents layout changing when images change */
min-height: 256px;
}
.step-video {
@@ -177,6 +181,8 @@
.step-image {
max-width: 450px;
max-height: 200px;
object-fit: contain;
background: #F9F9F9;
border: 1px solid #ddd;
border-radius: 0.5rem;
@@ -275,14 +281,6 @@
margin-right: 0.5rem;
}
.video-cover {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
.steps-list {
display: flex;
flex-direction: row;
+85 -29
View File
@@ -85,32 +85,67 @@ const CardHeader = ({onCloseCards, onShrinkExpandCards, onShowAll, totalSteps, s
</div>
);
// Video step needs to know if the card is being dragged to cover the video
// so that the mouseup is not swallowed by the iframe.
const VideoStep = ({video, dragging}) => (
<div className={styles.stepVideo}>
{dragging ? (
<div className={styles.videoCover} />
) : null}
<iframe
allowFullScreen
allowTransparency="true"
frameBorder="0"
height="257"
scrolling="no"
src={`https://fast.wistia.net/embed/iframe/${video}?seo=false&videoFoam=true`}
title="📹"
width="466"
/>
<script
async
src="https://fast.wistia.net/assets/external/E-v1.js"
/>
</div>
);
class VideoStep extends React.Component {
componentDidMount () {
const script = document.createElement('script');
script.src = `https://fast.wistia.com/embed/medias/${this.props.video}.jsonp`;
script.async = true;
script.setAttribute('id', 'wistia-video-content');
document.body.appendChild(script);
const script2 = document.createElement('script');
script2.src = 'https://fast.wistia.com/assets/external/E-v1.js';
script2.async = true;
script2.setAttribute('id', 'wistia-video-api');
document.body.appendChild(script2);
}
// We use the Wistia API here to update or pause the video dynamically:
// https://wistia.com/support/developers/player-api
componentDidUpdate (prevProps) {
// Ensure the wistia API is loaded and available
if (!(window.Wistia && window.Wistia.api)) return;
// Get a handle on the currently loaded video
const video = window.Wistia.api(prevProps.video);
// Reset the video source if a new video has been chosen from the library
if (prevProps.video !== this.props.video) {
video.replaceWith(this.props.video);
}
// Pause the video if the modal is being shrunken
if (!this.props.expanded) {
video.pause();
}
}
componentWillUnmount () {
const script = document.getElementById('wistia-video-content');
script.parentNode.removeChild(script);
const script2 = document.getElementById('wistia-video-api');
script2.parentNode.removeChild(script2);
}
render () {
return (
<div className={styles.stepVideo}>
<div
className={`wistia_embed wistia_async_${this.props.video}`}
id="video-div"
style={{height: `257px`, width: `466px`}}
>
&nbsp;
</div>
</div>
);
}
}
VideoStep.propTypes = {
dragging: PropTypes.bool.isRequired,
expanded: PropTypes.bool.isRequired,
video: PropTypes.string.isRequired
};
@@ -123,6 +158,7 @@ const ImageStep = ({title, image}) => (
<img
className={styles.stepImage}
draggable={false}
key={image} /* Use src as key to prevent hanging around on slow connections */
src={image}
/>
</div>
@@ -256,6 +292,7 @@ const Cards = props => {
onShowAll,
onNextStep,
onPrevStep,
showVideos,
step,
expanded,
...posProps
@@ -298,6 +335,7 @@ const Cards = props => {
>
<Draggable
bounds="parent"
cancel="#video-div" // disable dragging on video div
position={{x: x, y: y}}
onDrag={onDrag}
onStart={onStartDrag}
@@ -323,10 +361,18 @@ const Cards = props => {
/>
) : (
steps[step].video ? (
<VideoStep
dragging={dragging}
video={translateVideo(steps[step].video, locale)}
/>
showVideos ? (
<VideoStep
dragging={dragging}
expanded={expanded}
video={translateVideo(steps[step].video, locale)}
/>
) : ( // Else show the deck image and title
<ImageStep
image={content[activeDeckId].img}
title={content[activeDeckId].name}
/>
)
) : (
<ImageStep
image={translateImage(steps[step].image, locale)}
@@ -376,9 +422,19 @@ Cards.propTypes = {
onShowAll: PropTypes.func,
onShrinkExpandCards: PropTypes.func.isRequired,
onStartDrag: PropTypes.func,
showVideos: PropTypes.bool,
step: PropTypes.number.isRequired,
x: PropTypes.number,
y: PropTypes.number
};
export default Cards;
Cards.defaultProps = {
showVideos: true
};
export {
Cards as default,
// Others exported for testability
ImageStep,
VideoStep
};
+7 -10
View File
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
import React from 'react';
import Box from '../box/box.jsx';
import ScratchImage from '../scratch-image/scratch-image.jsx';
import styles from './library-item.css';
import classNames from 'classnames';
@@ -36,12 +35,10 @@ class LibraryItemComponent extends React.PureComponent {
/>
</div>
) : null}
{this.props.iconSource ? (
<ScratchImage
className={styles.featuredImage}
imageSource={this.props.iconSource}
/>
) : null}
<img
className={styles.featuredImage}
src={this.props.iconURL}
/>
</div>
{this.props.insetIconURL ? (
<div className={styles.libraryItemInsetImageContainer}>
@@ -124,9 +121,9 @@ class LibraryItemComponent extends React.PureComponent {
{/* Layers of wrapping is to prevent layout thrashing on animation */}
<Box className={styles.libraryItemImageContainerWrapper}>
<Box className={styles.libraryItemImageContainer}>
<ScratchImage
<img
className={styles.libraryItemImage}
imageSource={this.props.iconSource}
src={this.props.iconURL}
/>
</Box>
</Box>
@@ -149,7 +146,7 @@ LibraryItemComponent.propTypes = {
extensionId: PropTypes.string,
featured: PropTypes.bool,
hidden: PropTypes.bool,
iconSource: ScratchImage.ImageSourcePropType,
iconURL: PropTypes.string,
insetIconURL: PropTypes.string,
internetConnectionRequired: PropTypes.bool,
name: PropTypes.oneOfType([
+9
View File
@@ -12,6 +12,7 @@
overflow-y: auto;
height: auto;
padding: 0.5rem;
height: calc(100% - $library-header-height);
}
.library-scroll-grid.withFilterBar {
@@ -59,3 +60,11 @@
height: $library-filter-bar-height;
overflow: hidden;
}
.spinner-wrapper {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
+24 -52
View File
@@ -9,7 +9,7 @@ import Modal from '../../containers/modal.jsx';
import Divider from '../divider/divider.jsx';
import Filter from '../filter/filter.jsx';
import TagButton from '../../containers/tag-button.jsx';
import storage from '../../lib/storage';
import Spinner from '../spinner/spinner.jsx';
import styles from './library.css';
@@ -29,47 +29,6 @@ const messages = defineMessages({
const ALL_TAG = {tag: 'all', intlLabel: messages.allTag};
const tagListPrefix = [ALL_TAG];
/**
* Find the AssetType which corresponds to a particular file extension. For example, 'png' => AssetType.ImageBitmap.
* @param {string} fileExtension - the file extension to look up.
* @returns {AssetType} - the AssetType corresponding to the extension, if any.
*/
const getAssetTypeForFileExtension = function (fileExtension) {
const compareOptions = {
sensitivity: 'accent',
usage: 'search'
};
for (const assetTypeId in storage.AssetType) {
const assetType = storage.AssetType[assetTypeId];
if (fileExtension.localeCompare(assetType.runtimeFormat, compareOptions) === 0) {
return assetType;
}
}
};
/**
* Figure out an `imageSource` (URI or asset ID & type) for a library item's icon.
* @param {object} item - either a library item or one of a library item's costumes.
* @returns {object} - an `imageSource` ready to be passed to a `ScratchImage`.
*/
const getItemImageSource = function (item) {
if (item.rawURL) {
return {
uri: item.rawURL
};
}
// TODO: adjust libraries to be more storage-friendly; don't use split() here.
const md5 = item.md5 || item.baseLayerMD5;
if (md5) {
const [assetId, fileExtension] = md5.split('.');
return {
assetId: assetId,
assetType: getAssetTypeForFileExtension(fileExtension)
};
}
};
class LibraryComponent extends React.Component {
constructor (props) {
super(props);
@@ -86,9 +45,16 @@ class LibraryComponent extends React.Component {
this.state = {
selectedItem: null,
filterQuery: '',
selectedTag: ALL_TAG.tag
selectedTag: ALL_TAG.tag,
loaded: false
};
}
componentDidMount () {
// Allow the spinner to display before loading the content
setTimeout(() => {
this.setState({loaded: true});
});
}
componentDidUpdate (prevProps, prevState) {
if (prevState.filterQuery !== this.state.filterQuery ||
prevState.selectedTag !== this.state.selectedTag) {
@@ -204,10 +170,8 @@ class LibraryComponent extends React.Component {
})}
ref={this.setFilteredDataRef}
>
{this.getFilteredData().map((dataItem, index) => {
const iconSource = getItemImageSource(dataItem);
const icons = dataItem.json && dataItem.json.costumes.map(getItemImageSource);
return (<LibraryItem
{this.state.loaded ? this.getFilteredData().map((dataItem, index) => (
<LibraryItem
bluetoothRequired={dataItem.bluetoothRequired}
collaborator={dataItem.collaborator}
description={dataItem.description}
@@ -215,18 +179,26 @@ class LibraryComponent extends React.Component {
extensionId={dataItem.extensionId}
featured={dataItem.featured}
hidden={dataItem.hidden}
iconSource={iconSource}
icons={icons}
iconMd5={dataItem.md5}
iconRawURL={dataItem.rawURL}
icons={dataItem.json && dataItem.json.costumes}
id={index}
insetIconURL={dataItem.insetIconURL}
internetConnectionRequired={dataItem.internetConnectionRequired}
key={`item_${index}`}
key={typeof dataItem.name === 'string' ? dataItem.name : dataItem.rawURL}
name={dataItem.name}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
onSelect={this.handleSelect}
/>);
})}
/>
)) : (
<div className={styles.spinnerWrapper}>
<Spinner
large
level="primary"
/>
</div>
)}
</div>
</Modal>
);
+2 -1
View File
@@ -91,7 +91,8 @@ LoupeComponent.propTypes = {
color: PropTypes.shape({
r: PropTypes.number,
g: PropTypes.number,
b: PropTypes.number
b: PropTypes.number,
a: PropTypes.number
}),
data: PropTypes.instanceOf(Uint8Array),
width: PropTypes.number,
@@ -1,139 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import VisibilitySensor from 'react-visibility-sensor';
import storage from '../../lib/storage';
class ScratchImage extends React.PureComponent {
static init () {
this._maxParallelism = 6;
this._currentJobs = 0;
this._pendingImages = new Set();
}
static loadPendingImages () {
if (this._currentJobs >= this._maxParallelism) {
// already busy
return;
}
// Find the first visible image. If there aren't any, find the first non-visible image.
let nextImage;
for (const image of this._pendingImages) {
if (image.isVisible) {
nextImage = image;
break;
} else {
nextImage = nextImage || image;
}
}
// If we found an image to load:
// 1) Remove it from the queue
// 2) Load the image
// 3) Pump the queue again
if (nextImage) {
this._pendingImages.delete(nextImage);
const imageSource = nextImage.props.imageSource;
++this._currentJobs;
storage
.load(imageSource.assetType, imageSource.assetId)
.then(asset => {
if (!nextImage.wasUnmounted) {
const dataURI = asset.encodeDataURI();
nextImage.setState({
imageURI: dataURI
});
}
--this._currentJobs;
this.loadPendingImages();
});
}
}
constructor (props) {
super(props);
this.state = {};
Object.assign(this.state, this._loadImageSource(props.imageSource));
}
componentWillReceiveProps (nextProps) {
const newState = this._loadImageSource(nextProps.imageSource);
this.setState(newState);
}
componentWillUnmount () {
this.wasUnmounted = true;
ScratchImage._pendingImages.delete(this);
}
/**
* Calculate the state changes necessary to load the image specified in the provided source info. If the component
* is mounted, call setState() with the return value of this function. If the component has not yet mounted, use
* the return value of this function as initial state for the component.
*
* @param {object} imageSource - the new source for the image, including either assetId or URI
* @returns {object} - the new state values, if any.
*/
_loadImageSource (imageSource) {
if (imageSource) {
if (imageSource.uri) {
ScratchImage._pendingImages.delete(this);
return {
imageURI: imageSource.uri,
lastRequestedAsset: null
};
}
if (this.state.lastRequestedAsset !== imageSource.assetId) {
ScratchImage._pendingImages.add(this);
return {
lastRequestedAsset: imageSource.assetId
};
}
}
// Nothing to do - don't change any state.
return {};
}
render () {
const {
src: _src,
imageSource: _imageSource,
...imgProps
} = this.props;
return (
<VisibilitySensor
intervalCheck
scrollCheck
>
{
({isVisible}) => {
this.isVisible = isVisible;
ScratchImage.loadPendingImages();
return (
<img
src={this.state.imageURI}
{...imgProps}
/>
);
}
}
</VisibilitySensor>
);
}
}
ScratchImage.ImageSourcePropType = PropTypes.oneOfType([
PropTypes.shape({
assetId: PropTypes.string.isRequired,
assetType: PropTypes.oneOf(Object.values(storage.AssetType)).isRequired
}),
PropTypes.shape({
uri: PropTypes.string.isRequired
})
]);
ScratchImage.propTypes = {
imageSource: ScratchImage.ImageSourcePropType.isRequired
};
ScratchImage.init();
export default ScratchImage;
+17
View File
@@ -39,6 +39,16 @@
height: .5rem;
}
.large {
width: 2.5rem;
height: 2.5rem;
}
.large::before, .large::after {
width: 2.5rem;
height: 2.5rem;
}
@keyframes spin {
0% {
transform: rotate(0deg);
@@ -71,3 +81,10 @@
.spinner.info::after {
border-top-color: $ui-white;
}
.spinner.primary {
border-color: $motion-transparent;
}
.spinner.primary::after {
border-top-color: $motion-primary;
}
+10 -3
View File
@@ -8,7 +8,8 @@ const SpinnerComponent = function (props) {
const {
className,
level,
small
small,
large
} = props;
return (
<div
@@ -16,18 +17,24 @@ const SpinnerComponent = function (props) {
className,
styles.spinner,
styles[level],
{[styles.small]: small}
{
[styles.small]: small,
[styles.large]: large
}
)}
/>
);
};
SpinnerComponent.propTypes = {
className: PropTypes.string,
large: PropTypes.bool,
level: PropTypes.string,
small: PropTypes.bool
};
SpinnerComponent.defaultProps = {
className: '',
level: 'info'
large: false,
level: 'info',
small: false
};
export default SpinnerComponent;
@@ -20,7 +20,6 @@
cursor: pointer;
user-select: none;
touch-action: none;
}
.sprite-selector-item.is-selected {
@@ -23,8 +23,9 @@ const SpriteSelectorItem = props => (
onMouseDown: props.onMouseDown,
onTouchStart: props.onMouseDown
}}
disable={props.dragging}
disable={props.preventContextMenu}
id={`${props.name}-${contextMenuId}`}
ref={props.componentRef}
>
{typeof props.number === 'undefined' ? null : (
<div className={styles.number}>{props.number}</div>
@@ -89,9 +90,9 @@ const SpriteSelectorItem = props => (
SpriteSelectorItem.propTypes = {
className: PropTypes.string,
componentRef: PropTypes.func,
costumeURL: PropTypes.string,
details: PropTypes.string,
dragging: PropTypes.bool,
name: PropTypes.string.isRequired,
number: PropTypes.number,
onClick: PropTypes.func,
@@ -101,6 +102,7 @@ SpriteSelectorItem.propTypes = {
onMouseDown: PropTypes.func,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
preventContextMenu: PropTypes.bool,
selected: PropTypes.bool.isRequired
};
@@ -110,7 +110,8 @@ SpriteList.propTypes = {
editingTarget: PropTypes.string,
hoveredTarget: PropTypes.shape({
hoveredSprite: PropTypes.string,
receivedBlocks: PropTypes.bool
receivedBlocks: PropTypes.bool,
sprite: PropTypes.string
}),
items: PropTypes.arrayOf(PropTypes.shape({
costume: PropTypes.shape({
@@ -68,6 +68,8 @@
padding-left: calc($space / 2);
padding-right: calc($space / 2);
padding-bottom: $space;
overflow-x: hidden;
}
.add-button {
+2 -13
View File
@@ -2,7 +2,6 @@ import bindAll from 'lodash.bindall';
import PropTypes from 'prop-types';
import React from 'react';
import {defineMessages, injectIntl, intlShape} from 'react-intl';
import {connect} from 'react-redux';
import VM from 'scratch-vm';
import backdropLibraryContent from '../lib/libraries/backdrops.json';
@@ -33,7 +32,7 @@ class BackdropLibrary extends React.Component {
bitmapResolution: item.info.length > 2 ? item.info[2] : 1,
skinId: null
};
this.props.vm.setEditingTarget(this.props.stageID);
// Do not switch to stage, just add the backdrop
this.props.vm.addBackdrop(item.md5, vmBackdrop);
}
render () {
@@ -53,17 +52,7 @@ class BackdropLibrary extends React.Component {
BackdropLibrary.propTypes = {
intl: intlShape.isRequired,
onRequestClose: PropTypes.func,
stageID: PropTypes.string.isRequired,
vm: PropTypes.instanceOf(VM).isRequired
};
const mapStateToProps = state => ({
stageID: state.scratchGui.targets.stage.id
});
const mapDispatchToProps = () => ({});
export default injectIntl(connect(
mapStateToProps,
mapDispatchToProps
)(BackdropLibrary));
export default injectIntl(BackdropLibrary);
+38 -7
View File
@@ -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 '---'
});
// Update the toolbox with new blocks
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 (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);
+3 -1
View File
@@ -19,6 +19,7 @@ import {
import CardsComponent from '../components/cards/cards.jsx';
import {loadImageData} from '../lib/libraries/decks/translate-image.js';
import {notScratchDesktop} from '../lib/isScratchDesktop';
class Cards extends React.Component {
componentDidMount () {
@@ -52,7 +53,8 @@ const mapStateToProps = state => ({
y: state.scratchGui.cards.y,
isRtl: state.locales.isRtl,
locale: state.locales.locale,
dragging: state.scratchGui.cards.dragging
dragging: state.scratchGui.cards.dragging,
showVideos: notScratchDesktop()
});
const mapDispatchToProps = dispatch => ({
+5 -1
View File
@@ -52,6 +52,7 @@ class GUI extends React.Component {
setIsScratchDesktop(this.props.isScratchDesktop);
this.setReduxTitle(this.props.projectTitle);
this.props.onStorageInit(storage);
this.props.onVmInit(this.props.vm);
}
componentDidUpdate (prevProps) {
if (this.props.projectId !== prevProps.projectId && this.props.projectId !== null) {
@@ -92,6 +93,7 @@ class GUI extends React.Component {
onStorageInit,
onUpdateProjectId,
onUpdateReduxProjectTitle,
onVmInit,
projectHost,
projectId,
projectTitle,
@@ -131,6 +133,7 @@ GUI.propTypes = {
onUpdateProjectId: PropTypes.func,
onUpdateProjectTitle: PropTypes.func,
onUpdateReduxProjectTitle: PropTypes.func,
onVmInit: PropTypes.func,
projectHost: PropTypes.string,
projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
projectTitle: PropTypes.string,
@@ -142,7 +145,8 @@ GUI.defaultProps = {
isScratchDesktop: false,
onStorageInit: storageInstance => storageInstance.addOfficialScratchWebStores(),
onProjectLoaded: () => {},
onUpdateProjectId: () => {}
onUpdateProjectId: () => {},
onVmInit: (/* vm */) => {}
};
const mapStateToProps = state => {
+18 -8
View File
@@ -75,17 +75,21 @@ class LibraryItem extends React.PureComponent {
const nextIconIndex = (this.state.iconIndex + 1) % this.props.icons.length;
this.setState({iconIndex: nextIconIndex});
}
curIconSource () {
curIconMd5 () {
if (this.props.icons &&
this.state.isRotatingIcon &&
this.state.iconIndex < this.props.icons.length &&
this.props.icons[this.state.iconIndex]) {
return this.props.icons[this.state.iconIndex];
this.props.icons[this.state.iconIndex] &&
this.props.icons[this.state.iconIndex].baseLayerMD5) {
return this.props.icons[this.state.iconIndex].baseLayerMD5;
}
return this.props.iconSource;
return this.props.iconMd5;
}
render () {
const iconSource = this.curIconSource();
const iconMd5 = this.curIconMd5();
const iconURL = iconMd5 ?
`https://cdn.assets.scratch.mit.edu/internalapi/asset/${iconMd5}/get/` :
this.props.iconRawURL;
return (
<LibraryItemComponent
bluetoothRequired={this.props.bluetoothRequired}
@@ -95,7 +99,8 @@ class LibraryItem extends React.PureComponent {
extensionId={this.props.extensionId}
featured={this.props.featured}
hidden={this.props.hidden}
iconSource={iconSource}
iconURL={iconURL}
icons={this.props.icons}
id={this.props.id}
insetIconURL={this.props.insetIconURL}
internetConnectionRequired={this.props.internetConnectionRequired}
@@ -122,8 +127,13 @@ LibraryItem.propTypes = {
extensionId: PropTypes.string,
featured: PropTypes.bool,
hidden: PropTypes.bool,
iconSource: LibraryItemComponent.propTypes.iconSource, // single icon
icons: PropTypes.arrayOf(LibraryItemComponent.propTypes.iconSource), // rotating icons
iconMd5: PropTypes.string,
iconRawURL: PropTypes.string,
icons: PropTypes.arrayOf(
PropTypes.shape({
baseLayerMD5: PropTypes.string
})
),
id: PropTypes.number.isRequired,
insetIconURL: PropTypes.string,
internetConnectionRequired: PropTypes.bool,
+42 -31
View File
@@ -6,19 +6,19 @@ import {connect} from 'react-redux';
import {setHoveredSprite} from '../reducers/hovered-target';
import {updateAssetDrag} from '../reducers/asset-drag';
import storage from '../lib/storage';
import {getEventXY} from '../lib/touch-utils';
import VM from 'scratch-vm';
import getCostumeUrl from '../lib/get-costume-url';
import DragRecognizer from '../lib/drag-recognizer';
import {getEventXY} from '../lib/touch-utils';
import SpriteSelectorItemComponent from '../components/sprite-selector-item/sprite-selector-item.jsx';
const dragThreshold = 3; // Same as the block drag threshold
class SpriteSelectorItem extends React.PureComponent {
constructor (props) {
super(props);
bindAll(this, [
'getCostumeData',
'setRef',
'handleClick',
'handleDelete',
'handleDuplicate',
@@ -26,9 +26,22 @@ class SpriteSelectorItem extends React.PureComponent {
'handleMouseEnter',
'handleMouseLeave',
'handleMouseDown',
'handleMouseMove',
'handleMouseUp'
'handleDragEnd',
'handleDrag',
'handleTouchEnd'
]);
this.dragRecognizer = new DragRecognizer({
onDrag: this.handleDrag,
onDragEnd: this.handleDragEnd
});
}
componentDidMount () {
document.addEventListener('touchend', this.handleTouchEnd);
}
componentWillUnmount () {
document.removeEventListener('touchend', this.handleTouchEnd);
this.dragRecognizer.reset();
}
getCostumeData () {
if (this.props.costumeURL) return this.props.costumeURL;
@@ -36,12 +49,7 @@ class SpriteSelectorItem extends React.PureComponent {
return getCostumeUrl(this.props.asset);
}
handleMouseUp () {
this.initialOffset = null;
window.removeEventListener('mouseup', this.handleMouseUp);
window.removeEventListener('mousemove', this.handleMouseMove);
window.removeEventListener('touchend', this.handleMouseUp);
window.removeEventListener('touchmove', this.handleMouseMove);
handleDragEnd () {
if (this.props.dragging) {
this.props.onDrag({
img: null,
@@ -55,29 +63,26 @@ class SpriteSelectorItem extends React.PureComponent {
this.noClick = false;
});
}
handleMouseMove (e) {
const currentOffset = getEventXY(e);
const dx = currentOffset.x - this.initialOffset.x;
const dy = currentOffset.y - this.initialOffset.y;
if (Math.sqrt((dx * dx) + (dy * dy)) > dragThreshold) {
this.props.onDrag({
img: this.getCostumeData(),
currentOffset: currentOffset,
dragging: true,
dragType: this.props.dragType,
index: this.props.index,
payload: this.props.dragPayload
});
this.noClick = true;
handleDrag (currentOffset) {
this.props.onDrag({
img: this.getCostumeData(),
currentOffset: currentOffset,
dragging: true,
dragType: this.props.dragType,
index: this.props.index,
payload: this.props.dragPayload
});
this.noClick = true;
}
handleTouchEnd (e) {
const {x, y} = getEventXY(e);
const {top, left, bottom, right} = this.ref.getBoundingClientRect();
if (x >= left && x <= right && y >= top && y <= bottom) {
this.handleMouseEnter();
}
e.preventDefault();
}
handleMouseDown (e) {
this.initialOffset = getEventXY(e);
window.addEventListener('mouseup', this.handleMouseUp);
window.addEventListener('mousemove', this.handleMouseMove);
window.addEventListener('touchend', this.handleMouseUp);
window.addEventListener('touchmove', this.handleMouseMove);
this.dragRecognizer.start(e);
}
handleClick (e) {
e.preventDefault();
@@ -103,6 +108,10 @@ class SpriteSelectorItem extends React.PureComponent {
handleMouseEnter () {
this.props.dispatchSetHoveredSprite(this.props.id);
}
setRef (component) {
// Access the DOM node using .elem because it is going through ContextMenuTrigger
this.ref = component && component.elem;
}
render () {
const {
/* eslint-disable no-unused-vars */
@@ -122,7 +131,9 @@ class SpriteSelectorItem extends React.PureComponent {
} = this.props;
return (
<SpriteSelectorItemComponent
componentRef={this.setRef}
costumeURL={this.getCostumeData()}
preventContextMenu={this.dragRecognizer.gestureInProgress()}
onClick={this.handleClick}
onDeleteButtonClick={onDeleteButtonClick ? this.handleDelete : null}
onDuplicateButtonClick={onDuplicateButtonClick ? this.handleDuplicate : null}
+38 -11
View File
@@ -15,6 +15,7 @@ import ThrottledPropertyHOC from '../lib/throttled-property-hoc.jsx';
import {emptyCostume} from '../lib/empty-assets';
import sharedMessages from '../lib/shared-messages';
import {fetchCode} from '../lib/backpack-api';
import {getEventXY} from '../lib/touch-utils';
import StageSelectorComponent from '../components/stage-selector/stage-selector.jsx';
@@ -46,11 +47,26 @@ class StageSelector extends React.Component {
'handleBackdropUpload',
'handleMouseEnter',
'handleMouseLeave',
'handleTouchEnd',
'handleDrop',
'setFileInput'
'setFileInput',
'setRef'
]);
}
addBackdropFromLibraryItem (item) {
componentDidMount () {
document.addEventListener('touchend', this.handleTouchEnd);
}
componentWillUnmount () {
document.removeEventListener('touchend', this.handleTouchEnd);
}
handleTouchEnd (e) {
const {x, y} = getEventXY(e);
const {top, left, bottom, right} = this.ref.getBoundingClientRect();
if (x >= left && x <= right && y >= top && y <= bottom) {
this.handleMouseEnter();
}
}
addBackdropFromLibraryItem (item, shouldActivateTab = true) {
const vmBackdrop = {
name: item.name,
md5: item.md5,
@@ -59,25 +75,30 @@ class StageSelector extends React.Component {
bitmapResolution: item.info.length > 2 ? item.info[2] : 1,
skinId: null
};
this.handleNewBackdrop(vmBackdrop);
this.handleNewBackdrop(vmBackdrop, shouldActivateTab);
}
handleClick () {
this.props.onSelect(this.props.id);
}
handleNewBackdrop (backdrops_) {
handleNewBackdrop (backdrops_, shouldActivateTab = true) {
const backdrops = Array.isArray(backdrops_) ? backdrops_ : [backdrops_];
return Promise.all(backdrops.map(backdrop =>
this.props.vm.addBackdrop(backdrop.md5, backdrop)
)).then(() =>
this.props.onActivateTab(COSTUMES_TAB_INDEX)
);
)).then(() => {
if (shouldActivateTab) {
return this.props.onActivateTab(COSTUMES_TAB_INDEX);
}
});
}
handleSurpriseBackdrop () {
handleSurpriseBackdrop (e) {
e.stopPropagation(); // Prevent click from falling through to selecting stage.
// @todo should this not add a backdrop you already have?
const item = backdropLibraryContent[Math.floor(Math.random() * backdropLibraryContent.length)];
this.addBackdropFromLibraryItem(item);
this.addBackdropFromLibraryItem(item, false);
}
handleEmptyBackdrop () {
handleEmptyBackdrop (e) {
e.stopPropagation(); // Prevent click from falling through to stage selector, select it manually below
this.props.vm.setEditingTarget(this.props.id);
this.handleNewBackdrop(emptyCostume(this.props.intl.formatMessage(sharedMessages.backdrop, {index: 1})));
}
handleBackdropUpload (e) {
@@ -85,6 +106,7 @@ class StageSelector extends React.Component {
this.props.onShowImporting();
handleFileUpload(e.target, (buffer, fileType, fileName, fileIndex, fileCount) => {
costumeUpload(buffer, fileType, storage, vmCostumes => {
this.props.vm.setEditingTarget(this.props.id);
vmCostumes.forEach((costume, i) => {
costume.name = `${fileName}${i ? i + 1 : ''}`;
});
@@ -96,7 +118,8 @@ class StageSelector extends React.Component {
}, this.props.onCloseImporting);
}, this.props.onCloseImporting);
}
handleFileUploadClick () {
handleFileUploadClick (e) {
e.stopPropagation(); // Prevent click from selecting the stage, that is handled manually in backdrop upload
this.fileInput.click();
}
handleMouseEnter () {
@@ -130,12 +153,16 @@ class StageSelector extends React.Component {
setFileInput (input) {
this.fileInput = input;
}
setRef (ref) {
this.ref = ref;
}
render () {
const componentProps = omit(this.props, [
'asset', 'dispatchSetHoveredSprite', 'id', 'intl',
'onActivateTab', 'onSelect', 'onShowImporting', 'onCloseImporting']);
return (
<DroppableThrottledStage
componentRef={this.setRef}
fileInputRef={this.setFileInput}
onBackdropFileUpload={this.handleBackdropUpload}
onBackdropFileUploadClick={this.handleFileUploadClick}
+4 -1
View File
@@ -105,7 +105,10 @@ class TargetPane extends React.Component {
}
}
handleSurpriseSpriteClick () {
const item = spriteLibraryContent[Math.floor(Math.random() * spriteLibraryContent.length)];
const surpriseSprites = spriteLibraryContent.filter(sprite =>
(sprite.tags.indexOf('letters') === -1) && (sprite.tags.indexOf('numbers') === -1)
);
const item = surpriseSprites[Math.floor(Math.random() * surpriseSprites.length)];
randomizeSpritePosition(item);
this.props.vm.addSprite(JSON.stringify(item.json))
.then(this.handleActivateBlocksTab);
+9 -3
View File
@@ -62,10 +62,16 @@ class TipsLibrary extends React.PureComponent {
}
render () {
const decksLibraryThumbnailData = Object.keys(decksLibraryContent)
.filter(id =>
.filter(id => {
if (notScratchDesktop()) return true; // Do not filter anything in online editor
const deck = decksLibraryContent[id];
// Scratch Desktop doesn't want tutorials with `requiredProjectId`
notScratchDesktop() || !decksLibraryContent[id].hasOwnProperty('requiredProjectId')
)
if (deck.hasOwnProperty('requiredProjectId')) return false;
// Scratch Desktop should not load tutorials that are _only_ videos
if (deck.steps.filter(s => s.title).length === 0) return false;
// Allow any other tutorials
return true;
})
.map(id => ({
rawURL: decksLibraryContent[id].img,
id: id,
@@ -1,42 +0,0 @@
<svg version="1.1" id="cat" x="0px" y="0px" width="95px" height="111px" viewBox="0 0 95 111" enable-background="new 0 0 95 111" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<g id="Layer_3">
<path fill="#FAA51D" stroke="#000000" d="M22.462,79.039c-2.415-0.451-5.304-1.309-7.742-3.503&#xD;&#xA;&#x9;&#x9;C9.268,70.629,7.526,62.535,3.672,64.622c-3.856,2.088-3.782,15.165,8.353,19.194c4.182,1.391,7.998,1.396,11.091,1.312&#xD;&#xA;&#x9;&#x9;c0.811-0.025,7.717-0.654,10.079-4.074c2.361-3.42,0.719-4.272-0.09-4.744C32.295,75.838,25.878,79.677,22.462,79.039z"/>
<path fill="#FFFFFF" d="M4.236,64.877c-1.989,0.613-3.075,4.998-2.076,8.484c0.998,3.49,2.634,5.022,3.863,6.398&#xD;&#xA;&#x9;&#x9;c1.528,1.038-0.72-2.402,1.361-4.15c2.075-1.744,5.733-0.914,5.733-0.914s-2.909-3.987-4.57-6.396&#xD;&#xA;&#x9;&#x9;C6.975,65.988,6.359,64.375,4.236,64.877z"/>
</g>
<g>
<path fill="#FAA51D" d="M38.217,86.756c0,0-8.832,6.2-17.071,8.412l0.086,0.215c1.247,1.824,5.87,7.497-0.334,9.496&#xD;&#xA;&#x9;&#x9;c-5.333,1.717-15.12-13.104-10.821-15.902c2.626-1.713,4.892-0.252,4.892-0.252s3.474-1.07,6.001-2.345&#xD;&#xA;&#x9;&#x9;c4.303-2.161,5.784-3.453,5.784-3.453s4.184-4.306,6.856-4.137C36.281,78.96,41.669,83.504,38.217,86.756z"/>
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M21.232,95.383c1.247,1.824,5.87,7.497-0.334,9.496&#xD;&#xA;&#x9;&#x9;c-5.333,1.717-15.329-13.344-11.03-16.145c2.626-1.713,5.101-0.01,5.101-0.01s3.474-1.072,6.001-2.348&#xD;&#xA;&#x9;&#x9;c4.303-2.161,5.784-3.453,5.784-3.453"/>
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M38.217,86.756c0,0-10.123,7.107-18.804,8.819"/>
</g>
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M52.169,74.885c0,0,1.235,0.165,4.744,3.676&#xD;&#xA;&#x9;c3.509,3.508,6.026,2.16,8.911,0.724c2.877-1.443,10.537-6.126,6.49-9.817c-4.049-3.688-6.207,1.146-9.715,2.405&#xD;&#xA;&#x9;c-3.512,1.26-5.061-2.487-6.858-4.287c-0.589-0.593-1.188-1.099-1.729-1.505c0,0-0.971-0.76-1.906,2.79&#xD;&#xA;&#x9;C51.172,72.412,50.162,73.415,52.169,74.885z"/>
<g id="Layer_2_1_">
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M46.753,82.012c1.188-0.912,2.397-2.402,3.951-4.713&#xD;&#xA;&#x9;&#x9;c1.296-1.927,2.7-5.578,2.7-5.578c0.875-2.521,1.934-6.576-1.902-7.296c-1.553-0.291-4.079-0.098-7.67-0.776&#xD;&#xA;&#x9;&#x9;c-3.593-0.681-6.798-2.522-9.517,2.233c-2.718,4.757-9.59,8.271-1.056,16.563c0,0,4.901,3.842,10.764,9.639&#xD;&#xA;&#x9;&#x9;c4.831,4.775,12.045,10.602,12.045,10.602s18.972,2.188,19.535-0.693c1.922-9.79-14.777-6.911-14.777-6.911&#xD;&#xA;&#x9;&#x9;s-4.605-3.933-6.725-5.794c-3.478-3.059-11.125-10.771-11.125-10.771"/>
<path fill="#FFFFFF" d="M51.253,75.434c0,0,2.47-2.66-2.469-5.317c-4.939-2.657-7.213-0.017-8.739,1.521&#xD;&#xA;&#x9;&#x9;c-2.644,2.655,3.443,6.611,3.443,6.611l3.176,3.204c0,0,1.738-1.647,2.499-2.979C50.036,77.26,51.253,75.434,51.253,75.434"/>
</g>
<g id="Layer_8"/>
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M29.926,73.218c0.749-0.571,2.889-2.202,4.854-3.657&#xD;&#xA;&#x9;c2.428-1.799,6.117-5.849,1.077-7.646c-5.04-1.801-7.507,1.604-11.519,4.946c-2.159,1.801-5.308,2.699-4.319,6.209&#xD;&#xA;&#x9;c0.993,3.511,4.862,13.408,11.789,10.17c6.929-3.239-1.799-9.18-3.06-11.157"/>
<g id="Layer_2">
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M52.709,14.156c-1.54-0.143-4.75-0.316-6.518-0.231&#xD;&#xA;&#x9;&#x9;c-4.728,0.225-9.224,1.928-9.224,1.928L23.949,7.357l2.235,18.906c0.646-0.782-10.555,12.804-3.479,24.224&#xD;&#xA;&#x9;&#x9;c7.08,11.426,22.233,16.518,40.988,12.792c18.755-3.729,23.229-14.531,21.986-20.246c-1.242-5.714-8.322-7.823-8.322-7.823&#xD;&#xA;&#x9;&#x9;s-0.09-4.48-3.328-9.97c-1.926-3.268-8.348-8.041-8.348-8.041L62.822,5.647l-7.452,7.204L52.709,14.156z"/>
<path fill="#FFFFFF" d="M76.42,35.066l-2.482-2.064l-9.115,2.661c0,0,0,3.419-4.367,4.367c-4.37,0.951-11.211-2.277-11.211-2.277&#xD;&#xA;&#x9;&#x9;L41.46,41.17c0,0-8.437,0.928-8.739,6.081C32.048,58.704,46.1,63.479,51.425,63.783c2.905,0.167,8.235-0.338,12.277-1.141&#xD;&#xA;&#x9;&#x9;c17.752-3.234,22.551-13.919,21.31-19.635c-1.242-5.714-7.978-7.196-7.978-7.196L76.42,35.066z"/>
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M10.673,46.155c0,0,4.107,0.374,5.974,0.268&#xD;&#xA;&#x9;&#x9;c1.865-0.107,5.492-0.587,5.492-0.587"/>
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M81.656,40.671c0,0,4.549-0.743,6.859-1.549&#xD;&#xA;&#x9;&#x9;c2.715-0.942,4.543-2.545,4.543-2.545"/>
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M22.337,41.909c0,0-2.384-1.777-6.117-3.43&#xD;&#xA;&#x9;&#x9;c-4.134-1.831-6.405-2.303-6.405-2.303"/>
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M82.117,46.622c0,0,2.726,1.104,5.533,1.385&#xD;&#xA;&#x9;&#x9;c2.77,0.276,4.646,0.11,4.646,0.11"/>
<path fill="none" stroke="#000000" stroke-linecap="round" stroke-miterlimit="10" d="M52.35,14.212&#xD;&#xA;&#x9;&#x9;c2.84,0.7,3.887,1.469,3.887,1.469"/>
<line fill="none" stroke="#000000" x1="33.898" y1="13.684" x2="39.956" y2="18.042"/>
</g>
<g id="Layer_5">
<path fill="#FFFFFF" stroke="#231F20" d="M71.84,25.366c2.924,4.479,3.033,9.591,0.242,11.415&#xD;&#xA;&#x9;&#x9;c-2.793,1.825-7.426-0.332-10.354-4.813c-2.933-4.48-3.037-9.589-0.244-11.415C64.275,18.73,68.913,20.884,71.84,25.366z"/>
<path fill="#231F20" d="M71.089,32.522c0,1.08-0.802,1.956-1.8,1.956c-0.993,0-1.803-0.877-1.803-1.956&#xD;&#xA;&#x9;&#x9;c0-1.08,0.81-1.958,1.803-1.958C70.287,30.564,71.089,31.442,71.089,32.522"/>
</g>
<g id="Layer_7">
<path fill="#FFFFFF" stroke="#231F20" d="M47.867,28.619c2.926,4.48,2.619,9.862-0.681,12.015&#xD;&#xA;&#x9;&#x9;c-3.302,2.159-8.351,0.272-11.276-4.208c-2.928-4.48-2.624-9.86,0.678-12.017C39.891,22.253,44.938,24.137,47.867,28.619z"/>
<path fill="#231F20" d="M46.079,34.507c0,1.081-0.803,1.957-1.801,1.957c-0.992,0-1.803-0.878-1.803-1.957&#xD;&#xA;&#x9;&#x9;c0-1.08,0.811-1.957,1.803-1.957C45.274,32.55,46.079,33.427,46.079,34.507"/>
</g>
<path fill="#5E4A42" stroke="#000000" d="M59.766,37.926c1.854,0,4.555-0.284,4.697,0.569c0.143,0.855-1.709,4.203-2.988,4.345&#xD;&#xA;&#x9;c-1.283,0.142-6.125-2.353-6.195-3.919C55.206,37.355,58.055,37.926,59.766,37.926z"/>
<g id="Layer_4">
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M46.774,45.235c0,0,10.347,3.054,14.217,3.897&#xD;&#xA;&#x9;&#x9;c3.868,0.842,10.851,1.684,10.851,1.684s-7.99,10.245-17.328,7.644C45.176,55.863,45.345,49.975,46.774,45.235z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.5 KiB

@@ -1,37 +0,0 @@
<svg version="1.1" id="cat" x="0px" y="0px" width="95px" height="111px" viewBox="0 0 95 111" enable-background="new 0 0 95 111" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<g id="Layer_8">
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M23.754,82.506c-2.871-1.402-10.511-2.518-14.771-6.695&#xD;&#xA;&#x9;&#x9;c-9.431-9.246-3.824-21.732,0.704-21.797c3.611-0.061,3.363,5.482,4.622,11.145c0.875,3.939,1.673,6.988,6.407,9.674&#xD;&#xA;&#x9;&#x9;c4.735,2.688,6.918,2.916,6.918,2.916s3.258,0.791,1.596,2.562C27.568,82.076,23.754,82.506,23.754,82.506z"/>
<path fill="#FFFFFF" d="M14.291,66.369c-0.115-0.459-0.222-0.928-0.33-1.413c-1.172-5.271-0.941-10.432-4.303-10.377&#xD;&#xA;&#x9;&#x9;c-3.433,0.05-7.53,7.771-4.19,15.285c0.193-0.143,0.399-0.336,0.569-0.588c0.451-0.664,0.821-2.969,2.654-3.688&#xD;&#xA;&#x9;&#x9;C9.989,65.082,12.778,65.869,14.291,66.369z"/>
</g>
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M52.543,61.473c0,0,1.455,2.342,1.519,4.969&#xD;&#xA;&#x9;c0.065,2.723-0.351,6.984,1.804,8.158c3.381,1.842,9.959-0.463,10.174,5.191c0.211,5.652-10.152,3.623-13.299,2.547&#xD;&#xA;&#x9;c-2.695-0.926-4.559-1.758-5.18-5.182c0,0-3.764-13.969-0.196-15.59C50.933,59.945,52.543,61.473,52.543,61.473z"/>
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M14.61,89.584c0,0,1.504,10.391-5.922,8.387&#xD;&#xA;&#x9;c-5.586-1.51-5.705-20.68-0.406-20.691c3.237-0.006,4.425,2.859,4.425,2.859s4.385,4.812,6.95,4.312&#xD;&#xA;&#x9;c2.565-0.502,7.978-6.703,7.978-6.703s2.875-2.539,6.146,0.023c3.271,2.561,1.521,6.178,1.521,6.178s-6.07,7.072-11.014,8.072&#xD;&#xA;&#x9;c-4.945,1.002-9.375-2.568-11.58-3.451"/>
<g id="Layer_6">
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M42.435,76.238c0,0,4.866,4.062,8.166,7.52&#xD;&#xA;&#x9;&#x9;c4.384,4.592-3.339,15.039-3.339,15.039s11.234,6.467,2.998,11c-2.657,1.467-15.661-13.225-15.661-13.225s7.979-7.475,4.972-10.172&#xD;&#xA;&#x9;&#x9;c-4.964-4.449-10.771-4.959-11.6-9.824c-0.502-2.957,0.613-4.502,2.658-7.053c1.354-1.689,2.084-3.182,2.084-3.182&#xD;&#xA;&#x9;&#x9;s1.135-7.238,4.289-8.254c3.153-1.014,8.842,0.057,10.758,0.395c1.914,0.338,6.645,1.877,4.617,9.922c0,0-1.451,3.771-2.789,5.762&#xD;&#xA;&#x9;&#x9;c-1.604,2.389-2.854,3.924-4.08,4.867"/>
<path fill="#FFFFFF" d="M50.182,72.631c0,0,2.55-2.748-2.551-5.494c-5.102-2.746-7.449-0.016-9.024,1.572&#xD;&#xA;&#x9;&#x9;c-2.731,2.742,3.334,7.453,3.334,7.453l3.383,2.564c0,0,1.916-1.582,2.701-2.957C48.807,74.396,50.182,72.631,50.182,72.631"/>
</g>
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M24.428,65.395c0.939,0.057,3.625,0.213,6.064,0.391&#xD;&#xA;&#x9;c3.014,0.217,8.451-0.451,5.801-5.098c-2.649-4.65-6.74-3.674-11.961-3.754c-2.812-0.043-5.785-1.412-7.322,1.895&#xD;&#xA;&#x9;c-1.534,3.312-5.051,13.338,2.313,15.396c7.366,2.057,4.618-8.137,4.95-10.457"/>
<g id="Layer_2">
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M51.709,10.156c-1.54-0.143-4.75-0.316-6.518-0.231&#xD;&#xA;&#x9;&#x9;c-4.728,0.225-9.224,1.928-9.224,1.928L22.949,3.357l2.235,18.906c0.646-0.782-10.555,12.804-3.479,24.224&#xD;&#xA;&#x9;&#x9;c7.08,11.426,22.233,16.518,40.988,12.792c18.755-3.728,23.229-14.531,21.986-20.246c-1.242-5.714-8.322-7.823-8.322-7.823&#xD;&#xA;&#x9;&#x9;s-0.09-4.48-3.328-9.97c-1.926-3.268-8.348-8.041-8.348-8.041L61.822,1.647L54.37,8.851L51.709,10.156z"/>
<path fill="#FFFFFF" d="M75.42,31.066l-2.483-2.064l-9.115,2.661c0,0,0,3.419-4.367,4.367c-4.37,0.951-11.211-2.277-11.211-2.277&#xD;&#xA;&#x9;&#x9;l-7.784,3.417c0,0-8.437,0.928-8.739,6.081C31.048,54.704,45.1,59.478,50.425,59.783c2.905,0.167,8.235-0.337,12.277-1.14&#xD;&#xA;&#x9;&#x9;c17.752-3.235,22.551-13.92,21.309-19.635c-1.242-5.714-7.977-7.196-7.977-7.196L75.42,31.066z"/>
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M9.673,42.155c0,0,4.107,0.374,5.974,0.268&#xD;&#xA;&#x9;&#x9;c1.865-0.107,5.492-0.587,5.492-0.587"/>
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M80.656,36.671c0,0,4.549-0.743,6.859-1.549&#xD;&#xA;&#x9;&#x9;c2.715-0.942,4.543-2.545,4.543-2.545"/>
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M21.337,37.909c0,0-2.384-1.777-6.117-3.43&#xD;&#xA;&#x9;&#x9;c-4.134-1.831-6.405-2.303-6.405-2.303"/>
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M81.117,42.622c0,0,2.726,1.104,5.533,1.385&#xD;&#xA;&#x9;&#x9;c2.77,0.276,4.647,0.11,4.647,0.11"/>
<path fill="none" stroke="#000000" stroke-linecap="round" stroke-miterlimit="10" d="M51.349,10.212&#xD;&#xA;&#x9;&#x9;c2.84,0.7,3.888,1.469,3.888,1.469"/>
<line fill="none" stroke="#000000" x1="32.898" y1="9.684" x2="38.956" y2="14.042"/>
</g>
<g id="Layer_5">
<path fill="#FFFFFF" stroke="#231F20" d="M70.84,21.366c2.924,4.479,3.033,9.591,0.242,11.415&#xD;&#xA;&#x9;&#x9;c-2.793,1.825-7.426-0.332-10.354-4.813c-2.932-4.48-3.037-9.589-0.244-11.415C63.275,14.73,67.913,16.884,70.84,21.366z"/>
<path fill="#231F20" d="M70.089,28.522c0,1.08-0.802,1.956-1.8,1.956c-0.993,0-1.803-0.877-1.803-1.956&#xD;&#xA;&#x9;&#x9;c0-1.08,0.81-1.958,1.803-1.958C69.287,26.564,70.089,27.442,70.089,28.522"/>
</g>
<g id="Layer_7">
<path fill="#FFFFFF" stroke="#231F20" d="M46.867,24.619c2.926,4.48,2.619,9.862-0.681,12.015&#xD;&#xA;&#x9;&#x9;c-3.302,2.159-8.351,0.272-11.276-4.208c-2.928-4.48-2.624-9.86,0.678-12.017C38.891,18.253,43.938,20.137,46.867,24.619z"/>
<path fill="#231F20" d="M45.079,30.507c0,1.081-0.803,1.957-1.801,1.957c-0.992,0-1.803-0.878-1.803-1.957&#xD;&#xA;&#x9;&#x9;c0-1.08,0.811-1.957,1.803-1.957C44.274,28.55,45.079,29.427,45.079,30.507"/>
</g>
<path fill="#5E4A42" stroke="#000000" d="M58.766,33.926c1.854,0,4.555-0.284,4.697,0.569c0.143,0.855-1.709,4.203-2.988,4.345&#xD;&#xA;&#x9;c-1.283,0.142-6.125-2.353-6.195-3.919C54.206,33.355,57.055,33.926,58.766,33.926z"/>
<g id="Layer_4">
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M45.774,41.235c0,0,10.347,3.054,14.217,3.897&#xD;&#xA;&#x9;&#x9;c3.868,0.842,10.851,1.684,10.851,1.684s-7.99,10.245-17.328,7.644C44.176,51.863,44.345,45.975,45.774,41.235z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.1 KiB

@@ -0,0 +1,42 @@
<svg width="96px" height="101px" viewBox="0 0 96 101" version="1.1" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 52.5 (67469) - http://www.bohemiancoding.com/sketch -->
<g>
<title>costume1.1</title>
<desc>Created with Sketch.</desc>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="costume1" transform="translate(-13.000000, -10.000000)">
<g id="costume1.1" transform="translate(13.000000, 10.000000)">
<g id="tail" transform="translate(0.000000, 59.000000)">
<path d="M21.9,14.8 C19.5,14.3 16.6,13.5 14.2,11.3 C8.7,6.4 7,-1.7 3.2,0.4 C-0.7,2.5 -0.6,15.6 11.6,19.6 C15.8,21 19.6,21 22.7,20.9 C23.5,20.9 30.4,20.2 32.8,16.8 C35.2,13.4 33.5,12.5 32.7,12.1 C31.8,11.6 25.3,15.4 21.9,14.8 Z" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3.8,0.6 C1.8,1.2 0.8,5.4 1.8,8.9 C2.8,12.4 4.4,14.2 5.7,15.5 C5.5,14.8 5.1,12.6 6.8,11.3 C8.9,9.6 12.6,10.5 12.6,10.5 C12.6,10.5 9.5,6.7 7.9,4 C6.3,1.7 5.8,0.2 3.8,0.6 Z" id="detail" fill="#FFFFFF"/>
</g>
<path d="M37.7,81.5 C35.9,82.7 29.7,87.1 21.8,89.6 L21.4,89.7 C21,89.8 20.8,90.3 21,90.7 C22.7,93.1 25.8,97.9 20.3,99.6 C15,101.3 5.1,87.2 9.3,83.5 C11.2,82.1 12.9,82.8 13.8,83.2 C14.3,83.4 14.8,83.4 15.3,83.3 C16.5,82.9 18.7,82.1 20.4,81.2 C24.7,79 25.7,78.1 27.7,76.6 C29.7,75.1 34.3,71.4 38,74.6 C41.2,77.3 39.4,80.3 37.7,81.5 Z" id="leg" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M53.6,60.7 C54.1,61.1 60.2,68.3 62.2,66.5 C64.6,64.4 67.9,60.3 71.5,63.6 C75.1,66.9 68.3,72.5 65.4,74 C58.5,77.1 52.9,71.2 51.7,69.6 C50.5,68 48.4,65.3 48.4,62.7 C48.5,59.9 51.9,59.2 53.6,60.7 Z" id="arm" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
<g id="body-and-leg" transform="translate(28.000000, 57.000000)">
<path d="M18.2,19.7 C19.4,18.8 20.6,17.3 22.2,15 C23.5,13.1 24.9,9.4 24.9,9.4 C25.8,6.9 26.4,2.1 23.1,2.2 C20.9,2.3 18.9,2 15.5,1.5 C9.5,0.3 8.4,-0.5 5.9,3.6 C3.2,8.4 -3.7,11.9 4.8,20.2 C4.8,20.2 9.7,24 15.6,29.8 C19.6,33.7 25.9,39.3 28.1,41.2 C28.6,41.6 29.2,41.8 29.8,41.9 C39.5,42.8 46.7,41.8 46.7,37.5 C46.7,30.3 32.4,32.8 32.4,32.8 C32.4,32.8 27.8,28.9 25.7,27 L18.2,19.7 Z" id="body" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M22.6,13 C22.6,13 24.5,10.5 20.2,7.8 C15.7,4.9 14,8.1 12.2,10.5 C10.2,13.6 12.2,15.1 14.2,16.9 C15.8,18.4 17.3,19.6 17.3,19.6 C17.3,19.6 20.4,17.5 22.6,13 Z" id="tummy" fill="#FFFFFF"/>
</g>
<path d="M30.2,68.4 C32.4,71.2 35.8,74.7 31.5,77.6 C25.6,80.9 20.7,70.9 19.7,67.4 C18.8,64.3 21.4,62.3 23.6,60.6 C27.9,57.5 31.5,54.7 35.5,56.2 C40.5,58 36.9,62 34.4,63.8 C32.9,64.9 31.4,66.1 30.3,66.8 C30,67.3 29.9,67.9 30.2,68.4 Z" id="arm" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
<g id="head" transform="translate(14.000000, 0.000000)">
<path d="M39.1,9 C36.8,8.6 34.4,8.4 31.6,8.6 C26.9,8.8 22.4,10.5 22.4,10.5 L10.3,2.6 C9.9,2.4 9.4,2.7 9.5,3.1 L11.6,21 C12.2,20.2 1,33.8 8.1,45.2 C15.2,56.6 30.3,61.7 49.1,58 C67.9,54.3 72.3,43.5 71.1,37.8 C69.9,32.1 62.8,30 62.8,30 C62.8,30 62.7,25.5 59.5,20 C57.6,16.7 51.2,12 51.2,12 L48.6,1.3 C48.5,0.9 48,0.8 47.7,1 L39.1,9 Z" stroke="#001026" stroke-width="1.2" fill="#FFAB19"/>
<path d="M62.5,30.4 C62.5,30.4 69.4,32.2 70.6,37.9 C71.8,43.6 67,53.9 48.4,57.5 C24.2,62.5 12.7,48.1 19.4,37.5 C26.1,26.8 37.6,35.9 46,35.3 C53.2,34.8 54,28.5 62.5,30.4 Z" id="face" fill="#FFFFFF"/>
<path d="M31,41.1 C31,40.7 31.4,40.4 31.8,40.5 C33.7,41.2 39.1,42.8 45.1,43.2 C50.5,43.5 53.7,43.2 55.2,42.9 C55.7,42.8 56.1,43.3 55.9,43.8 C55,46.5 51.2,54 40.7,53.4 C31.6,52.4 30.7,46 31,41.1 Z" id="mouth" stroke="#001026" stroke-width="1.2" fill="#FFFFFF" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M69,35.4 C69,35.4 76.2,35.3 80.9,31.5" id="whisker" stroke="#001026" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M69.4,41.3 C69.4,41.3 73.3,43.2 79.6,42.7" id="whisker" stroke="#001026" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M45.6,32.7 C47.7,32.7 49.9,32.9 50,33.6 C50.1,35 48.6,37.8 47,37.9 C45.2,38.1 41,35.6 41,34 C40.9,32.8 43.6,32.7 45.6,32.7 Z" id="nose" stroke="#001026" stroke-width="1.2" fill="#001026" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M0.6,31.2 C0.6,31.2 9.2,34 12.7,37.1" id="whisker" stroke="#001026" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M1.3,41.2 C1.3,41.2 8.7,42.3 13,40.6" id="whisker" stroke="#001026" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<g id="eye" transform="translate(44.000000, 15.000000)">
<path d="M13.4,6 C16.3,10.5 16.4,15.6 13.6,17.4 C10.8,19.2 6.2,17.1 3.2,12.6 C0.3,8.1 0.2,3 3,1.2 C5.8,-0.7 10.5,1.5 13.4,6 Z" id="pupil" stroke="#001026" stroke-width="1.2" fill="#FFFFFF" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13,11.7 C13,12.8 12.2,13.7 11.2,13.7 C10.2,13.7 9.4,12.8 9.4,11.7 C9.4,10.6 10.2,9.7 11.2,9.7 C12.2,9.7 13,10.6 13,11.7" id="pupil" fill="#001026"/>
</g>
<g id="eye" transform="translate(19.000000, 18.000000)">
<path d="M13.6,5.8 C16.6,10.2 16.4,15.6 13.7,17.5 C10.4,19.4 6,18 3,13.6 C-0.1,9.2 -0.3,3.5 2.8,1.3 C5.9,-1 10.6,1.4 13.6,5.8 Z" stroke="#001026" stroke-width="1.2" fill="#FFFFFF" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13,11.6 C13,12.7 12.2,13.6 11.2,13.6 C10.2,13.6 9.4,12.7 9.4,11.6 C9.4,10.5 10.2,9.6 11.2,9.6 C12.2,9.7 13,10.5 13,11.6" id="pupil" fill="#001026"/>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

@@ -0,0 +1,42 @@
<svg width="93px" height="106px" viewBox="0 0 93 106" version="1.1" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 52.5 (67469) - http://www.bohemiancoding.com/sketch -->
<g>
<title>costume2.1</title>
<desc>Created with Sketch.</desc>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="cotsume2" transform="translate(-16.000000, -10.000000)">
<g id="costume2.1" transform="translate(16.250000, 9.500000)">
<g id="tail" transform="translate(0.000000, 53.000000)">
<path d="M12,14.8 C8.9,7 10.6,0 6.3,0.4 C1.9,0.8 -3.2,12.5 6.4,21.1 C15.3,29 24.4,26.1 28,24 C31.5,21.8 28.8,20.6 28.2,19.8 C27.6,19 16.4,26.1 12,14.8 Z" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.8,0.9 C4.3,0.5 2.1,4.1 1.6,7.6 C1.4,11.2 2,13.3 2.7,15 C2.8,14.3 3.7,11.9 6.6,11.4 C9,11 10.6,13 10.6,13 C10.6,13 9.6,10 9.1,5.6 C8.7,2.9 8.8,1.3 6.8,0.9 Z" id="detail" fill="#FFFFFF"/>
</g>
<path d="M32.9,83.2 C32.2,84.1 30.2,87.6 25.4,90.6 C22.1,92.7 13.6,89.2 13.6,89.2 C14.5,98.4 7,98.2 5.6,95.2 C4.5,92.8 4.7,82.5 6.9,81.7 C10.3,80.5 14.9,82.5 19,82.5 C20.2,82.5 21.5,79.9 24.3,77.1 C26.3,75.1 28.9,71.4 33.1,74 C36.7,76.3 34.8,80.5 32.9,83.2 Z" id="leg" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M50.1,62 C50.4,64.2 50.6,72.6 53,73.9 C55.3,75.1 62.3,73.1 62.7,78 C63.1,82.8 56.6,82.9 51.2,81.4 C44.2,79.5 41,68.8 45,62.2 C46.5,59.9 49.8,59.7 50.1,62 Z" id="arm" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
<g id="body-and-leg" transform="translate(24.000000, 57.000000)">
<path d="M18.2,21.3 C19.4,20.4 21.2,18 22.8,15.6 C24.1,13.7 25.5,10 25.5,10 C26.4,7.5 27,2.7 23.7,2.8 C21.5,2.9 19.5,2.6 16.1,2.1 C10.1,0.9 8.9,-0.3 6.5,4.2 C2,12.5 -4,18.4 4.2,23.2 C6.2,24.4 9.4,26.4 12.2,28.4 C14.2,30.3 11.2,34.4 9.1,36.8 C8.2,37.9 8.2,39.5 9.3,40.6 C16.6,47.7 18.8,50.2 21.6,47.1 C25.4,42.9 19.4,38.8 19.4,38.8 C19.4,38.8 24.2,31.3 23.6,28 C22.9,24.8 18.2,21.3 18.2,21.3 Z" id="body" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M22.7284271,14.6028611 C22.7284271,14.6028611 24.6284271,12.1028611 20.3284271,9.40286109 C15.8284271,6.50286109 14.1284271,9.70286109 12.3284271,12.1028611 C10.3284271,15.2028611 12.3284271,16.7028611 14.3284271,18.5028611 C15.9284271,20.0028611 17.4284271,21.2028611 17.4284271,21.2028611 C17.4284271,21.2028611 20.5284271,19.1028611 22.7284271,14.6028611 Z" id="tummy" fill="#FFFFFF"/>
</g>
<path d="M32.3,65.2 C30.1,66.2 26,64.4 24.9,64.6 C24.6,64.7 22.8,69.9 21.7,73 C20.2,77.4 13.4,76.1 13,71.5 C12.7,67.2 16.6,58.9 17.9,57.4 C20.8,53.9 30.6591797,56.7082962 37.2463379,58.456543" id="arm" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
<g id="head" transform="translate(10.750000, 0.500000)">
<path d="M39.1,9 C36.8,8.6 34.4,8.4 31.6,8.6 C26.9,8.8 22.4,10.5 22.4,10.5 L10.3,2.6 C9.9,2.4 9.4,2.7 9.5,3.1 L11.6,21 C12.2,20.2 1,33.8 8.1,45.2 C15.2,56.6 30.3,61.7 49.1,58 C67.9,54.3 72.3,43.5 71.1,37.8 C69.9,32.1 62.8,30 62.8,30 C62.8,30 62.7,25.5 59.5,20 C57.6,16.7 51.2,12 51.2,12 L48.6,1.3 C48.5,0.9 48,0.8 47.7,1 L39.1,9 Z" stroke="#001026" stroke-width="1.2" fill="#FFAB19"/>
<path d="M62.5,30.4 C62.5,30.4 69.4,32.2 70.6,37.9 C71.8,43.6 67,53.9 48.4,57.5 C24.2,62.5 12.7,48.1 19.4,37.5 C26.1,26.8 37.6,35.9 46,35.3 C53.2,34.8 54,28.5 62.5,30.4 Z" id="face" fill="#FFFFFF"/>
<path d="M31,41.1 C31,40.7 31.4,40.4 31.8,40.5 C33.7,41.2 39.1,42.8 45.1,43.2 C50.5,43.5 53.7,43.2 55.2,42.9 C55.7,42.8 56.1,43.3 55.9,43.8 C55,46.5 51.2,54 40.7,53.4 C31.6,52.4 30.7,46 31,41.1 Z" id="mouth" stroke="#001026" stroke-width="1.2" fill="#FFFFFF" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M69,35.4 C69,35.4 76.2,35.3 80.9,31.5" id="whisker" stroke="#001026" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M69.4,41.3 C69.4,41.3 73.3,43.2 79.6,42.7" id="whisker" stroke="#001026" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M45.6,32.7 C47.7,32.7 49.9,32.9 50,33.6 C50.1,35 48.6,37.8 47,37.9 C45.2,38.1 41,35.6 41,34 C40.9,32.8 43.6,32.7 45.6,32.7 Z" id="nose" stroke="#001026" stroke-width="1.2" fill="#001026" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M0.6,31.2 C0.6,31.2 9.2,34 12.7,37.1" id="whisker" stroke="#001026" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M1.3,41.2 C1.3,41.2 8.7,42.3 13,40.6" id="whisker" stroke="#001026" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<g id="eye" transform="translate(44.000000, 15.000000)">
<path d="M13.4,6 C16.3,10.5 16.4,15.6 13.6,17.4 C10.8,19.2 6.2,17.1 3.2,12.6 C0.3,8.1 0.2,3 3,1.2 C5.8,-0.7 10.5,1.5 13.4,6 Z" id="pupil" stroke="#001026" stroke-width="1.2" fill="#FFFFFF" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13,11.7 C13,12.8 12.2,13.7 11.2,13.7 C10.2,13.7 9.4,12.8 9.4,11.7 C9.4,10.6 10.2,9.7 11.2,9.7 C12.2,9.7 13,10.6 13,11.7" id="pupil" fill="#001026"/>
</g>
<g id="eye" transform="translate(19.000000, 18.000000)">
<path d="M13.6,5.8 C16.6,10.2 16.4,15.6 13.7,17.5 C10.4,19.4 6,18 3,13.6 C-0.1,9.2 -0.3,3.5 2.8,1.3 C5.9,-1 10.6,1.4 13.6,5.8 Z" stroke="#001026" stroke-width="1.2" fill="#FFFFFF" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13,11.6 C13,12.7 12.2,13.6 11.2,13.6 C10.2,13.6 9.4,12.7 9.4,11.6 C9.4,10.5 10.2,9.6 11.2,9.6 C12.2,9.7 13,10.5 13,11.6" id="pupil" fill="#001026"/>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

+4 -4
View File
@@ -4,8 +4,8 @@ import projectData from './project-data';
import popWav from '!arraybuffer-loader!./83a9787d4cb6f3b7632b4ddfebf74367.wav';
import meowWav from '!arraybuffer-loader!./83c36d806dc92327b9e7049a565c6bff.wav';
import backdrop from '!raw-loader!./cd21514d0531fdffb22204e0ec5ed84a.svg';
import costume1 from '!raw-loader!./09dc888b0b7df19f70d81588ae73420e.svg';
import costume2 from '!raw-loader!./3696356a03a8d938318876a593572843.svg';
import costume1 from '!raw-loader!./b7853f557e4426412e64bb3da6531a99.svg';
import costume2 from '!raw-loader!./e6ddc55a6ddd9cc9d84fe0b4c21e016f.svg';
/* eslint-enable import/no-unresolved */
const defaultProject = translator => {
@@ -40,12 +40,12 @@ const defaultProject = translator => {
dataFormat: 'SVG',
data: encoder.encode(backdrop)
}, {
id: '09dc888b0b7df19f70d81588ae73420e',
id: 'b7853f557e4426412e64bb3da6531a99',
assetType: 'ImageVector',
dataFormat: 'SVG',
data: encoder.encode(costume1)
}, {
id: '3696356a03a8d938318876a593572843',
id: 'e6ddc55a6ddd9cc9d84fe0b4c21e016f',
assetType: 'ImageVector',
dataFormat: 'SVG',
data: encoder.encode(costume2)
+108
View File
@@ -0,0 +1,108 @@
// TODO: access `BlockType` and `ArgumentType` without reaching into VM
// Should we move these into a new extension support module or something?
import ArgumentType from 'scratch-vm/src/extension-support/argument-type';
import BlockType from 'scratch-vm/src/extension-support/block-type';
/**
* Define a block using extension info which has the ability to dynamically determine (and update) its layout.
* This functionality is used for extension blocks which can change its properties based on different state
* information. For example, the `control_stop` block changes its shape based on which menu item is selected
* and a variable block changes its text to reflect the variable name without using an editable field.
* @param {object} ScratchBlocks - The ScratchBlocks name space.
* @param {object} categoryInfo - Information about this block's extension category, including any menus and icons.
* @param {object} staticBlockInfo - The base block information before any dynamic changes.
* @param {string} extendedOpcode - The opcode for the block (including the extension ID).
*/
// TODO: grow this until it can fully replace `_convertForScratchBlocks` in the VM runtime
const defineDynamicBlock = (ScratchBlocks, categoryInfo, staticBlockInfo, extendedOpcode) => ({
init: function () {
const blockJson = {
type: extendedOpcode,
inputsInline: true,
category: categoryInfo.name,
colour: categoryInfo.color1,
colourSecondary: categoryInfo.color2,
colourTertiary: categoryInfo.color3
};
// There is a scratch-blocks / Blockly extension called "scratch_extension" which adjusts the styling of
// blocks to allow for an icon, a feature of Scratch extension blocks. However, Scratch "core" extension
// blocks don't have icons and so they should not use 'scratch_extension'. Adding a scratch-blocks / Blockly
// extension after `jsonInit` isn't fully supported (?), so we decide now whether there will be an icon.
if (staticBlockInfo.blockIconURI || categoryInfo.blockIconURI) {
blockJson.extensions = ['scratch_extension'];
}
// initialize the basics of the block, to be overridden & extended later by `domToMutation`
this.jsonInit(blockJson);
// initialize the cached block info used to carry block info from `domToMutation` to `mutationToDom`
this.blockInfoText = '{}';
// we need a block info update (through `domToMutation`) before we have a completely initialized block
this.needsBlockInfoUpdate = true;
},
mutationToDom: function () {
const container = document.createElement('mutation');
container.setAttribute('blockInfo', this.blockInfoText);
return container;
},
domToMutation: function (xmlElement) {
const blockInfoText = xmlElement.getAttribute('blockInfo');
if (!blockInfoText) return;
if (!this.needsBlockInfoUpdate) {
throw new Error('Attempted to update block info twice');
}
delete this.needsBlockInfoUpdate;
this.blockInfoText = blockInfoText;
const blockInfo = JSON.parse(blockInfoText);
switch (blockInfo.blockType) {
case BlockType.COMMAND:
case BlockType.CONDITIONAL:
case BlockType.LOOP:
this.setOutputShape(ScratchBlocks.OUTPUT_SHAPE_SQUARE);
this.setPreviousStatement(true);
this.setNextStatement(!blockInfo.isTerminal);
break;
case BlockType.REPORTER:
this.setOutput(true);
this.setOutputShape(ScratchBlocks.OUTPUT_SHAPE_ROUND);
if (!blockInfo.disableMonitor) {
this.setCheckboxInFlyout(true);
}
break;
case BlockType.BOOLEAN:
this.setOutput(true);
this.setOutputShape(ScratchBlocks.OUTPUT_SHAPE_HEXAGONAL);
break;
case BlockType.HAT:
case BlockType.EVENT:
this.setOutputShape(ScratchBlocks.OUTPUT_SHAPE_SQUARE);
this.setNextStatement(true);
break;
}
if (blockInfo.color1 || blockInfo.color2 || blockInfo.color3) {
// `setColour` handles undefined parameters by adjusting defined colors
this.setColour(blockInfo.color1, blockInfo.color2, blockInfo.color3);
}
// Layout block arguments
// TODO handle E/C Blocks
const blockText = blockInfo.text;
const args = [];
let argCount = 0;
const scratchBlocksStyleText = blockText.replace(/\[(.+?)]/g, (match, argName) => {
const arg = blockInfo.arguments[argName];
switch (arg.type) {
case ArgumentType.STRING:
args.push({type: 'input_value', name: argName});
break;
case ArgumentType.BOOLEAN:
args.push({type: 'input_value', name: argName, check: 'Boolean'});
break;
}
return `%${++argCount}`;
});
this.interpolate_(scratchBlocksStyleText, args);
}
});
export default defineDynamicBlock;
+125
View File
@@ -0,0 +1,125 @@
import bindAll from 'lodash.bindall';
import {getEventXY} from '../lib/touch-utils';
class DragRecognizer {
/* Gesture states */
static get STATE_UNIDENTIFIED () {
return 'unidentified';
}
static get STATE_SCROLL () {
return 'scroll';
}
static get STATE_DRAG () {
return 'drag';
}
constructor ({
onDrag = (() => {}),
onDragEnd = (() => {}),
touchDragAngle = 70, // Angle and distance thresholds are the same as scratch-blocks
distanceThreshold = 3
}) {
this._onDrag = onDrag;
this._onDragEnd = onDragEnd;
this._touchDragAngle = touchDragAngle;
this._distanceThreshold = distanceThreshold;
this._initialOffset = null;
this._gestureState = DragRecognizer.STATE_UNIDENTIFIED;
bindAll(this, [
'start',
'gestureInProgress',
'reset',
'_handleMove',
'_handleEnd'
]);
}
start (event) {
this._initialOffset = getEventXY(event);
this._bindListeners();
}
gestureInProgress () {
return this._gestureState !== DragRecognizer.STATE_UNIDENTIFIED;
}
reset () {
this._unbindListeners();
this._initialOffset = null;
this._gestureState = DragRecognizer.STATE_UNIDENTIFIED;
}
//
// Internal functions
//
_bindListeners () {
window.addEventListener('mouseup', this._handleEnd);
window.addEventListener('mousemove', this._handleMove);
window.addEventListener('touchend', this._handleEnd);
// touchmove must be marked as non-passive, or else it cannot prevent scrolling
window.addEventListener('touchmove', this._handleMove, {passive: false});
}
_unbindListeners () {
window.removeEventListener('mouseup', this._handleEnd);
window.removeEventListener('mousemove', this._handleMove);
window.removeEventListener('touchend', this._handleEnd);
window.removeEventListener('touchmove', this._handleMove, {passive: false});
}
_handleMove (event) {
// For gestures identified as vertical scrolls, do not process movement events
if (this._isScroll()) return;
const currentOffset = getEventXY(event);
// Try to identify this gesture if it hasn't been identified already
if (!this.gestureInProgress()) {
const dx = currentOffset.x - this._initialOffset.x;
const dy = currentOffset.y - this._initialOffset.y;
const dragDistance = Math.sqrt((dx * dx) + (dy * dy));
if (dragDistance < this._distanceThreshold) return;
// For touch moves, additionally check if the angle suggests drag vs. scroll
if (event.type === 'touchmove') {
// Direction goes from -180 to 180, with 0 toward the right.
let angle = Math.atan2(dy, dx) / Math.PI * 180;
// Fold over horizontal axis, range now 0 to 180
angle = Math.abs(angle);
// Fold over vertical axis, range now 0 to 90
if (angle > 90) angle = 180 - angle;
if (angle > this._touchDragAngle) {
this._gestureState = DragRecognizer.STATE_SCROLL;
} else {
this._gestureState = DragRecognizer.STATE_DRAG;
}
} else {
// Mouse moves are always considered drags
this._gestureState = DragRecognizer.STATE_DRAG;
}
}
if (this._isDrag()) {
this._onDrag(currentOffset, this._initialOffset);
event.preventDefault();
}
}
_handleEnd () {
this._onDragEnd();
this.reset();
}
_isDrag () {
return this._gestureState === DragRecognizer.STATE_DRAG;
}
_isScroll () {
return this._gestureState === DragRecognizer.STATE_SCROLL;
}
}
export default DragRecognizer;
+71 -52
View File
@@ -75,7 +75,7 @@
},
{
"name": "Andie-a",
"md5": "419ff5a76adbb4b076c072f68267fdeb.svg",
"md5": "d2c433e02481b10584e34ccdcf5a046a.svg",
"type": "costume",
"tags": [
"sports",
@@ -87,14 +87,33 @@
"alex eben meyer"
],
"info": [
86,
81,
80,
65,
1
]
},
{
"name": "Andie-b",
"md5": "a6a766912b4e43064ee3f389c72925b4.svg",
"md5": "045f92ad80d72521098ede6d7c80549a.svg",
"type": "costume",
"tags": [
"sports",
"basketball",
"people",
"wheelchair",
"handicap",
"handicapable",
"alex eben meyer"
],
"info": [
40,
91,
1
]
},
{
"name": "Andie-c",
"md5": "c9bad70ad371b8d6d97daed7ab9def98.svg",
"type": "costume",
"tags": [
"sports",
@@ -107,32 +126,13 @@
],
"info": [
46,
106,
1
]
},
{
"name": "Andie-c",
"md5": "fac1025857d2c28391cb31f39a80df54.svg",
"type": "costume",
"tags": [
"sports",
"basketball",
"people",
"wheelchair",
"handicap",
"handicapable",
"alex eben meyer"
],
"info": [
52,
65,
49,
1
]
},
{
"name": "Andie-d",
"md5": "7af50835e05d43e5e07cd2d641e7401e.svg",
"md5": "7c9db638cd671898ff07771112faff0f.svg",
"type": "costume",
"tags": [
"sports",
@@ -144,8 +144,8 @@
"alex eben meyer"
],
"info": [
87,
72,
71,
57,
1
]
},
@@ -1069,7 +1069,7 @@
},
{
"name": "Ben-a",
"md5": "4386d05d5abd6e1aa0a93b7b447d1b39.svg",
"md5": "322e13bd17d02acfdc048a5ef2167be8.svg",
"type": "costume",
"tags": [
"sports",
@@ -1079,14 +1079,14 @@
"alex eben meyer"
],
"info": [
53,
54,
69,
1
]
},
{
"name": "Ben-b",
"md5": "90db137cd0743204d02fdc9be1c8eb94.svg",
"md5": "59ba1e80ba75b1d06c37db4b9b4fde27.svg",
"type": "costume",
"tags": [
"sports",
@@ -1096,14 +1096,14 @@
"alex eben meyer"
],
"info": [
53,
67,
54,
69,
1
]
},
{
"name": "Ben-c",
"md5": "5f05d41ede773b3bb379286e86a5b869.svg",
"md5": "940ec2fa85d666dde95a51bcbccd266e.svg",
"type": "costume",
"tags": [
"sports",
@@ -1113,14 +1113,14 @@
"alex eben meyer"
],
"info": [
73,
71,
72,
1
]
},
{
"name": "Ben-d",
"md5": "6f6e246567c0639c758ed4f6930be76f.svg",
"md5": "3043a50a91b9d484a8cd32eb82f2d88f.svg",
"type": "costume",
"tags": [
"sports",
@@ -1130,8 +1130,8 @@
"alex eben meyer"
],
"info": [
42,
72,
44,
71,
1
]
},
@@ -3628,16 +3628,17 @@
},
{
"name": "Earth",
"md5": "d9df2cf4f6eb2460d0de3e4f1e05f40b.svg",
"md5": "64220361b80d2812e9f27fc88ef39027.svg",
"type": "costume",
"tags": [
"space",
"planet",
"earth"
"earth",
"globe"
],
"info": [
41,
40,
55,
55,
1
]
},
@@ -6178,7 +6179,7 @@
},
{
"name": "Jordyn-a",
"md5": "8355df83cca2baa0fdf6269d1311f1ea.svg",
"md5": "2f59adebc2c0feb9ebfabd7ee9642f2b.svg",
"type": "costume",
"tags": [
"sports",
@@ -6189,13 +6190,13 @@
],
"info": [
51,
63,
62,
1
]
},
{
"name": "Jordyn-b",
"md5": "2e2c43bbd7b18524fb132f1426ca3134.svg",
"md5": "075f382883e60dba8094b3dc30918f60.svg",
"type": "costume",
"tags": [
"sports",
@@ -6212,7 +6213,7 @@
},
{
"name": "Jordyn-c",
"md5": "5c0d24c53805e16358a2f6f6cc4a456e.svg",
"md5": "fbe81c40d4a880c8799c458359758629.svg",
"type": "costume",
"tags": [
"sports",
@@ -6223,13 +6224,13 @@
],
"info": [
68,
61,
62,
1
]
},
{
"name": "Jordyn-d",
"md5": "ebe2b20b99c1e1b373fd2b02be48aab9.svg",
"md5": "68a784d4d0340475a6e6feff3b28b470.svg",
"type": "costume",
"tags": [
"sports",
@@ -6240,7 +6241,7 @@
],
"info": [
40,
61,
62,
1
]
},
@@ -9660,7 +9661,7 @@
},
{
"name": "Sun",
"md5": "55c931c65456822c0c56a2b30e3e550d.svg",
"md5": "14b0508da0dc30e83b2bede13c1be1e9.svg",
"type": "costume",
"tags": [
"space",
@@ -9671,8 +9672,8 @@
"nuclear"
],
"info": [
72,
72,
54,
54,
1
]
},
@@ -10582,6 +10583,24 @@
1
]
},
{
"name": "Wizard Girl",
"md5": "dd40d3a2565990bcc24d2b36cd446394.svg",
"type": "costume",
"tags": [
"people",
"female",
"girl",
"wizard",
"magic",
"fantasy"
],
"info": [
80,
91,
1
]
},
{
"name": "Wizard Hat",
"md5": "2da561a7e5cbfcda613f4750020d4aa5.svg",
@@ -12682,4 +12701,4 @@
1
]
}
]
]
+162 -1
View File
@@ -90,6 +90,86 @@ import videoPet from './steps/video-pet.en.gif';
import videoAnimate from './steps/video-animate.en.gif';
import videoPop from './steps/video-pop.en.gif';
// Make it Fly
import flyChooseBackdrop from './steps/fly-choose-backdrop.en.gif';
import flyChooseCharacter from './steps/fly-choose-character.en.gif';
import flySaySomething from './steps/fly-say-something.en.gif';
import flyMoveArrows from './steps/fly-make-interactive.en.gif';
import flyChooseObject from './steps/fly-object-to-collect.en.gif';
import flyFlyingObject from './steps/fly-flying-heart.en.gif';
import flySelectFlyingSprite from './steps/fly-select-flyer.en.jpg';
import flyAddScore from './steps/fly-add-score.en.gif';
import flyKeepScore from './steps/fly-keep-score.en.gif';
import flyAddScenery from './steps/fly-choose-scenery.en.gif';
import flyMoveScenery from './steps/fly-move-scenery.en.gif';
import flySwitchLooks from './steps/fly-switch-costume.en.gif';
// Pong
import pongAddBackdrop from './steps/pong-add-backdrop.en.png';
import pongAddBallSprite from './steps/pong-add-ball-sprite.en.png';
import pongBounceAround from './steps/pong-bounce-around.en.gif';
import pongAddPaddle from './steps/pong-add-a-paddle.en.gif';
import pongMoveThePaddle from './steps/pong-move-the-paddle.en.gif';
import pongSelectBallSprite from './steps/pong-select-ball.en.png';
import pongAddMoreCodeToBall from './steps/pong-add-code-to-ball.en.png';
import pongAddAScore from './steps/pong-add-score.en.gif';
import pongChooseScoreFromMenu from './steps/pong-choose-score.en.png';
import pongInsertChangeScoreBlock from './steps/pong-insert-change-score.en.png';
import pongResetScore from './steps/pong-reset-score.en.png';
import pongAddLineSprite from './steps/pong-add-line.en.gif';
import pongGameOver from './steps/pong-game-over.en.png';
// Imagine If
import imagineTypeWhatYouWant from './steps/imagine-type-what-you-want.en.png';
import imagineClickGreenFlag from './steps/imagine-click-green-flag.en.png';
import imagineChooseBackdrop from './steps/imagine-choose-backdrop.en.png';
import imagineChooseSprite from './steps/imagine-choose-any-sprite.en.png';
import imagineFlyAround from './steps/imagine-fly-around.en.png';
import imagineChooseAnotherSprite from './steps/imagine-choose-another-sprite.en.png';
import imagineLeftRight from './steps/imagine-left-right.en.png';
import imagineUpDown from './steps/imagine-up-down.en.png';
import imagineChangeCostumes from './steps/imagine-change-costumes.en.png';
import imagineGlideToPoint from './steps/imagine-glide-to-point.en.png';
import imagineGrowShrink from './steps/imagine-grow-shrink.en.png';
import imagineChooseAnotherBackdrop from './steps/imagine-choose-another-backdrop.en.png';
import imagineSwitchBackdrops from './steps/imagine-switch-backdrops.en.gif';
import imagineRecordASound from './steps/imagine-record-a-sound.en.gif';
import imagineChooseSound from './steps/imagine-choose-sound.en.png';
// Add a Backdrop
import addBackdrop from './steps/add-backdrop.en.png';
// Add Effectsf
import addEffects from './steps/add-effects.en.png';
// Hide and Show
import hideAndShow from './steps/hide-show.en.png';
// Switch Costumes
import switchCostumes from './steps/switch-costumes.en.png';
// Change Size
import changeSize from './steps/change-size.en.png';
// Spin
import spinTurn from './steps/spin-turn.en.png';
import spinPointInDirection from './steps/spin-point-in-direction.en.png';
// Record a Sound
import recordASoundSoundsTab from './steps/record-a-sound-sounds-tab.en.png';
import recordASoundClickRecord from './steps/record-a-sound-click-record.en.png';
import recordASoundPressRecordButton from './steps/record-a-sound-press-record-button.en.png';
import recordASoundChooseSound from './steps/record-a-sound-choose-sound.en.png';
import recordASoundPlayYourSound from './steps/record-a-sound-play-your-sound.en.png';
// Use Arrow Keys
import moveArrowKeysLeftRight from './steps/move-arrow-keys-left-right.en.png';
import moveArrowKeysUpDown from './steps/move-arrow-keys-up-down.en.png';
// Glide Around
import glideAroundBackAndForth from './steps/glide-around-back-and-forth.en.png';
import glideAroundPoint from './steps/glide-around-point.en.png';
const enImages = {
// Intro
introMove: introMove,
@@ -181,8 +261,89 @@ const enImages = {
videoAddExtension: videoAddExtension,
videoPet: videoPet,
videoAnimate: videoAnimate,
videoPop: videoPop
videoPop: videoPop,
// Make it Fly
flyChooseBackdrop: flyChooseBackdrop,
flyChooseCharacter: flyChooseCharacter,
flySaySomething: flySaySomething,
flyMoveArrows: flyMoveArrows,
flyChooseObject: flyChooseObject,
flyFlyingObject: flyFlyingObject,
flySelectFlyingSprite: flySelectFlyingSprite,
flyAddScore: flyAddScore,
flyKeepScore: flyKeepScore,
flyAddScenery: flyAddScenery,
flyMoveScenery: flyMoveScenery,
flySwitchLooks: flySwitchLooks,
// Pong
pongAddBackdrop: pongAddBackdrop,
pongAddBallSprite: pongAddBallSprite,
pongBounceAround: pongBounceAround,
pongAddPaddle: pongAddPaddle,
pongMoveThePaddle: pongMoveThePaddle,
pongSelectBallSprite: pongSelectBallSprite,
pongAddMoreCodeToBall: pongAddMoreCodeToBall,
pongAddAScore: pongAddAScore,
pongChooseScoreFromMenu: pongChooseScoreFromMenu,
pongInsertChangeScoreBlock: pongInsertChangeScoreBlock,
pongResetScore: pongResetScore,
pongAddLineSprite: pongAddLineSprite,
pongGameOver: pongGameOver,
// Imagine If
imagineTypeWhatYouWant: imagineTypeWhatYouWant,
imagineClickGreenFlag: imagineClickGreenFlag,
imagineChooseBackdrop: imagineChooseBackdrop,
imagineChooseSprite: imagineChooseSprite,
imagineFlyAround: imagineFlyAround,
imagineChooseAnotherSprite: imagineChooseAnotherSprite,
imagineLeftRight: imagineLeftRight,
imagineUpDown: imagineUpDown,
imagineChangeCostumes: imagineChangeCostumes,
imagineGlideToPoint: imagineGlideToPoint,
imagineGrowShrink: imagineGrowShrink,
imagineChooseAnotherBackdrop: imagineChooseAnotherBackdrop,
imagineSwitchBackdrops: imagineSwitchBackdrops,
imagineRecordASound: imagineRecordASound,
imagineChooseSound: imagineChooseSound,
// Add a Backdrop
addBackdrop: addBackdrop,
// Add Effects
addEffects: addEffects,
// Hide and Show
hideAndShow: hideAndShow,
// Switch Costumes
switchCostumes: switchCostumes,
// Change Size
changeSize: changeSize,
// Spin
spinTurn: spinTurn,
spinPointInDirection: spinPointInDirection,
// Record a Sound
recordASoundSoundsTab: recordASoundSoundsTab,
recordASoundClickRecord: recordASoundClickRecord,
recordASoundPressRecordButton: recordASoundPressRecordButton,
recordASoundChooseSound: recordASoundChooseSound,
recordASoundPlayYourSound: recordASoundPlayYourSound,
// Use Arrow Keys
moveArrowKeysLeftRight: moveArrowKeysLeftRight,
moveArrowKeysUpDown: moveArrowKeysUpDown,
// Glide Around
glideAroundBackAndForth: glideAroundBackAndForth,
glideAroundPoint: glideAroundPoint
};
export {enImages};
File diff suppressed because it is too large. Load diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

@@ -74,6 +74,12 @@ const videos = {
'en': '3jvl8zgjo2',
'ja': 'q7zq9vu0wb',
'ja-Hira': 'q7zq9vu0wb'
},
'make-it-fly': {
en: 'zbtdx2dem9'
},
'pong-game': {
en: '8m48dv0ens'
}
};
+127 -68
View File
@@ -118,7 +118,7 @@
},
{
"name": "Andie",
"md5": "419ff5a76adbb4b076c072f68267fdeb.svg",
"md5": "d2c433e02481b10584e34ccdcf5a046a.svg",
"type": "sprite",
"tags": [
"sports",
@@ -138,7 +138,7 @@
"objName": "Andie",
"sounds": [
{
"soundName": "basketball bounce",
"soundName": "Basketball Bounce",
"soundID": -1,
"md5": "1727f65b5f22d151685b8e5917456a60.wav",
"sampleCount": 8099,
@@ -149,40 +149,40 @@
"costumes": [
{
"costumeName": "andie-a",
"baseLayerID": 0,
"baseLayerMD5": "419ff5a76adbb4b076c072f68267fdeb.svg",
"baseLayerID": -1,
"baseLayerMD5": "d2c433e02481b10584e34ccdcf5a046a.svg",
"bitmapResolution": 1,
"rotationCenterX": 86,
"rotationCenterY": 81
"rotationCenterX": 80,
"rotationCenterY": 65
},
{
"costumeName": "andie-b",
"baseLayerID": -1,
"baseLayerMD5": "a6a766912b4e43064ee3f389c72925b4.svg",
"baseLayerMD5": "045f92ad80d72521098ede6d7c80549a.svg",
"bitmapResolution": 1,
"rotationCenterX": 46,
"rotationCenterY": 106
"rotationCenterX": 40,
"rotationCenterY": 91
},
{
"costumeName": "andie-c",
"baseLayerID": -1,
"baseLayerMD5": "fac1025857d2c28391cb31f39a80df54.svg",
"baseLayerMD5": "c9bad70ad371b8d6d97daed7ab9def98.svg",
"bitmapResolution": 1,
"rotationCenterX": 52,
"rotationCenterY": 65
"rotationCenterX": 46,
"rotationCenterY": 49
},
{
"costumeName": "andie-d",
"baseLayerID": -1,
"baseLayerMD5": "7af50835e05d43e5e07cd2d641e7401e.svg",
"baseLayerMD5": "7c9db638cd671898ff07771112faff0f.svg",
"bitmapResolution": 1,
"rotationCenterX": 87,
"rotationCenterY": 72
"rotationCenterX": 71,
"rotationCenterY": 57
}
],
"currentCostumeIndex": 0,
"scratchX": -129,
"scratchY": -73,
"scratchX": -150,
"scratchY": 90,
"scale": 1,
"direction": 90,
"rotationStyle": "normal",
@@ -1442,7 +1442,7 @@
},
{
"name": "Ben",
"md5": "4386d05d5abd6e1aa0a93b7b447d1b39.svg",
"md5": "322e13bd17d02acfdc048a5ef2167be8.svg",
"type": "sprite",
"tags": [
"sports",
@@ -1460,18 +1460,18 @@
"objName": "Ben",
"sounds": [
{
"soundName": "pop",
"soundName": "Goal Cheer",
"soundID": -1,
"md5": "83a9787d4cb6f3b7632b4ddfebf74367.wav",
"sampleCount": 258,
"rate": 11025,
"format": ""
"md5": "a434069c58e79d42f5d21abb1c318919.wav",
"sampleCount": 84096,
"rate": 22050,
"format": "adpcm"
},
{
"soundName": "basketball bounce",
"soundName": "Referee Whistle",
"soundID": -1,
"md5": "1727f65b5f22d151685b8e5917456a60.wav",
"sampleCount": 8099,
"md5": "8468b9b3f11a665ee4d215afd8463b97.wav",
"sampleCount": 14034,
"rate": 22050,
"format": "adpcm"
}
@@ -1480,39 +1480,39 @@
{
"costumeName": "ben-a",
"baseLayerID": -1,
"baseLayerMD5": "4386d05d5abd6e1aa0a93b7b447d1b39.svg",
"baseLayerMD5": "322e13bd17d02acfdc048a5ef2167be8.svg",
"bitmapResolution": 1,
"rotationCenterX": 53,
"rotationCenterX": 54,
"rotationCenterY": 69
},
{
"costumeName": "ben-b",
"baseLayerID": -1,
"baseLayerMD5": "90db137cd0743204d02fdc9be1c8eb94.svg",
"baseLayerMD5": "59ba1e80ba75b1d06c37db4b9b4fde27.svg",
"bitmapResolution": 1,
"rotationCenterX": 53,
"rotationCenterY": 67
"rotationCenterX": 54,
"rotationCenterY": 69
},
{
"costumeName": "ben-c",
"baseLayerID": -1,
"baseLayerMD5": "5f05d41ede773b3bb379286e86a5b869.svg",
"baseLayerMD5": "940ec2fa85d666dde95a51bcbccd266e.svg",
"bitmapResolution": 1,
"rotationCenterX": 71,
"rotationCenterY": 72
"rotationCenterX": 73,
"rotationCenterY": 71
},
{
"costumeName": "ben-d",
"baseLayerID": -1,
"baseLayerMD5": "6f6e246567c0639c758ed4f6930be76f.svg",
"baseLayerMD5": "3043a50a91b9d484a8cd32eb82f2d88f.svg",
"bitmapResolution": 1,
"rotationCenterX": 42,
"rotationCenterY": 72
"rotationCenterX": 44,
"rotationCenterY": 71
}
],
"currentCostumeIndex": 0,
"scratchX": -63,
"scratchY": -94,
"scratchX": -15,
"scratchY": 93,
"scale": 1,
"direction": 90,
"rotationStyle": "normal",
@@ -5434,12 +5434,13 @@
},
{
"name": "Earth",
"md5": "d9df2cf4f6eb2460d0de3e4f1e05f40b.svg",
"md5": "64220361b80d2812e9f27fc88ef39027.svg",
"type": "sprite",
"tags": [
"space",
"planet",
"earth"
"earth",
"globe"
],
"info": [
0,
@@ -5460,17 +5461,17 @@
],
"costumes": [
{
"costumeName": "Earth",
"costumeName": "earth",
"baseLayerID": -1,
"baseLayerMD5": "d9df2cf4f6eb2460d0de3e4f1e05f40b.svg",
"baseLayerMD5": "64220361b80d2812e9f27fc88ef39027.svg",
"bitmapResolution": 1,
"rotationCenterX": 41,
"rotationCenterY": 40
"rotationCenterX": 55,
"rotationCenterY": 55
}
],
"currentCostumeIndex": 0,
"scratchX": 57,
"scratchY": -39,
"scratchX": 142,
"scratchY": -53,
"scale": 1,
"direction": 90,
"rotationStyle": "normal",
@@ -8842,7 +8843,7 @@
},
{
"name": "Jordyn",
"md5": "8355df83cca2baa0fdf6269d1311f1ea.svg",
"md5": "2f59adebc2c0feb9ebfabd7ee9642f2b.svg",
"type": "sprite",
"tags": [
"sports",
@@ -8854,16 +8855,24 @@
"info": [
0,
4,
1
2
],
"json": {
"objName": "Jordyn",
"sounds": [
{
"soundName": "basketball bounce",
"soundName": "Goal Cheer",
"soundID": -1,
"md5": "1727f65b5f22d151685b8e5917456a60.wav",
"sampleCount": 8099,
"md5": "199b30c8b4fe0642e849924bd1e1b463.wav",
"sampleCount": 84411,
"rate": 22050,
"format": "adpcm"
},
{
"soundName": "Referee Whistle",
"soundID": -1,
"md5": "7d91d95d841dc6cf1282914306a4674a.wav",
"sampleCount": 14238,
"rate": 22050,
"format": "adpcm"
}
@@ -8872,15 +8881,15 @@
{
"costumeName": "jordyn-a",
"baseLayerID": -1,
"baseLayerMD5": "8355df83cca2baa0fdf6269d1311f1ea.svg",
"baseLayerMD5": "2f59adebc2c0feb9ebfabd7ee9642f2b.svg",
"bitmapResolution": 1,
"rotationCenterX": 51,
"rotationCenterY": 63
"rotationCenterY": 62
},
{
"costumeName": "jordyn-b",
"baseLayerID": -1,
"baseLayerMD5": "2e2c43bbd7b18524fb132f1426ca3134.svg",
"baseLayerMD5": "075f382883e60dba8094b3dc30918f60.svg",
"bitmapResolution": 1,
"rotationCenterX": 51,
"rotationCenterY": 63
@@ -8888,23 +8897,23 @@
{
"costumeName": "jordyn-c",
"baseLayerID": -1,
"baseLayerMD5": "5c0d24c53805e16358a2f6f6cc4a456e.svg",
"baseLayerMD5": "fbe81c40d4a880c8799c458359758629.svg",
"bitmapResolution": 1,
"rotationCenterX": 68,
"rotationCenterY": 61
"rotationCenterY": 62
},
{
"costumeName": "jordyn-d",
"baseLayerID": -1,
"baseLayerMD5": "ebe2b20b99c1e1b373fd2b02be48aab9.svg",
"baseLayerMD5": "68a784d4d0340475a6e6feff3b28b470.svg",
"bitmapResolution": 1,
"rotationCenterX": 40,
"rotationCenterY": 61
"rotationCenterY": 62
}
],
"currentCostumeIndex": 0,
"scratchX": -164,
"scratchY": -41,
"scratchX": 61,
"scratchY": 93,
"scale": 1,
"direction": 90,
"rotationStyle": "normal",
@@ -14372,7 +14381,7 @@
},
{
"name": "Sun",
"md5": "55c931c65456822c0c56a2b30e3e550d.svg",
"md5": "14b0508da0dc30e83b2bede13c1be1e9.svg",
"type": "sprite",
"tags": [
"space",
@@ -14403,15 +14412,15 @@
{
"costumeName": "sun",
"baseLayerID": -1,
"baseLayerMD5": "55c931c65456822c0c56a2b30e3e550d.svg",
"baseLayerMD5": "14b0508da0dc30e83b2bede13c1be1e9.svg",
"bitmapResolution": 1,
"rotationCenterX": 72,
"rotationCenterY": 72
"rotationCenterX": 54,
"rotationCenterY": 54
}
],
"currentCostumeIndex": 0,
"scratchX": 16,
"scratchY": -28,
"scratchX": -3,
"scratchY": -42,
"scale": 1,
"direction": 90,
"rotationStyle": "normal",
@@ -15778,6 +15787,56 @@
"spriteInfo": {}
}
},
{
"name": "Wizard Girl",
"md5": "dd40d3a2565990bcc24d2b36cd446394.svg",
"type": "sprite",
"tags": [
"people",
"female",
"girl",
"wizard",
"magic",
"fantasy"
],
"info": [
0,
1,
1
],
"json": {
"objName": "Wizard Girl",
"sounds": [
{
"soundName": "Magic Spell",
"soundID": -1,
"md5": "1cb60ecdb1075c8769cb346d5c2a22c7.wav",
"sampleCount": 43077,
"rate": 22050,
"format": "adpcm"
}
],
"costumes": [
{
"costumeName": "wizard girl",
"baseLayerID": -1,
"baseLayerMD5": "dd40d3a2565990bcc24d2b36cd446394.svg",
"bitmapResolution": 1,
"rotationCenterX": 80,
"rotationCenterY": 91
}
],
"currentCostumeIndex": 0,
"scratchX": -147,
"scratchY": -71,
"scale": 1,
"direction": 90,
"rotationStyle": "normal",
"isDraggable": false,
"visible": true,
"spriteInfo": {}
}
},
{
"name": "Wizard Hat",
"md5": "2da561a7e5cbfcda613f4750020d4aa5.svg",
@@ -20412,4 +20471,4 @@
"spriteInfo": {}
}
}
]
]
+36 -13
View File
@@ -716,13 +716,16 @@ const xmlClose = '</xml>';
/**
* @param {!boolean} isStage - Whether the toolbox is for a stage-type target.
* @param {?string} targetId - The current editing target
* @param {?string} categoriesXML - null for default toolbox, or an XML string with <category> elements.
* @param {?Array.<object>} categoriesXML - optional array of `{id,xml}` for categories. This can include both core
* and other extensions: core extensions will be placed in the normal Scratch order; others will go at the bottom.
* @property {string} id - the extension / category ID.
* @property {string} xml - the `<category>...</category>` XML for this extension / category.
* @param {?string} costumeName - The name of the default selected costume dropdown.
* @param {?string} backdropName - The name of the default selected backdrop dropdown.
* @param {?string} soundName - The name of the default selected sound dropdown.
* @returns {string} - a ScratchBlocks-style XML document for the contents of the toolbox.
*/
const makeToolboxXML = function (isStage, targetId, categoriesXML,
const makeToolboxXML = function (isStage, targetId, categoriesXML = [],
costumeName = '', backdropName = '', soundName = '') {
const gap = [categorySeparator];
@@ -730,21 +733,41 @@ const makeToolboxXML = function (isStage, targetId, categoriesXML,
backdropName = xmlEscape(backdropName);
soundName = xmlEscape(soundName);
categoriesXML = categoriesXML.slice();
const moveCategory = categoryId => {
const index = categoriesXML.findIndex(categoryInfo => categoryInfo.id === categoryId);
if (index >= 0) {
// remove the category from categoriesXML and return its XML
const [categoryInfo] = categoriesXML.splice(index, 1);
return categoryInfo.xml;
}
// return `undefined`
};
const motionXML = moveCategory('motion') || motion(isStage, targetId);
const looksXML = moveCategory('looks') || looks(isStage, targetId, costumeName, backdropName);
const soundXML = moveCategory('sound') || sound(isStage, targetId, soundName);
const eventsXML = moveCategory('event') || events(isStage, targetId);
const controlXML = moveCategory('control') || control(isStage, targetId);
const sensingXML = moveCategory('sensing') || sensing(isStage, targetId);
const operatorsXML = moveCategory('operators') || operators(isStage, targetId);
const variablesXML = moveCategory('data') || variables(isStage, targetId);
const myBlocksXML = moveCategory('procedures') || myBlocks(isStage, targetId);
const everything = [
xmlOpen,
motion(isStage, targetId), gap,
looks(isStage, targetId, costumeName, backdropName), gap,
sound(isStage, targetId, soundName), gap,
events(isStage, targetId), gap,
control(isStage, targetId), gap,
sensing(isStage, targetId), gap,
operators(isStage, targetId), gap,
variables(isStage, targetId), gap,
myBlocks(isStage, targetId)
motionXML, gap,
looksXML, gap,
soundXML, gap,
eventsXML, gap,
controlXML, gap,
sensingXML, gap,
operatorsXML, gap,
variablesXML, gap,
myBlocksXML
];
if (categoriesXML) {
everything.push(gap, categoriesXML);
for (const extensionCategory of categoriesXML) {
everything.push(gap, extensionCategory.xml);
}
everything.push(xmlClose);
+14 -10
View File
@@ -8,6 +8,11 @@ const {By, until, Button} = webdriver;
const USE_HEADLESS = process.env.USE_HEADLESS !== 'no';
// The main reason for this timeout is so that we can control the timeout message and report details;
// if we hit the Jasmine default timeout then we get a terse message that we can't control.
// The Jasmine default timeout is 30 seconds so make sure this is lower.
const DEFAULT_TIMEOUT_MILLISECONDS = 20 * 1000;
class SeleniumHelper {
constructor () {
bindAll(this, [
@@ -21,13 +26,12 @@ class SeleniumHelper {
'getSauceDriver',
'getLogs',
'loadUri',
'rightClickText',
'waitUntilGone'
'rightClickText'
]);
}
elementIsVisible (element) {
return this.driver.wait(until.elementIsVisible(element));
elementIsVisible (element, timeoutMessage = 'elementIsVisible timed out') {
return this.driver.wait(until.elementIsVisible(element), DEFAULT_TIMEOUT_MILLISECONDS, timeoutMessage);
}
get scope () {
@@ -79,8 +83,12 @@ class SeleniumHelper {
return this.driver;
}
findByXpath (xpath) {
return this.driver.wait(until.elementLocated(By.xpath(xpath), 5 * 1000));
findByXpath (xpath, timeoutMessage = `findByXpath timed out for path: ${xpath}`) {
return this.driver.wait(until.elementLocated(By.xpath(xpath)), DEFAULT_TIMEOUT_MILLISECONDS, timeoutMessage)
.then(el => (
this.driver.wait(el.isDisplayed(), DEFAULT_TIMEOUT_MILLISECONDS, `${xpath} is not visible`)
.then(() => el)
));
}
findByText (text, scope) {
@@ -120,10 +128,6 @@ class SeleniumHelper {
return this.clickXpath(`//button//*[contains(text(), '${text}')]`);
}
waitUntilGone (element) {
return this.driver.wait(until.stalenessOf(element));
}
getLogs (whitelist) {
if (!whitelist) {
// Default whitelist
+47 -7
View File
@@ -25,7 +25,7 @@ describe('Working with backdrops', () => {
await driver.quit();
});
test('Adding a backdrop from the library', async () => {
test('Adding a backdrop from the library should not switch to stage', async () => {
await loadUri(uri);
// Start on the sounds tab of sprite1 to test switching behavior
@@ -37,12 +37,11 @@ describe('Working with backdrops', () => {
await el.sendKeys('blue');
await clickText('Blue Sky'); // Adds the backdrop
// Make sure the stage is selected and the sound tab remains selected.
// This is different from Scratch2 which selected backdrop tab automatically
// See issue #3500
await clickText('pop', scope.soundsTab);
// Make sure the sprite is still selected, and that the tab has not changed
await clickText('Meow', scope.soundsTab);
// Make sure the backdrop was actually added by going to the backdrops tab
await clickXpath('//span[text()="Stage"]');
await clickText('Backdrops');
await clickText('Blue Sky', scope.costumesTab);
@@ -50,7 +49,48 @@ describe('Working with backdrops', () => {
await expect(logs).toEqual([]);
});
test('Adding multiple backdrops at the same time', async () => {
test('Adding backdrop via paint should switch to stage', async () => {
await loadUri(uri);
const buttonXpath = '//button[@aria-label="Choose a Backdrop"]';
const paintXpath = `${buttonXpath}/following-sibling::div//button[@aria-label="Paint"]`;
const el = await findByXpath(buttonXpath);
await driver.actions().mouseMove(el)
.perform();
await driver.sleep(500); // Wait for thermometer menu to come up
await clickXpath(paintXpath);
// Stage should become selected and costume tab activated
await findByText('backdrop2', scope.costumesTab);
const logs = await getLogs();
await expect(logs).toEqual([]);
});
test('Adding backdrop via surprise should not switch to stage', async () => {
await loadUri(uri);
// Start on the sounds tab of sprite1 to test switching behavior
await clickText('Sounds');
const buttonXpath = '//button[@aria-label="Choose a Backdrop"]';
const surpriseXpath = `${buttonXpath}/following-sibling::div//button[@aria-label="Surprise"]`;
const el = await findByXpath(buttonXpath);
await driver.actions().mouseMove(el)
.perform();
await driver.sleep(500); // Wait for thermometer menu to come up
await clickXpath(surpriseXpath);
// Make sure the sprite is still selected, and that the tab has not changed
await clickText('Meow', scope.soundsTab);
const logs = await getLogs();
await expect(logs).toEqual([]);
});
test('Adding multiple backdrops from file should switch to stage', async () => {
const files = [
path.resolve(__dirname, '../fixtures/gh-3582-png.png'),
path.resolve(__dirname, '../fixtures/100-100.svg')
@@ -67,7 +107,7 @@ describe('Working with backdrops', () => {
const input = await findByXpath(fileXpath);
await input.sendKeys(files.join('\n'));
await clickXpath('//span[text()="Stage"]');
// Should have been switched to stage/costume tab already
await findByText('gh-3582-png', scope.costumesTab);
await findByText('100-100', scope.costumesTab);
+1 -5
View File
@@ -193,11 +193,7 @@ describe('Working with costumes', () => {
.mouseMove(abbyElement)
.perform();
// wait for one of Abby's alternate costumes to appear
const src1 = await abbyElement.findElement({css: 'img'}).getAttribute('src');
await driver.sleep(300);
const src2 = await abbyElement.findElement({css: 'img'}).getAttribute('src');
const sourcesMatch = (src1 === src2);
await expect(sourcesMatch).toBeFalsy(); // 'src' attribute should have changed by now
await findByXpath('//img[@src="https://cdn.assets.scratch.mit.edu/internalapi/asset/b6e23922f23b49ddc6f62f675e77417c.svg/get/"]');
const logs = await getLogs();
await expect(logs).toEqual([]);
});
+5 -7
View File
@@ -4,15 +4,13 @@ import path from 'path';
import SeleniumHelper from '../helpers/selenium-helper';
const {
findByText,
clickButton,
clickText,
clickXpath,
findByXpath,
getDriver,
getLogs,
loadUri,
waitUntilGone
loadUri
} = new SeleniumHelper();
let driver;
@@ -28,10 +26,9 @@ describe('player example', () => {
await driver.quit();
});
test('Load a project by ID', async () => {
test.skip('Player: load a project by ID', async () => {
const projectId = '96708228';
await loadUri(`${uri}#${projectId}`);
await waitUntilGone(findByText('Loading'));
await clickXpath('//img[@title="Go"]');
await new Promise(resolve => setTimeout(resolve, 2000));
await clickXpath('//img[@title="Stop"]');
@@ -59,7 +56,7 @@ describe('blocks example', () => {
await driver.quit();
});
test('Load a project by ID', async () => {
test.skip('Blocks: load a project by ID', async () => {
const projectId = '96708228';
await loadUri(`${uri}#${projectId}`);
await new Promise(resolve => setTimeout(resolve, 2000));
@@ -78,7 +75,8 @@ describe('blocks example', () => {
await expect(projectRequests).toEqual(['https://projects.scratch.mit.edu/96708228']);
});
test('Change categories', async () => {
// skipping per https://github.com/LLK/scratch-gui/issues/4902 until we have better approach
test.skip('Change categories', async () => {
await loadUri(`${uri}`);
await clickText('Looks');
await clickText('Sound');
+1 -4
View File
@@ -9,8 +9,7 @@ const {
getDriver,
loadUri,
rightClickText,
scope,
waitUntilGone
scope
} = new SeleniumHelper();
const uri = path.resolve(__dirname, '../../build/index.html');
@@ -78,7 +77,6 @@ describe('Menu bar settings', () => {
await clickText('File');
const input = await findByXpath('//input[@accept=".sb,.sb2,.sb3"]');
await input.sendKeys(path.resolve(__dirname, '../fixtures/project1.sb3'));
await waitUntilGone(findByText('Loading'));
// No replace alert since no changes were made
await findByText('project1-sprite');
});
@@ -95,7 +93,6 @@ describe('Menu bar settings', () => {
await input.sendKeys(path.resolve(__dirname, '../fixtures/project1.sb3'));
await driver.switchTo().alert()
.accept();
await waitUntilGone(findByText('Loading'));
await findByText('project1-sprite');
});
});
Loaded 100 of 105 files, more files were not shown because too many files have changed in this diff. Show more