From 628e00f4016725b3b3b8fba32d4231e33de6fde8 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 16 Sep 2019 22:34:32 -0400 Subject: [PATCH 01/38] when select dropdown is focused, clear any validation errors --- src/components/join-flow/birthdate-step.jsx | 9 ++++++++- src/components/join-flow/country-step.jsx | 6 +++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/components/join-flow/birthdate-step.jsx b/src/components/join-flow/birthdate-step.jsx index e22c7f22c..b4a726862 100644 --- a/src/components/join-flow/birthdate-step.jsx +++ b/src/components/join-flow/birthdate-step.jsx @@ -84,7 +84,8 @@ class BirthDateStep extends React.Component { const { errors, handleSubmit, - isSubmitting + isSubmitting, + setFieldError } = props; return ( setFieldError('birth_month', null)} + /* eslint-enable react/jsx-no-bind */ /> setFieldError('birth_year', null)} + /* eslint-enable react/jsx-no-bind */ />
diff --git a/src/components/join-flow/country-step.jsx b/src/components/join-flow/country-step.jsx index fbe53a7c4..ee9854250 100644 --- a/src/components/join-flow/country-step.jsx +++ b/src/components/join-flow/country-step.jsx @@ -64,7 +64,8 @@ class CountryStep extends React.Component { const { errors, handleSubmit, - isSubmitting + isSubmitting, + setFieldError } = props; return ( setFieldError('country', null)} + /* eslint-enable react/jsx-no-bind */ /> {/* note that this is a hidden checkbox the user will never see */} Date: Wed, 18 Sep 2019 10:26:37 -0400 Subject: [PATCH 02/38] Handle errors of captcha loading by setting error state on JoinFlow. --- src/components/join-flow/email-step.jsx | 11 +++++-- src/components/join-flow/join-flow.jsx | 10 +++++++ src/l10n.json | 1 + test/unit/components/email-step.test.jsx | 37 ++++++++++++++++++++++++ test/unit/components/join-flow.test.jsx | 13 +++++++++ 5 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/components/join-flow/email-step.jsx b/src/components/join-flow/email-step.jsx index f2d1e42a3..15ae71872 100644 --- a/src/components/join-flow/email-step.jsx +++ b/src/components/join-flow/email-step.jsx @@ -56,7 +56,11 @@ class EmailStep extends React.Component { this.emailInput = emailInputRef; } onCaptchaError () { - // TODO send user to error step once we have one. + this.props.onMidRegistrationError( + this.props.intl.formatMessage({ + id: 'registation.troubleReload' + }) + ); } onCaptchaLoad () { this.setState({captchaIsLoading: false}); @@ -64,9 +68,9 @@ class EmailStep extends React.Component { if (!this.grecaptcha) { // According to the reCaptcha documentation, this callback shouldn't get // called unless window.grecaptcha exists. This is just here to be extra defensive. - // TODO: Put up the error screen when we have one. + this.onCaptchaError(); + return; } - // TODO: Add in error callback for render once we have an error screen. this.widgetId = this.grecaptcha.render(this.captchaRef, { callback: this.captchaSolved, @@ -207,6 +211,7 @@ class EmailStep extends React.Component { EmailStep.propTypes = { intl: intlShape, + onMidRegistrationError: PropTypes.func, onNextStep: PropTypes.func, waiting: PropTypes.bool }; diff --git a/src/components/join-flow/join-flow.jsx b/src/components/join-flow/join-flow.jsx index 6b18d6a40..5eb8641be 100644 --- a/src/components/join-flow/join-flow.jsx +++ b/src/components/join-flow/join-flow.jsx @@ -23,6 +23,7 @@ class JoinFlow extends React.Component { super(props); bindAll(this, [ 'handleAdvanceStep', + 'handleMidRegistrationError', 'handlePrepareToRegister', 'handleRegistrationResponse', 'handleSubmitRegistration' @@ -34,6 +35,14 @@ class JoinFlow extends React.Component { waiting: false }; } + handleMidRegistrationError (message) { + if (!message) { + message = this.props.intl.formatMessage({ + id: 'registration.generalError' + }); + } + this.setState({registrationError: message}); + } handlePrepareToRegister (newFormData) { newFormData = newFormData || {}; const newState = { @@ -142,6 +151,7 @@ class JoinFlow extends React.Component { { expect(emailInputWrapper.props().onSetRef).toEqual(formikWrapper.instance().handleSetEmailRef); expect(emailInputWrapper.props().validate).toEqual(formikWrapper.instance().validateEmail); }); + test('props sent to FormikCheckbox for subscribe', () => { const wrapper = shallowWithIntl(); // Dive to get past the intl wrapper @@ -63,6 +64,7 @@ describe('EmailStep test', () => { expect(checkboxWrapper.first().props().label).toEqual('registration.receiveEmails'); expect(checkboxWrapper.first().props().name).toEqual('subscribe'); }); + test('handleValidSubmit passes formData to next step', () => { const formikBag = { setSubmitting: jest.fn() @@ -82,6 +84,7 @@ describe('EmailStep test', () => { expect(formikBag.setSubmitting).toHaveBeenCalledWith(false); expect(global.grecaptcha.execute).toHaveBeenCalled(); }); + test('captchaSolved sets token and goes to next step', () => { const props = { onNextStep: jest.fn() @@ -116,6 +119,38 @@ describe('EmailStep test', () => { })); expect(formikBag.setSubmitting).toHaveBeenCalledWith(true); }); + + test('onCaptchaError calls error function with correct message', () => { + const props = { + onMidRegistrationError: jest.fn() + }; + + const wrapper = shallowWithIntl( + ); + + const formikWrapper = wrapper.dive(); + formikWrapper.instance().onCaptchaError(); + expect(props.onMidRegistrationError).toHaveBeenCalledWith('registation.troubleReload'); + }); + + test('Captcha load error calls error function', () => { + const props = { + onMidRegistrationError: jest.fn() + }; + // Set this to null to force an error. + global.grecaptcha = null; + const wrapper = shallowWithIntl( + ); + + const formikWrapper = wrapper.dive(); + formikWrapper.instance().onCaptchaLoad(); + expect(props.onMidRegistrationError).toHaveBeenCalledWith('registation.troubleReload'); + }); + test('validateEmail test email empty', () => { const wrapper = shallowWithIntl( ); @@ -123,6 +158,7 @@ describe('EmailStep test', () => { const val = formikWrapper.instance().validateEmail(''); expect(val).toBe('general.required'); }); + test('validateEmail test email null', () => { const wrapper = shallowWithIntl( ); @@ -130,6 +166,7 @@ describe('EmailStep test', () => { const val = formikWrapper.instance().validateEmail(null); expect(val).toBe('general.required'); }); + test('validateEmail test email undefined', () => { const wrapper = shallowWithIntl( ); diff --git a/test/unit/components/join-flow.test.jsx b/test/unit/components/join-flow.test.jsx index 80dfe5a14..2714ae317 100644 --- a/test/unit/components/join-flow.test.jsx +++ b/test/unit/components/join-flow.test.jsx @@ -126,6 +126,19 @@ describe('JoinFlow', () => { expect(joinFlowInstance.props.refreshSession).not.toHaveBeenCalled(); expect(joinFlowInstance.state.registrationError).toBe('registration.generalError (400)'); }); + test('handleMidRegistrationError with no message ', () => { + const joinFlowInstance = getJoinFlowWrapper().instance(); + joinFlowInstance.setState({}); + joinFlowInstance.handleMidRegistrationError(); + expect(joinFlowInstance.state.registrationError).toBe('registration.generalError'); + }); + + test('handleMidRegistrationError with message ', () => { + const joinFlowInstance = getJoinFlowWrapper().instance(); + joinFlowInstance.setState({}); + joinFlowInstance.handleMidRegistrationError('my message'); + expect(joinFlowInstance.state.registrationError).toBe('my message'); + }); test('handleAdvanceStep', () => { const joinFlowInstance = getJoinFlowWrapper().instance(); From b98f8eda72757666d81f9f4ae8ccdb4b07d7dd83 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Thu, 19 Sep 2019 13:12:50 -0400 Subject: [PATCH 03/38] Hopefully fix captcha site key in prod. I think I made a copy/paste error originally. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b60270840..8879cfdf6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,7 +35,7 @@ env: - CLOUDDATA_HOST=${CLOUDDATA_HOST:-$CLOUDDATA_HOST_STAGING} - RECAPTCHA_SITE_KEY_master=6LeRbUwUAAAAAFYhKgk3G9OKWqE_OJ7Z-7VTUCbl - RECAPTCHA_SITE_KEY_STAGING=6LfukK4UAAAAAFR44yoZMhv8fj6xh-PMiIxwryG3 - - RECAPTCHA_SITE=RECAPTCHA_SITE_KEY_$TRAVIS_BRANCH + - RECAPTCHA_SITE_KEY_VAR=RECAPTCHA_SITE_KEY_$TRAVIS_BRANCH - RECAPTCHA_SITE_KEY=${!RECAPTCHA_SITE_KEY_VAR} - RECAPTCHA_SITE_KEY=${RECAPTCHA_SITE_KEY:-$RECAPTCHA_SITE_KEY_STAGING} - ROOT_URL_master=https://scratch.mit.edu From cdd90da423e59e825983858748617f85b29ba783 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Thu, 19 Sep 2019 13:40:09 -0400 Subject: [PATCH 04/38] Rename error function. --- src/components/join-flow/email-step.jsx | 4 ++-- src/components/join-flow/join-flow.jsx | 6 +++--- test/unit/components/email-step.test.jsx | 8 ++++---- test/unit/components/join-flow.test.jsx | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/components/join-flow/email-step.jsx b/src/components/join-flow/email-step.jsx index 15ae71872..803bd1bf8 100644 --- a/src/components/join-flow/email-step.jsx +++ b/src/components/join-flow/email-step.jsx @@ -56,7 +56,7 @@ class EmailStep extends React.Component { this.emailInput = emailInputRef; } onCaptchaError () { - this.props.onMidRegistrationError( + this.props.onRegistrationError( this.props.intl.formatMessage({ id: 'registation.troubleReload' }) @@ -211,8 +211,8 @@ class EmailStep extends React.Component { EmailStep.propTypes = { intl: intlShape, - onMidRegistrationError: PropTypes.func, onNextStep: PropTypes.func, + onRegistrationError: PropTypes.func, waiting: PropTypes.bool }; diff --git a/src/components/join-flow/join-flow.jsx b/src/components/join-flow/join-flow.jsx index 5eb8641be..6849531e6 100644 --- a/src/components/join-flow/join-flow.jsx +++ b/src/components/join-flow/join-flow.jsx @@ -23,7 +23,7 @@ class JoinFlow extends React.Component { super(props); bindAll(this, [ 'handleAdvanceStep', - 'handleMidRegistrationError', + 'handleRegistrationError', 'handlePrepareToRegister', 'handleRegistrationResponse', 'handleSubmitRegistration' @@ -35,7 +35,7 @@ class JoinFlow extends React.Component { waiting: false }; } - handleMidRegistrationError (message) { + handleRegistrationError (message) { if (!message) { message = this.props.intl.formatMessage({ id: 'registration.generalError' @@ -151,8 +151,8 @@ class JoinFlow extends React.Component { { test('onCaptchaError calls error function with correct message', () => { const props = { - onMidRegistrationError: jest.fn() + onRegistrationError: jest.fn() }; const wrapper = shallowWithIntl( @@ -132,12 +132,12 @@ describe('EmailStep test', () => { const formikWrapper = wrapper.dive(); formikWrapper.instance().onCaptchaError(); - expect(props.onMidRegistrationError).toHaveBeenCalledWith('registation.troubleReload'); + expect(props.onRegistrationError).toHaveBeenCalledWith('registation.troubleReload'); }); test('Captcha load error calls error function', () => { const props = { - onMidRegistrationError: jest.fn() + onRegistrationError: jest.fn() }; // Set this to null to force an error. global.grecaptcha = null; @@ -148,7 +148,7 @@ describe('EmailStep test', () => { const formikWrapper = wrapper.dive(); formikWrapper.instance().onCaptchaLoad(); - expect(props.onMidRegistrationError).toHaveBeenCalledWith('registation.troubleReload'); + expect(props.onRegistrationError).toHaveBeenCalledWith('registation.troubleReload'); }); test('validateEmail test email empty', () => { diff --git a/test/unit/components/join-flow.test.jsx b/test/unit/components/join-flow.test.jsx index 2714ae317..04cb1a6dc 100644 --- a/test/unit/components/join-flow.test.jsx +++ b/test/unit/components/join-flow.test.jsx @@ -126,17 +126,17 @@ describe('JoinFlow', () => { expect(joinFlowInstance.props.refreshSession).not.toHaveBeenCalled(); expect(joinFlowInstance.state.registrationError).toBe('registration.generalError (400)'); }); - test('handleMidRegistrationError with no message ', () => { + test('handleRegistrationError with no message ', () => { const joinFlowInstance = getJoinFlowWrapper().instance(); joinFlowInstance.setState({}); - joinFlowInstance.handleMidRegistrationError(); + joinFlowInstance.handleRegistrationError(); expect(joinFlowInstance.state.registrationError).toBe('registration.generalError'); }); - test('handleMidRegistrationError with message ', () => { + test('handleRegistrationError with message ', () => { const joinFlowInstance = getJoinFlowWrapper().instance(); joinFlowInstance.setState({}); - joinFlowInstance.handleMidRegistrationError('my message'); + joinFlowInstance.handleRegistrationError('my message'); expect(joinFlowInstance.state.registrationError).toBe('my message'); }); From aa6e85d863b4e7cd6460fb7e90771f3247c8116d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 20 Sep 2019 08:59:26 -0400 Subject: [PATCH 05/38] Include canonical link in project meta tags This will tell crawlers that this page and all its exact duplicates (/fullscreen, /editor, /embed, etc...) should be considered the same and use the project page as the canonical crawled page. We had an issue before where by not selected a canonical, google was considering all the project pages duplicates :( --- src/views/preview/meta.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/views/preview/meta.jsx b/src/views/preview/meta.jsx index 57d0758a3..67cb841da 100644 --- a/src/views/preview/meta.jsx +++ b/src/views/preview/meta.jsx @@ -4,7 +4,7 @@ const Helmet = require('react-helmet').default; const projectShape = require('./projectshape.jsx').projectShape; const Meta = props => { - const {title, instructions, author} = props.projectInfo; + const {id, title, instructions, author} = props.projectInfo; // Do not want to render any meta tags unless all the info is loaded // Check only author (object) because it is ok to have empty string instructions @@ -29,6 +29,10 @@ const Meta = props => { content={truncatedInstructions} property="og:description" /> + ); }; From bbbe0f8836ecd89b5c6f791f3c122a37b3d3cdef Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 20 Sep 2019 13:48:08 -0400 Subject: [PATCH 06/38] Fix linter issues with prop ordering --- src/views/preview/meta.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/preview/meta.jsx b/src/views/preview/meta.jsx index 67cb841da..d526515a1 100644 --- a/src/views/preview/meta.jsx +++ b/src/views/preview/meta.jsx @@ -30,8 +30,8 @@ const Meta = props => { property="og:description" /> ); From 0e7ed0e0fd4b99857d2a400df8b39233c0cd4a0d Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Fri, 20 Sep 2019 17:17:38 -0400 Subject: [PATCH 07/38] show info button in a way that does not increase line height --- src/components/info-button/info-button.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/info-button/info-button.scss b/src/components/info-button/info-button.scss index b776ee6a3..f2d93303d 100644 --- a/src/components/info-button/info-button.scss +++ b/src/components/info-button/info-button.scss @@ -7,6 +7,7 @@ width: 1rem; height: 1rem; margin-left: .375rem; + margin-top: -.25rem; border-radius: 50%; background-color: $type-gray-60percent; background-image: url("/svgs/info-button/info-button.svg"); From 6bcde6129b896fb9e0a42997913fc0bac84cc070 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Fri, 20 Sep 2019 17:17:53 -0400 Subject: [PATCH 08/38] shorter description line height --- src/components/join-flow/join-flow-step.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/join-flow/join-flow-step.scss b/src/components/join-flow/join-flow-step.scss index 5c27e4253..e7de82007 100644 --- a/src/components/join-flow/join-flow-step.scss +++ b/src/components/join-flow/join-flow-step.scss @@ -27,7 +27,7 @@ .join-flow-description { font-size: .875rem; font-weight: bold; - line-height: 1.37500rem; + line-height: 1.125rem; margin-top: 0.78125rem; margin-bottom: 1.875rem; text-align: center; From 106ccb34c691b1308cd02e7d001d622181142e6d Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Fri, 20 Sep 2019 17:18:26 -0400 Subject: [PATCH 09/38] make gender step height more flexible --- src/components/join-flow/join-flow-steps.scss | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/join-flow/join-flow-steps.scss b/src/components/join-flow/join-flow-steps.scss index abddb3b99..7488fd2b2 100644 --- a/src/components/join-flow/join-flow-steps.scss +++ b/src/components/join-flow/join-flow-steps.scss @@ -114,8 +114,6 @@ } .join-flow-inner-gender-step { - /* need height so that flex will adjust children proportionately */ - height: 27.25rem; padding-top: 2.625rem; padding-bottom: 1rem; } @@ -177,7 +175,7 @@ .gender-radio-row { transition: all .125s ease; width: 20.875rem; - height: 2.85rem; + min-height: 2.85rem; background-color: $ui-gray; border-radius: .5rem; margin: 0 auto 0.375rem; From d905f624ca232df0b3a7c12a5c250fdf610101e4 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Fri, 20 Sep 2019 18:22:15 -0400 Subject: [PATCH 10/38] gender step uses blank default, lcase values --- src/components/join-flow/gender-step.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/join-flow/gender-step.jsx b/src/components/join-flow/gender-step.jsx index 9148925ea..1aa358183 100644 --- a/src/components/join-flow/gender-step.jsx +++ b/src/components/join-flow/gender-step.jsx @@ -67,7 +67,7 @@ class GenderStep extends React.Component { handleValidSubmit (formData, formikBag) { formikBag.setSubmitting(false); if (!formData.gender || formData.gender === 'null') { - formData.gender = 'Prefer not to say'; + formData.gender = ''; // default to blank } delete formData.custom; this.props.onNextStep(formData); @@ -102,20 +102,20 @@ class GenderStep extends React.Component { id="GenderRadioOptionFemale" label={this.props.intl.formatMessage({id: 'general.female'})} selectedValue={values.gender} - value="Female" + value="female" onSetFieldValue={setFieldValue} />
Date: Sat, 21 Sep 2019 08:25:00 +0900 Subject: [PATCH 11/38] Update Code Club Link --- src/views/teachers/landing/landing.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/teachers/landing/landing.jsx b/src/views/teachers/landing/landing.jsx index 3b59db7a4..d4b0ac937 100644 --- a/src/views/teachers/landing/landing.jsx +++ b/src/views/teachers/landing/landing.jsx @@ -276,7 +276,7 @@ const Landing = props => ( id="teacherlanding.codeClub" values={{ codeClubLink: ( - + ) From 693c8e11e3760317b121f02460df3d87e43e3204 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 23 Sep 2019 10:36:58 -0400 Subject: [PATCH 12/38] new join flow modal stays open on click outside --- src/components/modal/join/modal.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/modal/join/modal.jsx b/src/components/modal/join/modal.jsx index d309433ee..287e6b31e 100644 --- a/src/components/modal/join/modal.jsx +++ b/src/components/modal/join/modal.jsx @@ -14,6 +14,7 @@ const JoinModal = ({ isOpen useStandardSizes className="mod-join" + shouldCloseOnOverlayClick={false} onRequestClose={onRequestClose} {...modalProps} > From 21e9494e1a96326390761c1b21a2807911c636d5 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 23 Sep 2019 11:45:17 -0400 Subject: [PATCH 13/38] Update BOOST troubleshooting to suggest Link update --- src/views/boost/boost.jsx | 4 ++-- src/views/boost/l10n.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/views/boost/boost.jsx b/src/views/boost/boost.jsx index 1e3203ea6..d4abeac54 100644 --- a/src/views/boost/boost.jsx +++ b/src/views/boost/boost.jsx @@ -208,9 +208,9 @@ class Boost extends ExtensionLanding {

-

+

- +

diff --git a/src/views/boost/l10n.json b/src/views/boost/l10n.json index 470781f62..b99a7a056 100644 --- a/src/views/boost/l10n.json +++ b/src/views/boost/l10n.json @@ -12,8 +12,8 @@ "boost.connectALegoBeam": "Connect a LEGO beam with an axle to motor A and click the block again to make it spin.", "boost.starterProjects": "Starter Projects", "boost.troubleshootingTitle": "Troubleshooting", - "boost.avoidFirmwareUpdateTitle": "Having trouble connecting or using motor blocks?", - "boost.avoidFirmwareUpdateText": "We suggest you avoid updating the firmware on your BOOST for now. We are working on a fix with the team at LEGO for users who have already updated their firmware. Stay tuned! We hope to have a fix released soon.", + "boost.updateScratchLinkTitle": "Make sure you have the latest version of Scratch Link", + "boost.updateScratchLinkText": "Install Scratch Link using the button above. We recommend using the app store installation process to help keep your version up to date.", "boost.checkOSVersionTitle": "Make sure your operating system is compatible with Scratch Link", "boost.checkOSVersionText": "The minimum operating system versions are listed at the top of this page. See instructions for checking your version of {winOSVersionLink} or {macOSVersionLink}.", "boost.winOSVersionLinkText": "Windows", From 865e0a09dd18f576f5b8b3fa37509c4909b86940 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 23 Sep 2019 13:23:38 -0400 Subject: [PATCH 14/38] give join-flow validations more spacing from inputs they refer to; unset width of Required message --- src/components/join-flow/join-flow-steps.scss | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/components/join-flow/join-flow-steps.scss b/src/components/join-flow/join-flow-steps.scss index abddb3b99..ad5f05b9f 100644 --- a/src/components/join-flow/join-flow-steps.scss +++ b/src/components/join-flow/join-flow-steps.scss @@ -39,17 +39,15 @@ } .validation-full-width-input { - transform: translate(21.5625rem, 0); + transform: translate(21.8125rem, 0); } .validation-birthdate-month { - transform: translate(-9.25rem, 0); - width: 7.25rem; + transform: translate(-9.875rem, 0); } .validation-birthdate-year { - transform: translate(8.75rem, 0); - width: 7.25rem; + transform: translate(9.375rem, 0); } @media #{$intermediate-and-smaller} { From d4c791adfb2fd3a0ded9c02a074976f403ae53d6 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 23 Sep 2019 15:16:20 -0400 Subject: [PATCH 15/38] set modal close icon to draggable false --- src/components/modal/base/modal.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/modal/base/modal.jsx b/src/components/modal/base/modal.jsx index 156d435ce..7353b9080 100644 --- a/src/components/modal/base/modal.jsx +++ b/src/components/modal/base/modal.jsx @@ -56,6 +56,7 @@ class Modal extends React.Component { close-icon

From e901deb3981aeeb313155fa9b1c5224bba762a44 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 23 Sep 2019 16:18:20 -0400 Subject: [PATCH 16/38] =?UTF-8?q?make=20validations=20be=20the=20same=20wi?= =?UTF-8?q?dth=20as=20inputs=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …when window is narrow --- src/components/join-flow/birthdate-step.jsx | 6 +++++- src/components/join-flow/country-step.jsx | 5 ++++- src/components/join-flow/join-flow-steps.scss | 18 ++++++++++++++++-- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/components/join-flow/birthdate-step.jsx b/src/components/join-flow/birthdate-step.jsx index 6849bc126..7e507468b 100644 --- a/src/components/join-flow/birthdate-step.jsx +++ b/src/components/join-flow/birthdate-step.jsx @@ -117,6 +117,7 @@ class BirthDateStep extends React.Component { options={birthMonthOptions} validate={this.validateSelect} validationClassName={classNames( + 'validation-birthdate', 'validation-birthdate-month', 'validation-left' )} @@ -135,7 +136,10 @@ class BirthDateStep extends React.Component { name="birth_year" options={birthYearOptions} validate={this.validateSelect} - validationClassName="validation-birthdate-year" + validationClassName={classNames( + 'validation-birthdate', + 'validation-birthdate-year' + )} /* eslint-disable react/jsx-no-bind */ onFocus={() => setFieldError('birth_year', null)} /* eslint-enable react/jsx-no-bind */ diff --git a/src/components/join-flow/country-step.jsx b/src/components/join-flow/country-step.jsx index 1e6ebd252..92ca4559c 100644 --- a/src/components/join-flow/country-step.jsx +++ b/src/components/join-flow/country-step.jsx @@ -93,7 +93,10 @@ class CountryStep extends React.Component { name="country" options={this.countryOptions} validate={this.validateSelect} - validationClassName="validation-full-width-input" + validationClassName={classNames( + 'validation-full-width-input', + 'validation-country' + )} /* eslint-disable react/jsx-no-bind */ onFocus={() => setFieldError('country', null)} /* eslint-enable react/jsx-no-bind */ diff --git a/src/components/join-flow/join-flow-steps.scss b/src/components/join-flow/join-flow-steps.scss index 7488fd2b2..ebfcb73f2 100644 --- a/src/components/join-flow/join-flow-steps.scss +++ b/src/components/join-flow/join-flow-steps.scss @@ -56,11 +56,25 @@ .validation-full-width-input { transform: unset; margin-bottom: .75rem; + max-width: 100%; } - .validation-birthdate-input { + .validation-country { + top: .5rem; + } + + .validation-birthdate { transform: unset; - width: 8rem; + top: .5rem; + width: 19rem; + } + + .validation-birthdate-month { + margin-right: -9.25rem; + } + + .validation-birthdate-year { + margin-left: -9.625rem; } } From 0d60b64c392b4aca469210e491084bf477497551 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Fri, 20 Sep 2019 14:40:49 -0400 Subject: [PATCH 17/38] Add a prop to Modal that allows you to hide the close button. Set it to show by default and have the standalone join flow page set it to hidden. --- src/components/modal/base/modal.jsx | 29 ++++++++++++++---------- src/components/modal/join/modal.jsx | 4 +++- src/views/join/join.jsx | 2 ++ test/unit/components/modal.test.jsx | 34 +++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 test/unit/components/modal.test.jsx diff --git a/src/components/modal/base/modal.jsx b/src/components/modal/base/modal.jsx index 7353b9080..0203a0f41 100644 --- a/src/components/modal/base/modal.jsx +++ b/src/components/modal/base/modal.jsx @@ -49,17 +49,20 @@ class Modal extends React.Component { }} {...omit(this.props, ['className', 'overlayClassName'])} > -
- close-icon -
+ + {this.props.showCloseButton && ( +
+ close-icon +
+ )} {this.props.children} ); @@ -70,7 +73,11 @@ Modal.propTypes = { children: PropTypes.node, className: PropTypes.string, overlayClassName: PropTypes.string, + showCloseButton: PropTypes.bool, useStandardSizes: PropTypes.bool }; +Modal.defaultProps = { + showCloseButton: true +}; module.exports = Modal; diff --git a/src/components/modal/join/modal.jsx b/src/components/modal/join/modal.jsx index 287e6b31e..bd17ac6ab 100644 --- a/src/components/modal/join/modal.jsx +++ b/src/components/modal/join/modal.jsx @@ -12,6 +12,7 @@ const JoinModal = ({ }) => ( ( ); diff --git a/test/unit/components/modal.test.jsx b/test/unit/components/modal.test.jsx new file mode 100644 index 000000000..6c427dc2f --- /dev/null +++ b/test/unit/components/modal.test.jsx @@ -0,0 +1,34 @@ +const React = require('react'); +const {shallowWithIntl} = require('../../helpers/intl-helpers.jsx'); +const Modal = require('../../../src/components/modal/base/modal.jsx'); + +describe('Modal', () => { + test('Close button not shown when showCloseButton false', () => { + const showClose = true; + const component = shallowWithIntl( + + ); + expect(component.find('div.modal-content-close').exists()).toBe(true); + expect(component.find('img.modal-content-close-img').exists()).toBe(true); + }); + test('Close button shown by default', () => { + const component = shallowWithIntl( + + ); + expect(component.find('div.modal-content-close').exists()).toBe(true); + expect(component.find('img.modal-content-close-img').exists()).toBe(true); + }); + + test('Close button shown when showCloseButton true', () => { + const showClose = false; + const component = shallowWithIntl( + + ); + expect(component.find('div.modal-content-close').exists()).toBe(false); + expect(component.find('img.modal-content-close-img').exists()).toBe(false); + }); +}); From 1a580957821e21c9e6199b8b4e1543f64094ffb5 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Mon, 23 Sep 2019 16:15:20 -0400 Subject: [PATCH 18/38] Put values directly in render props. --- src/views/join/join.jsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/views/join/join.jsx b/src/views/join/join.jsx index 3064ec08b..60379fd86 100644 --- a/src/views/join/join.jsx +++ b/src/views/join/join.jsx @@ -5,14 +5,12 @@ const ErrorBoundary = require('../../components/errorboundary/errorboundary.jsx' // Require this even though we don't use it because, without it, webpack runs out of memory... const Page = require('../../components/page/www/page.jsx'); // eslint-disable-line no-unused-vars -const openModal = true; -const showCloseButton = false; const Register = () => ( ); From fd131b84c15f5410cd7ca3f059fd725d45c5ff6d Mon Sep 17 00:00:00 2001 From: picklesrus Date: Mon, 23 Sep 2019 18:04:59 -0400 Subject: [PATCH 19/38] Add Logo to join flow standalone page. Will need some css work, especially for small screens. --- src/views/join/join.jsx | 13 +++++++++++++ src/views/join/join.scss | 29 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/views/join/join.scss diff --git a/src/views/join/join.jsx b/src/views/join/join.jsx index 60379fd86..a62e7d449 100644 --- a/src/views/join/join.jsx +++ b/src/views/join/join.jsx @@ -5,8 +5,21 @@ const ErrorBoundary = require('../../components/errorboundary/errorboundary.jsx' // Require this even though we don't use it because, without it, webpack runs out of memory... const Page = require('../../components/page/www/page.jsx'); // eslint-disable-line no-unused-vars +require('./join.scss'); const Register = () => ( +
+ + + + +
Date: Tue, 24 Sep 2019 14:34:02 -0400 Subject: [PATCH 20/38] Use a feature flag to contain new Scratch Desktop updates --- .../extension-landing/extension-landing.jsx | 12 +- .../install-scratch-link.jsx | 9 +- .../install-scratch/install-scratch.jsx | 146 +++++++++ .../install-scratch/install-scratch.scss | 95 ++++++ .../install-scratch/install-util.js | 13 + src/components/os-chooser/os-chooser.jsx | 25 +- src/l10n.json | 10 +- src/lib/detect-os.js | 18 ++ src/lib/feature-flags.js | 10 + .../extension-landing => lib}/os-enum.js | 4 +- src/views/download/download.jsx | 299 ++++++++++-------- src/views/download/download.scss | 13 + src/views/download/l10n.json | 19 +- src/views/ev3/ev3.jsx | 2 +- src/views/microbit/microbit.jsx | 2 +- static/images/badges/google-play-badge.png | Bin 0 -> 16087 bytes static/images/badges/mac-store-badge.svg | 46 +++ static/images/badges/windows-store-badge.svg | 82 +++++ static/svgs/download/mac-badge.svg | 46 +++ static/svgs/download/ms-badge.svg | 82 +++++ 20 files changed, 768 insertions(+), 165 deletions(-) create mode 100644 src/components/install-scratch/install-scratch.jsx create mode 100644 src/components/install-scratch/install-scratch.scss create mode 100644 src/components/install-scratch/install-util.js create mode 100644 src/lib/detect-os.js create mode 100644 src/lib/feature-flags.js rename src/{components/extension-landing => lib}/os-enum.js (51%) create mode 100644 static/images/badges/google-play-badge.png create mode 100755 static/images/badges/mac-store-badge.svg create mode 100755 static/images/badges/windows-store-badge.svg create mode 100755 static/svgs/download/mac-badge.svg create mode 100755 static/svgs/download/ms-badge.svg diff --git a/src/components/extension-landing/extension-landing.jsx b/src/components/extension-landing/extension-landing.jsx index 44cb3fb01..c883d294f 100644 --- a/src/components/extension-landing/extension-landing.jsx +++ b/src/components/extension-landing/extension-landing.jsx @@ -1,7 +1,7 @@ const bindAll = require('lodash.bindall'); const React = require('react'); -const OS_ENUM = require('./os-enum.js'); +const detectOS = require('../../lib/detect-os.js').default; class ExtensionLanding extends React.Component { constructor (props) { @@ -10,16 +10,8 @@ class ExtensionLanding extends React.Component { 'onSetOS' ]); - // @todo use bowser for browser detection - let detectedOS = OS_ENUM.WINDOWS; - if (window.navigator && window.navigator.platform) { - if (window.navigator.platform === 'MacIntel') { - detectedOS = OS_ENUM.MACOS; - } - } - this.state = { - OS: detectedOS + OS: detectOS() }; } diff --git a/src/components/extension-landing/install-scratch-link.jsx b/src/components/extension-landing/install-scratch-link.jsx index b8186fbcf..507cdbeb7 100644 --- a/src/components/extension-landing/install-scratch-link.jsx +++ b/src/components/extension-landing/install-scratch-link.jsx @@ -2,13 +2,14 @@ const PropTypes = require('prop-types'); const FormattedMessage = require('react-intl').FormattedMessage; const React = require('react'); -const OS_ENUM = require('./os-enum.js'); +const OS_ENUM = require('../../lib/os-enum.js'); const FlexRow = require('../../components/flex-row/flex-row.jsx'); const Steps = require('../../components/steps/steps.jsx'); const Step = require('../../components/steps/step.jsx'); require('./extension-landing.scss'); +// Assumes this will only be called with an OS that needs Scratch Link const InstallScratchLink = ({ currentOS }) => ( @@ -37,20 +38,20 @@ const InstallScratchLink = ({ - + - +
diff --git a/src/components/install-scratch/install-scratch.jsx b/src/components/install-scratch/install-scratch.jsx new file mode 100644 index 000000000..eff760cff --- /dev/null +++ b/src/components/install-scratch/install-scratch.jsx @@ -0,0 +1,146 @@ +const PropTypes = require('prop-types'); +const FormattedMessage = require('react-intl').FormattedMessage; +const React = require('react'); + +const OS_ENUM = require('../../lib/os-enum.js'); +const {CHROME_APP_RELEASED} = require('../../lib/feature-flags.js'); + +const {isDownloaded, isFromGooglePlay} = require('./install-util.js'); + +const FlexRow = require('../../components/flex-row/flex-row.jsx'); +const Steps = require('../../components/steps/steps.jsx'); +const Step = require('../../components/steps/step.jsx'); + +require('./install-scratch.scss'); + +const InstallScratch = ({ + currentOS +}) => ( +
+ +

+ {CHROME_APP_RELEASED ? ( + + ) : ( + + {isDownloaded(currentOS) && ( + + )} + {isFromGooglePlay(currentOS) && ( + + )} + + )} +

+ +
+ + + + {isDownloaded(currentOS) && ( + + )} + {isFromGooglePlay(currentOS) && ( + + )} + + +
+ {currentOS === OS_ENUM.WINDOWS && ( + + + + )} + {currentOS === OS_ENUM.MACOS && ( + + + + )} + {isFromGooglePlay(currentOS) && ( + + + + + )} + {isDownloaded(currentOS) && ( + + + + + + + + + )} +
+
+ +
+ {isDownloaded(currentOS) && ( + + + {currentOS === OS_ENUM.WINDOWS ? + : + + } + + +
+ +
+
+ )} +
+
+
+); + +InstallScratch.propTypes = { + currentOS: PropTypes.string +}; + +module.exports = InstallScratch; diff --git a/src/components/install-scratch/install-scratch.scss b/src/components/install-scratch/install-scratch.scss new file mode 100644 index 000000000..953b93d5a --- /dev/null +++ b/src/components/install-scratch/install-scratch.scss @@ -0,0 +1,95 @@ +@import "../../colors"; +@import "../../frameless"; + +#view { + padding: 0; +} + +.install-scratch { + padding: 2rem 0; + + .inner { + align-items: flex-start; + } + + .downloads-container { + text-align: center; + + .horizontal-divider { + display: block; + margin: 20px; + } + + .horizontal-divider:before, + .horizontal-divider:after { + display: inline-block; + position: relative; + background-color: $ui-dark-gray; + width: 50%; + height: 1px; + vertical-align: middle; + content: ""; + } + + .horizontal-divider:before { + right: .5em; + margin-left: -50%; + } + + .horizontal-divider:after { + left: .5em; + margin-right: -50%; + } + } + + .step-image { + height: 14rem; + } + + .title { + margin-bottom: 2rem; + font-size: 2rem; + } + + .legacy-link { + display: flex; + } + + .download-button { + display: inline-block; + margin: .5em 0; + border: 0; + border-radius: 8px; + background-color: $ui-blue; + cursor: pointer; + padding: 1rem 2rem; + color: $ui-white; + font-size: 1rem; + } + + .macos-badge img { + height: 50px; + } + + .ms-badge img { + height: 50px; + } + + .play-badge img { + height: 50px; + } + + .download-image { + width: 100%; + max-width: $cols6; + + img { + max-width: 100%; + max-height: 100%; + } + } + + .blue { + background-color: $ui-blue-10percent; + } +} diff --git a/src/components/install-scratch/install-util.js b/src/components/install-scratch/install-util.js new file mode 100644 index 000000000..20f667c21 --- /dev/null +++ b/src/components/install-scratch/install-util.js @@ -0,0 +1,13 @@ +const OS_ENUM = require('../../lib/os-enum.js'); + +module.exports = {}; + +module.exports.isDownloaded = os => { + if (os === OS_ENUM.WINDOWS || os === OS_ENUM.MACOS) return true; + return false; +}; + +module.exports.isFromGooglePlay = os => { + if (os === OS_ENUM.ANDROID || os === OS_ENUM.CHROMEOS) return true; + return false; +}; diff --git a/src/components/os-chooser/os-chooser.jsx b/src/components/os-chooser/os-chooser.jsx index 977c207ea..5d77d19e2 100644 --- a/src/components/os-chooser/os-chooser.jsx +++ b/src/components/os-chooser/os-chooser.jsx @@ -4,11 +4,12 @@ const FormattedMessage = require('react-intl').FormattedMessage; const PropTypes = require('prop-types'); const React = require('react'); +const {CHROME_APP_RELEASED} = require('../../lib/feature-flags.js'); const FlexRow = require('../../components/flex-row/flex-row.jsx'); const Button = require('../../components/forms/button.jsx'); -const OS_ENUM = require('../../components/extension-landing/os-enum.js'); +const OS_ENUM = require('../../lib/os-enum.js'); require('./os-chooser.scss'); @@ -34,6 +35,28 @@ const OSChooser = props => ( macOS + {CHROME_APP_RELEASED && ( + + + + + )} ); diff --git a/src/l10n.json b/src/l10n.json index 9b9032b7a..a72a5de67 100644 --- a/src/l10n.json +++ b/src/l10n.json @@ -133,10 +133,16 @@ "oschooser.choose": "Choose your OS:", + "installScratch.or": "or", + "installScratch.directDownload": "Direct download", + "installScratch.desktopHeaderTitle": "Install Scratch Desktop", + "installScratch.appHeaderTitle": "Install Scratch for {operatingsystem}", + "installScratch.downloadScratchDesktop": "Download Scratch Desktop", + "installScratch.downloadScratchAppGeneric": "Download Scratch for {operatingsystem}", + "installScratch.getScratchAppPlay": "Get Scratch on the Google Play Store", + "installScratchLink.installHeaderTitle": "Install Scratch Link", "installScratchLink.downloadAndInstall": "Download and install Scratch Link.", - "installScratchLink.or": "or", - "installScratchLink.directDownload": "Direct download", "installScratchLink.startScratchLink": "Start Scratch Link and make sure it is running. It should appear in your toolbar.", "parents.FaqAgeRangeA": "While Scratch is primarily designed for 8 to 16 year olds, it is also used by people of all ages, including younger children with their parents.", diff --git a/src/lib/detect-os.js b/src/lib/detect-os.js new file mode 100644 index 000000000..fa94c3229 --- /dev/null +++ b/src/lib/detect-os.js @@ -0,0 +1,18 @@ +import bowser from 'bowser'; +import OS_ENUM from './os-enum.js'; +import {CHROME_APP_RELEASED} from './feature-flags.js'; + +/** + * Helper function to the current Operating System. + * @returns {OS_ENUM} Returns the OS value, defaults to WINDOWS + */ +export default function () { + // matching OS strings from https://github.com/lancedikson/bowser/blob/master/src/constants.js + if (bowser.osname === 'macOS') return OS_ENUM.MACOS; + if (CHROME_APP_RELEASED) { + if (bowser.osname === 'Chrome OS') return OS_ENUM.CHROMEOS; + if (bowser.osname === 'Android') return OS_ENUM.ANDROID; + } + // if (bowser.osname === 'iOS') return OS_ENUM.IOS; // @todo + return OS_ENUM.WINDOWS; +} diff --git a/src/lib/feature-flags.js b/src/lib/feature-flags.js new file mode 100644 index 000000000..2965c1e9c --- /dev/null +++ b/src/lib/feature-flags.js @@ -0,0 +1,10 @@ +const isStaging = () => process.env.SCRATCH_ENV === 'staging'; + +const flagInUrl = flag => { + const url = (window.location && window.location.search) || ''; + return url.indexOf(`${flag}=true`) !== -1; +}; + +module.exports = { + CHROME_APP_RELEASED: isStaging() && flagInUrl('CHROME_APP_RELEASED') +}; diff --git a/src/components/extension-landing/os-enum.js b/src/lib/os-enum.js similarity index 51% rename from src/components/extension-landing/os-enum.js rename to src/lib/os-enum.js index b8cde494e..24d41c018 100644 --- a/src/components/extension-landing/os-enum.js +++ b/src/lib/os-enum.js @@ -1,6 +1,8 @@ const OS_ENUM = { WINDOWS: 'Windows', - MACOS: 'macOS' + MACOS: 'macOS', + CHROMEOS: 'ChromeOS', + ANDROID: 'Android' }; module.exports = OS_ENUM; diff --git a/src/views/download/download.jsx b/src/views/download/download.jsx index 2c935f04b..b0f77a4a8 100644 --- a/src/views/download/download.jsx +++ b/src/views/download/download.jsx @@ -5,13 +5,14 @@ const React = require('react'); const FlexRow = require('../../components/flex-row/flex-row.jsx'); const bindAll = require('lodash.bindall'); -const Steps = require('../../components/steps/steps.jsx'); -const Step = require('../../components/steps/step.jsx'); const Page = require('../../components/page/www/page.jsx'); const render = require('../../lib/render.jsx'); -const OS_ENUM = require('../../components/extension-landing/os-enum.js'); +const detectOS = require('../../lib/detect-os.js').default; +const {CHROME_APP_RELEASED} = require('../../lib/feature-flags.js'); const OSChooser = require('../../components/os-chooser/os-chooser.jsx'); +const InstallScratch = require('../../components/install-scratch/install-scratch.jsx'); +const {isDownloaded, isFromGooglePlay} = require('../../components/install-scratch/install-util.js'); require('./download.scss'); require('../../components/forms/button.scss'); @@ -22,15 +23,9 @@ class Download extends React.Component { bindAll(this, [ 'onSetOS' ]); - let detectedOS = OS_ENUM.WINDOWS; - if (window.navigator && window.navigator.platform) { - if (window.navigator.platform === 'MacIntel') { - detectedOS = OS_ENUM.MACOS; - } - } this.state = { - OS: detectedOS + OS: detectOS() }; } @@ -55,10 +50,16 @@ class Download extends React.Component { src="/images/download/icon.png" width="40" /> - + - + @@ -80,6 +81,24 @@ class Download extends React.Component { /> macOS 10.13+ + {CHROME_APP_RELEASED && ( + + + + ChromeOS + + + + Android 5.0+ + + + )} @@ -95,89 +114,150 @@ class Download extends React.Component { currentOS={this.state.OS} handleSetOS={this.onSetOS} /> -
- -

- -

- -
- - - - -
+ + {isDownloaded(this.state.OS) && ( +
+ +

+ +

+

+ +

+ + - - -
- - - {this.state.OS === OS_ENUM.WINDOWS ? - : - - } - - -
- +

-
- - -
+
+ + + +

+ + + + +

+
+ + +
+ )}

+ {isDownloaded(this.state.OS) && ( + +

+ +

+

+ +

+
+ )} + {isFromGooglePlay(this.state.OS) && ( + +

+ +

+

+ +

+
+ )}

- -

-

- -

-

- + {isFromGooglePlay(this.state.OS) ? + : + + }

-

- -

-

- -

-

- -

-

- -

+ {isDownloaded(this.state.OS) && (CHROME_APP_RELEASED ? ( + +

+ +

+

+ +

+
+ ) : ( + +

+ +

+

+ +

+
+ ))} + {isFromGooglePlay(this.state.OS) && ( + +

+ +

+

+ +

+
+ )} + {!CHROME_APP_RELEASED && ( + +

+ +

+

+ +

+
+ )}

@@ -186,64 +266,7 @@ class Download extends React.Component {

-
- -

- -

-

- -

- -
- - - -

- - - - -

-
-
- - - -

- - - - -

-
-
-
-
+
); diff --git a/src/views/download/download.scss b/src/views/download/download.scss index e53275950..3e2b4064c 100644 --- a/src/views/download/download.scss +++ b/src/views/download/download.scss @@ -27,6 +27,18 @@ font-size: 1rem; } + .macos-badge img { + height: 50px; + } + + .ms-badge img { + height: 50px; + } + + .play-badge img { + height: 50px; + } + .download-header { background-color: $ui-blue; padding: 4rem 0; @@ -80,6 +92,7 @@ .download-requirements { justify-content: space-between; + line-height: 2rem; } .download-requirements span { diff --git a/src/views/download/l10n.json b/src/views/download/l10n.json index e475c8a75..c1559b070 100644 --- a/src/views/download/l10n.json +++ b/src/views/download/l10n.json @@ -1,11 +1,10 @@ { "download.title": "Scratch Desktop", "download.intro": "You can install the Scratch Desktop editor to work on projects without an internet connection. This version will work on Windows and MacOS.", + "download.appTitle": "Download Scratch", + "download.appIntro": "You can install Scratch for free to work on projects without an internet connection.", "download.requirements": "Requirements", "download.imgAltDownloadIllustration" : "Scratch 3.0 Desktop screenshot", - "download.installHeaderTitle": "Install Scratch Desktop", - "download.downloadScratchDesktop": "Download Scratch Desktop", - "download.downloadButton": "Download", "download.troubleshootingTitle": "FAQ", "download.startScratchDesktop": "Start Scratch Desktop", "download.howDoIInstall": "How do I install Scratch Desktop?", @@ -14,17 +13,23 @@ "download.supportChromeOS" : "When will you have Scratch Desktop for Chromebooks?", "download.supportChromeOSAnswer": "Scratch Desktop for Chromebooks is not yet available. We are working on it and expect to release later in 2019.", "download.olderVersionsTitle" : "Older Versions", - "download.olderVersions": "Looking for the Scratch 2.0 Offline Editor or Scratch 1.4?", - "download.scratch1-4Desktop" : "Scratch 1.4 Desktop", - "download.scratch2Desktop" : "Scratch 2.0 Desktop", + "download.olderVersions": "Looking for earlier Scratch Offline Editors?", + "download.scratch1-4Desktop" : "Scratch 1.4", + "download.scratch2Desktop" : "Scratch 2.0 Offline Editor", "download.cannotAccessMacStore" : "What if I can't access the Mac App Store?", "download.cannotAccessWindowsStore" : "What if I can't access the Microsoft Store?", "download.macMoveToApplications" : "Open the .dmg file. Move Scratch Desktop into Applications.", "download.winMoveToApplications" : "Run the .exe file.", "download.canIUseScratchLink" : "Can I use Scratch Link to connect to extensions?", "download.canIUseScratchLinkAnswer" : "Yes. However, you will need an Internet connection to use Scratch Link.", + "download.canIUseExtensions" : "Can I connect to hardware extensions?", + "download.canIUseExtensionsAnswer" : "Yes. With the Scratch app you can connect to extensions, and you do not need Scratch Link.", "download.desktopAndBrowser": "Can I use Scratch Desktop and also have Scratch open in the browser?", + "download.appAndBrowser": "Can I use the Scratch app and also have Scratch open in the browser?", "download.yesAnswer" : "Yes.", "download.canIShare": "Can I share from Scratch Desktop?", - "download.canIShareAnswer": "This isn’t supported currently. For now, you can save a project from Scratch Desktop, upload it to your Scratch account, and share it there. In a later version we will add the ability to upload to your Scratch account directly in Scratch Desktop." + "download.canIShareAnswer": "This isn’t supported currently. For now, you can save a project from Scratch Desktop, upload it to your Scratch account, and share it there. In a later version we will add the ability to upload to your Scratch account directly in Scratch Desktop.", + "download.canIShareApp": "Can I share from Scratch for {operatingsystem}?", + "download.canIShareAnswerPlayStore": "Yes. Click the 3-dots menu on a project in the lobby and select Share from the options. In addition to sharing by email you can sign in to your Scratch account and share the project on the Scratch Community.", + "download.canIShareAnswerDownloaded": "This isn’t supported currently. For now, you can save a project from Scratch for {operatingsystem}, upload it to your Scratch account, and share it there. In a later version we will add the ability to upload to your Scratch account directly in Scratch for {operatingsystem}." } diff --git a/src/views/ev3/ev3.jsx b/src/views/ev3/ev3.jsx index a03b55a14..6666490e9 100644 --- a/src/views/ev3/ev3.jsx +++ b/src/views/ev3/ev3.jsx @@ -23,7 +23,7 @@ const ProjectCard = require('../../components/extension-landing/project-card.jsx const Steps = require('../../components/steps/steps.jsx'); const Step = require('../../components/steps/step.jsx'); -const OS_ENUM = require('../../components/extension-landing/os-enum.js'); +const OS_ENUM = require('../../lib/os-enum.js'); require('../../components/extension-landing/extension-landing.scss'); require('./ev3.scss'); diff --git a/src/views/microbit/microbit.jsx b/src/views/microbit/microbit.jsx index 1b36c4ba3..ac274423b 100644 --- a/src/views/microbit/microbit.jsx +++ b/src/views/microbit/microbit.jsx @@ -22,7 +22,7 @@ const Button = require('../../components/forms/button.jsx'); const Steps = require('../../components/steps/steps.jsx'); const Step = require('../../components/steps/step.jsx'); -const OS_ENUM = require('../../components/extension-landing/os-enum.js'); +const OS_ENUM = require('../../lib/os-enum.js'); require('../../components/extension-landing/extension-landing.scss'); require('./microbit.scss'); diff --git a/static/images/badges/google-play-badge.png b/static/images/badges/google-play-badge.png new file mode 100644 index 0000000000000000000000000000000000000000..27392d21753e7e06361a69aa19cbb771bbbb288e GIT binary patch literal 16087 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 fsQ>tuapn0PWb2aei^0>sch99I + Download_on_the_App_Store_Badge_US-UK_RGB_blk_4SVG_092917 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/badges/windows-store-badge.svg b/static/images/badges/windows-store-badge.svg new file mode 100755 index 000000000..21c139edd --- /dev/null +++ b/static/images/badges/windows-store-badge.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/svgs/download/mac-badge.svg b/static/svgs/download/mac-badge.svg new file mode 100755 index 000000000..072b425a1 --- /dev/null +++ b/static/svgs/download/mac-badge.svg @@ -0,0 +1,46 @@ + + Download_on_the_App_Store_Badge_US-UK_RGB_blk_4SVG_092917 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/svgs/download/ms-badge.svg b/static/svgs/download/ms-badge.svg new file mode 100755 index 000000000..21c139edd --- /dev/null +++ b/static/svgs/download/ms-badge.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 762dc324b8e6589ad0fdd7d707e834d51ec53d69 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Sep 2019 15:15:22 -0400 Subject: [PATCH 21/38] Update extension pages for new OS picker --- .../extension-requirements.jsx | 55 +++- src/l10n.json | 3 +- src/views/boost/boost.jsx | 143 +++++----- src/views/ev3/ev3.jsx | 198 +++++++------- src/views/ev3/ev3.scss | 3 + src/views/ev3/l10n.json | 2 + src/views/gdxfor/gdxfor.jsx | 135 +++++----- src/views/microbit/l10n.json | 1 + src/views/microbit/microbit.jsx | 245 ++++++++++-------- src/views/wedo2/wedo2.jsx | 135 +++++----- static/images/ev3/chromeos-enter-passcode.png | Bin 0 -> 32951 bytes static/images/microbit/chromeos-copy-hex.png | Bin 0 -> 54352 bytes static/images/scratchlink/mac-store-badge.svg | 46 ---- .../scratchlink/windows-store-badge.svg | 82 ------ static/svgs/extensions/android.svg | 14 + static/svgs/extensions/chromeos.svg | 14 + 16 files changed, 518 insertions(+), 558 deletions(-) create mode 100644 static/images/ev3/chromeos-enter-passcode.png create mode 100644 static/images/microbit/chromeos-copy-hex.png delete mode 100755 static/images/scratchlink/mac-store-badge.svg delete mode 100755 static/images/scratchlink/windows-store-badge.svg create mode 100644 static/svgs/extensions/android.svg create mode 100644 static/svgs/extensions/chromeos.svg diff --git a/src/components/extension-landing/extension-requirements.jsx b/src/components/extension-landing/extension-requirements.jsx index 67bc2ba46..3754eb02c 100644 --- a/src/components/extension-landing/extension-requirements.jsx +++ b/src/components/extension-landing/extension-requirements.jsx @@ -4,6 +4,8 @@ const React = require('react'); const FlexRow = require('../../components/flex-row/flex-row.jsx'); +const {CHROME_APP_RELEASED} = require('../../lib/feature-flags.js'); + require('./extension-landing.scss'); const ExtensionRequirements = props => ( @@ -12,13 +14,64 @@ const ExtensionRequirements = props => ( - {props.children} + {props.bluetoothStandard ? ( + + + + Windows 10 version 1709+ + + + + macOS 10.13+ + + {CHROME_APP_RELEASED && ( + + + + ChromeOS + + + + Android 5.0+ + + + )} + + + Bluetooth + + + + Scratch Link + + + ) : props.children} ); ExtensionRequirements.propTypes = { + bluetoothStandard: PropTypes.bool, children: PropTypes.node }; +ExtensionRequirements.defaultProps = { + bluetoothStandard: false +}; + module.exports = ExtensionRequirements; diff --git a/src/l10n.json b/src/l10n.json index a72a5de67..3b5dbc393 100644 --- a/src/l10n.json +++ b/src/l10n.json @@ -140,7 +140,8 @@ "installScratch.downloadScratchDesktop": "Download Scratch Desktop", "installScratch.downloadScratchAppGeneric": "Download Scratch for {operatingsystem}", "installScratch.getScratchAppPlay": "Get Scratch on the Google Play Store", - + "installScratch.useScratchApp": "Open the Scratch app on your device.", + "installScratchLink.installHeaderTitle": "Install Scratch Link", "installScratchLink.downloadAndInstall": "Download and install Scratch Link.", "installScratchLink.startScratchLink": "Start Scratch Link and make sure it is running. It should appear in your toolbar.", diff --git a/src/views/boost/boost.jsx b/src/views/boost/boost.jsx index d4abeac54..5c3b885eb 100644 --- a/src/views/boost/boost.jsx +++ b/src/views/boost/boost.jsx @@ -9,12 +9,14 @@ const render = require('../../lib/render.jsx'); const FlexRow = require('../../components/flex-row/flex-row.jsx'); const OSChooser = require('../../components/os-chooser/os-chooser.jsx'); +const {isDownloaded, isFromGooglePlay} = require('../../components/install-scratch/install-util.js'); const ExtensionLanding = require('../../components/extension-landing/extension-landing.jsx'); const ExtensionHeader = require('../../components/extension-landing/extension-header.jsx'); const ExtensionRequirements = require('../../components/extension-landing/extension-requirements.jsx'); const ExtensionSection = require('../../components/extension-landing/extension-section.jsx'); const InstallScratchLink = require('../../components/extension-landing/install-scratch-link.jsx'); +const InstallScratch = require('../../components/install-scratch/install-scratch.jsx'); const ProjectCard = require('../../components/extension-landing/project-card.jsx'); const Steps = require('../../components/steps/steps.jsx'); @@ -56,45 +58,23 @@ class Boost extends ExtensionLanding { src="/images/boost/boost-header.svg" />} renderRequirements={ - - - - Windows 10 version 1709+ - - - - macOS 10.13+ - - - - Bluetooth 4.0 - - - - Scratch Link - - + } /> - + {(isDownloaded(this.state.OS)) && ( + + )} + {(isFromGooglePlay(this.state.OS)) && ( + + )}

@@ -109,20 +89,25 @@ class Boost extends ExtensionLanding { />

- - Scratch - - ) - }} - /> + {isDownloaded(this.state.OS) && ( + + Scratch + + ) + }} + /> + )} + {isFromGooglePlay(this.state.OS) && ( + + )}

@@ -208,36 +193,40 @@ class Boost extends ExtensionLanding {

-

-

- -

-

-

- - - - ), - macOSVersionLink: ( - - - - ) - }} - /> -

+ {isDownloaded(this.state.OS) && ( + +

+

+ +

+

+

+ + + + ), + macOSVersionLink: ( + + + + ) + }} + /> +

+
+ )}

diff --git a/src/views/ev3/ev3.jsx b/src/views/ev3/ev3.jsx index 6666490e9..c6ca1c240 100644 --- a/src/views/ev3/ev3.jsx +++ b/src/views/ev3/ev3.jsx @@ -17,6 +17,7 @@ const ExtensionVideo = require('../../components/extension-landing/extension-vid const ExtensionRequirements = require('../../components/extension-landing/extension-requirements.jsx'); const ExtensionSection = require('../../components/extension-landing/extension-section.jsx'); const InstallScratchLink = require('../../components/extension-landing/install-scratch-link.jsx'); +const InstallScratch = require('../../components/install-scratch/install-scratch.jsx'); const TipBox = require('../../components/extension-landing/tip-box.jsx'); const ProjectCard = require('../../components/extension-landing/project-card.jsx'); @@ -24,6 +25,7 @@ const Steps = require('../../components/steps/steps.jsx'); const Step = require('../../components/steps/step.jsx'); const OS_ENUM = require('../../lib/os-enum.js'); +const {isDownloaded, isFromGooglePlay} = require('../../components/install-scratch/install-util.js'); require('../../components/extension-landing/extension-landing.scss'); require('./ev3.scss'); @@ -60,42 +62,23 @@ class EV3 extends ExtensionLanding { videoId="0huu6wfiki" />} renderRequirements={ - - - - Windows 10 version 1709+ - - - - macOS 10.13+ - - - - Bluetooth - - - - Scratch Link - - + } /> - + {(isDownloaded(this.state.OS)) && ( + + )} + {(isFromGooglePlay(this.state.OS)) && ( + + )}

@@ -119,20 +102,25 @@ class EV3 extends ExtensionLanding { />

- - Scratch - - ) - }} - /> + {(isDownloaded(this.state.OS)) && ( + + Scratch + + ) + }} + /> + )} + {(isFromGooglePlay(this.state.OS)) && ( + + )}

@@ -168,27 +156,51 @@ class EV3 extends ExtensionLanding {

-
- {this.props.intl.formatMessage({id: -
-

- {this.state.OS === OS_ENUM.WINDOWS ? - : - - } -

+ {this.state.OS === OS_ENUM.WINDOWS && ( + +
+ {this.props.intl.formatMessage( +
+

+ +

+
+ )} + {this.state.OS === OS_ENUM.MACOS && ( + +
+ {this.props.intl.formatMessage( +
+

+ +

+
+ )} + {this.state.OS === OS_ENUM.CHROMEOS && ( + +
+ {this.props.intl.formatMessage( +
+

+ +

+
+ )}
@@ -269,32 +281,36 @@ class EV3 extends ExtensionLanding {

-

-

- - - - ), - macOSVersionLink: ( - - - - ) - }} - /> -

+ {isDownloaded(this.state.OS) && ( + +

+

+ + + + ), + macOSVersionLink: ( + + + + ) + }} + /> +

+
+ )}

} renderRequirements={ - - - - Windows 10 version 1709+ - - - - macOS 10.13+ - - - - Bluetooth 4.0 - - - - Scratch Link - - + } /> - + {(isDownloaded(this.state.OS)) && ( + + )} + {(isFromGooglePlay(this.state.OS)) && ( + + )}

@@ -117,20 +97,25 @@ class GdxFor extends ExtensionLanding { />

- - Scratch - - ) - }} - /> + {isDownloaded(this.state.OS) && ( + + Scratch + + ) + }} + /> + )} + {isFromGooglePlay(this.state.OS) && ( + + )}

@@ -221,32 +206,36 @@ class GdxFor extends ExtensionLanding {

-

-

- - - - ), - macOSVersionLink: ( - - - - ) - }} - /> -

+ {isDownloaded(this.state.OS) && ( + +

+

+ + + + ), + macOSVersionLink: ( + + + + ) + }} + /> +

+
+ )}

diff --git a/src/views/microbit/l10n.json b/src/views/microbit/l10n.json index fd97d6ef6..4b57e29c2 100644 --- a/src/views/microbit/l10n.json +++ b/src/views/microbit/l10n.json @@ -7,6 +7,7 @@ "microbit.downloadCardsTitle": "Download micro:bit Cards", "microbit.downloadHex": "Download the Scratch micro:bit HEX file", "microbit.dragDropHex": "Drag and drop the HEX file onto your micro:bit", + "microbit.installHexAndroid": "Please follow the instructions to install the HEX file on a computer running Windows, MacOS or ChromeOS.", "microbit.connectingMicrobit": "Connecting micro:bit to Scratch", "microbit.powerMicrobit": "Power your micro:bit with USB or a battery pack.", "microbit.useScratch3": "Use the {scratch3Link} editor.", diff --git a/src/views/microbit/microbit.jsx b/src/views/microbit/microbit.jsx index ac274423b..9e9beda27 100644 --- a/src/views/microbit/microbit.jsx +++ b/src/views/microbit/microbit.jsx @@ -10,12 +10,14 @@ const render = require('../../lib/render.jsx'); const FlexRow = require('../../components/flex-row/flex-row.jsx'); const OSChooser = require('../../components/os-chooser/os-chooser.jsx'); +const {isDownloaded, isFromGooglePlay} = require('../../components/install-scratch/install-util.js'); const ExtensionLanding = require('../../components/extension-landing/extension-landing.jsx'); const ExtensionHeader = require('../../components/extension-landing/extension-header.jsx'); const ExtensionRequirements = require('../../components/extension-landing/extension-requirements.jsx'); const ExtensionSection = require('../../components/extension-landing/extension-section.jsx'); const InstallScratchLink = require('../../components/extension-landing/install-scratch-link.jsx'); +const InstallScratch = require('../../components/install-scratch/install-scratch.jsx'); const ProjectCard = require('../../components/extension-landing/project-card.jsx'); const Button = require('../../components/forms/button.jsx'); @@ -59,90 +61,96 @@ class MicroBit extends ExtensionLanding { src="/images/microbit/microbit-heart.png" />} renderRequirements={ - - - - Windows 10 version 1709+ - - - - macOS 10.13+ - - - - Bluetooth 4.0 - - - - Scratch Link - - + } /> - + {(isDownloaded(this.state.OS)) && ( + + )} + {(isFromGooglePlay(this.state.OS)) && ( + + )}

- - -
- -
-

- -

-
- -
- -
- - - -
- -
- {this.props.intl.formatMessage({id: -
-

- -

-
-
+ {this.state.OS !== OS_ENUM.ANDROID && ( + + +
+ +
+

+ +

+
+ +
+ +
+ + + +
+ + {this.state.OS === OS_ENUM.WINDOWS && ( +
+ {this.props.intl.formatMessage({id: +
+ )} + {this.state.OS === OS_ENUM.MACOS && ( +
+ {this.props.intl.formatMessage({id: +
+ )} + {this.state.OS === OS_ENUM.CHROMEOS && ( +
+ {this.props.intl.formatMessage({id: +
+ )} +

+ +

+
+
+ )} + {this.state.OS === OS_ENUM.ANDROID && ( + + +

+ +

+
+
+ )} +

@@ -166,20 +174,25 @@ class MicroBit extends ExtensionLanding { />

- - Scratch - - ) - }} - /> + {isDownloaded(this.state.OS) && ( + + Scratch + + ) + }} + /> + )} + {isFromGooglePlay(this.state.OS) && ( + + )}

@@ -307,32 +320,36 @@ class MicroBit extends ExtensionLanding {

-

-

- - - - ), - macOSVersionLink: ( - - - - ) - }} - /> -

+ {isDownloaded(this.state.OS) && ( + +

+

+ + + + ), + macOSVersionLink: ( + + + + ) + }} + /> +

+
+ )}

diff --git a/src/views/wedo2/wedo2.jsx b/src/views/wedo2/wedo2.jsx index 21506aa64..3a4714cfe 100644 --- a/src/views/wedo2/wedo2.jsx +++ b/src/views/wedo2/wedo2.jsx @@ -9,6 +9,7 @@ const render = require('../../lib/render.jsx'); const FlexRow = require('../../components/flex-row/flex-row.jsx'); const OSChooser = require('../../components/os-chooser/os-chooser.jsx'); +const {isDownloaded, isFromGooglePlay} = require('../../components/install-scratch/install-util.js'); const ExtensionLanding = require('../../components/extension-landing/extension-landing.jsx'); const ExtensionHeader = require('../../components/extension-landing/extension-header.jsx'); @@ -16,6 +17,7 @@ const ExtensionVideo = require('../../components/extension-landing/extension-vid const ExtensionRequirements = require('../../components/extension-landing/extension-requirements.jsx'); const ExtensionSection = require('../../components/extension-landing/extension-section.jsx'); const InstallScratchLink = require('../../components/extension-landing/install-scratch-link.jsx'); +const InstallScratch = require('../../components/install-scratch/install-scratch.jsx'); const ProjectCard = require('../../components/extension-landing/project-card.jsx'); const Steps = require('../../components/steps/steps.jsx'); @@ -57,45 +59,23 @@ class Wedo2 extends ExtensionLanding { /> } renderRequirements={ - - - - Windows 10 version 1709+ - - - - macOS 10.13+ - - - - Bluetooth 4.0 - - - - Scratch Link - - + } /> - + {(isDownloaded(this.state.OS)) && ( + + )} + {(isFromGooglePlay(this.state.OS)) && ( + + )}

@@ -110,20 +90,25 @@ class Wedo2 extends ExtensionLanding { />

- - Scratch - - ) - }} - /> + {isDownloaded(this.state.OS) && ( + + Scratch + + ) + }} + /> + )} + {isFromGooglePlay(this.state.OS) && ( + + )}

@@ -207,32 +192,36 @@ class Wedo2 extends ExtensionLanding {

-

-

- - - - ), - macOSVersionLink: ( - - - - ) - }} - /> -

+ {isDownloaded(this.state.OS) && ( + +

+

+ + + + ), + macOSVersionLink: ( + + + + ) + }} + /> +

+
+ )}

diff --git a/static/images/ev3/chromeos-enter-passcode.png b/static/images/ev3/chromeos-enter-passcode.png new file mode 100644 index 0000000000000000000000000000000000000000..0177172ec47263bb65696ee3bfa12e812003b404 GIT binary patch literal 32951 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%0AGbmcDk*rs8c=zMvS59_E|-Jt9UU& zA`@ATau^+!3$dp9%WnZtMU?3+^8{*g%n1^zT%_tp4>?K>ktUY54Q2q3h97tZ;NHc6LHVfW*F zK2OP{f5@%74G6SjF$vt UY4%wb*7F-hSv8qjDYGB{8=NAd`2YX_ literal 0 HcmV?d00001 diff --git a/static/images/microbit/chromeos-copy-hex.png b/static/images/microbit/chromeos-copy-hex.png new file mode 100644 index 0000000000000000000000000000000000000000..8e6ad61159fa8508ae33ec1887c52010094798f7 GIT binary patch literal 54352 zcmbrl1z1(V_b9q)P(V_VmJaC#X^=d0r$~2qBhrX;ceiwNKuWr#OIkRDbi>>J>i%!M z_wKuo?*nGftcjJg)~uOuB?T#rXN1oH0KkxumQVoz_;LV%3qXYjYmjc+$H0GR4$|7r z0Pr05?=KvXmO%hEkz1;1xoAP;`Hk&unT<^BKbkUo*gAmF0Ps%O!@l1VS)5d)m1ec`(^IQ~rAd2~%fdCrbwxOM5%=zatub zw0Ct8qySg?PZVq&{xhtd^S{Uh5{$*e$bp58ne{J9{{S>G{tujktCP(?CO0u=F|{$Z zHMMhb24UI$1M6U6?_%$4VgG+K^naTFy9q$jLLmQ{5*qPb>v0Mm*U&hYa#mLUsR7OIO0;CYLrKJh~ z$B#TF+&p~jOnhuSMogS+ro2o>Cg2|vb|W5RZVpouUe^Egm#{Z>{VNCm?r-w{vA?2| zC8&o+Hvh-t{MF9C0>m$E=?t>g^I!U-YU=o}l8q(#KV*jA$oQ`=2vQjT6#`QeihosG z{vT7|pToLan1X};Kgh*D!JO^QT-=SEOhwH>YW?pC9}9>9%U{*~Co(MmBcA_g`!8z# zZ*b6d{4M^+Xn`O9F?*(VpsjNPjT*ZAb`JoI&B{oKs(EA_thjsN_1yt~+|8CQ+VuFT za<9IqAkn|XQ|o1Zc8hNl_7M&rUpeA1G$H~6AD@U$MIs`CoGvFQZ>j)U8g;M5ugP}j zYCtTCQ*_T$_{EFyZFT{kx`icAcQdyn&ffyKSYoJe)J7-b^?_ipn2AULM-CQdu-^ly zZ-SLvsj?2>|5p6YR+dGNKV*m^qS=fprlQ~TOE0SXMDvm^yf<3wyZp%@phP%9%-wDJ z3ewLw{Q)uH?_m2T;da9@i}?IUUy2|wFL>p%Gy5~Y>&mx-OeCDXoOYa*n~saY)&2~~ z0nY^rByo58m%l==8K!^H0pO}!$`~0mS&oxD2NGTFTMINCJ@edm&xyQDlar`16NS=P zGu_rdu^lr1{^}+gTK}TI-g)t}#rKLdHYWu{%c$T zRvNTS&{L-{=;l}V2@K5|{Gu}?-ol01KWm1z>iK>NLS**=vEKJ28n@tU?BxpR`fK`>tN!J@bMq)QJ3=IA^*#LlJJbw zr;KNBKsKAdjD3b;r~tAaEVE7~YCM@F!~OlBCA|EXV2mM%fX!u;?XLi^q3jDQ{6D{= z{z3J9RLZ}CRKouq_5Yyx6D1;gJP{4;mdUGaUMxj7xPLGbyYQ=$r@SffOQ{6%CMxrQ zokaC7mnIygP=6yUT4XpghfBoLQ+M4AGQZ^K6eF)XEwSo!Q)B`NPEQpugCI2KQ-+Wd z$4h;cK#g?)2`@XtF0j(xY}k=J?K+6)&1p2xxF)CfM2%}ScaPQ^Fr(joP1-B5e6TD2ai;XT4m#K&5F zd)OG39I2jBrG2=eP8+8-xuqVxzDj&ahbB2%GNke@j}Exg5hmY;;d0=`GAA&XEk~Kj9%;UEjrWrTGFgH^U)Pu*~1qaxj2H`w>uM-q! zRyQkrjD0BKKI9@Zu#v0;8-?RH(ekP zJs-GiHgZ}w{OlYhu4k(0axo*TxvHy07|x+hakmZS=IJIFXPLy*VG-*x!HP%=4AZ8R zbXq8}`tuOsL}iH>n1F;4f3o=cnZpkzHR{-F!92^Ohj0Il5YhsX^>2Jtv)fycfe~$N z)ZkG}NM>}MEs;A-6-k>QAy2&E2HF=mMA>IUoH61AKLsvYNoBZHsDK|VlSM409ol4`;vcxx@nt8wof)TLRD3%Wv~UYE=sERaRJU0)ful#Bqbp3=2Z`by#UKuF4JqhW?4YS_~06jng}kS_Qk?HvJ^{eI#y>+t1ZlPNWvYEtjRDb()Zk{bH?SV*e_6|N^B zE@zc@z|E3&?Z(`jW}GSYL7aT%O4H~IE5vd_`Yc@WMb$z_0}=4@0mtjQa>euwu_ph$G zM}OrxYy_Ff=H|!`%O(o@6ex5M4pn_TjPKh9CMpx}0Q91w`8_pXvA@`K#& z-1jyZH!)Jo@Qq6+C3Vi1SPf|fp~0s%Hv7yMwBK^Y?Anz@AS}GHQ9?YKSzg^fefNg#MCM;V(N z(r?QSQN(#2>e_N*|5z;=V(4WvWT#ERXX)hm3QrO-R;#2CbjWV41ZWBUz#EcS@bLj^M@oc1C;s*l}Y&CWn{& z`;&L^L1nUZt%@mQiaJ+S`hE1BmHyPAHGURbRl5J3F{v_PCJ}PBkALM$*F%=^w;EJV zcZH?#2jA5d?WQrTGF2r4iLK_?Ff+t0Cs5x*0BI+*QlUW;AF15;s6WTPq6LzCPP07C zaM8vooq6cv-NLt4m%^0@EVwJ2+evtX&?Cuw(0;uv_Tl7ZI9r>%Fp)kB zqk8M{O#65e@^{BiIC(#|hN>PlLroC^iq2BJmXG}-%Zt`1mt8%rwgn>nFxK4FZ*%?b zZ7et5(A?MZ?bIuey&>T0P{@^GA_DGH zla;n`Uv-!k4^6*H)?ME?@7rD|J@p^?Q{qlth!sT;Bx2V_Rp@JMZV-OC=Z@dvtyZl{ z_u};{_dl2XOYsvoyTc3`c44;}-=CNjWUj)6ZKDk4$2jgnyEeGi>xlTs+L`GEW%OF! z+zvKvV-jWFI@n550Ia4GhgVRByPagEy2VC;yP}`6s(uSN={5E52-xV509N_mYqW*- z=4+t2<-#{Z(rs8`+U9SF}RzP(B$$qHuRY}bGxcpTlIP;i3HWlIl z`!=x+4%2fh$3&8<5p(aI3e>j`f^nG)Yu=__@x)6Znc<31Miw-JJdsN7KNTTg*5?Y! zk+VAsd{ndcYgN&>lk9s*H&xWG@q7yA7^} zeqY8}Z=y^Ck7t9^W*VSQikD+zwx`L3QD@$_g)`-)?Hthh9C>#-&Rl01a^ojW$^}Lp z4s;4fv$Xq$mOh}1kBZZPQoH?W|2&)fy%p6k)Bd8SUD(Lun_snP+}lxU;*k@=vKDV? z)$NoA9%srX8sQs6li-1jPx`sqrKdluhwVV)SlIxJObo<*4TpykHGpDfRw(-d+TcBP zuc?&nwi)>_Q|IiztCmRTuXT&pX=F)4S-q|0qpCG+CRN@i+GofT78i}SB8zrDCrsp3zD+pd}Ses`QtR@NY*1Q3m%8BPlh!tkp0 zvJw(0gfAvZW8_%TF@e^>O$KPIuFA02B@v6Xn?Q-kpr`FhA6z~J0fpK@{{?j5O_V34 z|0zO)SG}8|fZN>*7{!y;j{RJ)EqmsrM`nmNxD6z`pIpE;V8VXhc7ZRWIRKBdN1Z$o z)WFW&>ncyDJNI(&>Xr2AjzsUSX#v97di7cB*%$bxZ+iH>OvB;3x{957Y$KxE1c*MY zRV*_cJTpBYn<_!BPGpAbT0BJaFSB8Pl(K7=B64?YZFiIU2<^Q)Y;`0%Vk3ZpewZ=hwgX<93iyzshbJAweT1ytDAMaN zj|h7}F&OsVnXM2aX%{4$-}pKR?!U`!GO|==t;FBCR^rok=CeJZ; zC1R#$njtQ4nsMwu=s2Ri5}|SS0@#y@BgZQ55dI?-8m$69;EEHVelpDO4fg@mlw{Y- zOTmGw<@!8}?=Fjo+El?^mxBBH$;J9}J%%`}q<-E_1N31i>1}QqRGgT=W{8P42OKWp zQw#@Oj2GpVwKbS{*7d(=2T;LP)_Ov#vzK* z*tc`?v2`jYk`$8o{6JJ{>%jDGlT4jl?X*d8^p4)$08$SRLRZczeXZ16x*52fZ+d^- zj~Aw-oi}Z3J@I5eEK^L0xar6J_tIkRkL^^3xvfN^r9l!+P9EcXu;_DaMBl4b^WLYq zuxcr{U;=^P9J|9tH9A@`mg)~;q|=eVq|rM?<1N7Oa0peY_Q#d<8pSb3h-id;2*)-*QR z+i-dGWU)WY%Bkqg)S=|}PmJJ`-@h&ZbE zViPp;9tY54T3>Wo=EIye*PA^zZ~Sh(;_Wp;y@cCMAHMW1^;pAo37p9qGc4(s=|A*d zqC>Y_(KMh}b<@uxhc0yXPK{Ncfz*g;=3a4ijt=3%2mZ7m%23S;ZJ}Eg4j&Q<@a)p= zJmJY-hOvw@JQ{oZtbFlkRYVLp_IqK!Oe!ar2(~YBI~>r@Nv)|F|1kAt*$`N5V9VtI zu6L3@h#8mjNCGOth8i_(O7RjZgzWF!x~r66tLY9=7yL`k*X;I;O6hZr`pc{7ZBm(? z1elHHyO9iRaHVX1*CZKtywJV$KteH-&zxpB|)W@!t4{n)ef1Gujfv&2FIkKuzI2-nc z;d*`5-S))pSo>|oW)32N)W7M2zFoTkq;Oxk>h;06ez@Z&t4o_rKX;>rdTM-`yNz>M zMxNfr0}bj}d2OxR-x#TtFo9W{Xxf%9CCsRquWi^D)GIpLz8!p#Qw9B*?Nr5Qb*iT6 z^3R4{v*m~dzxB_qgvY#0EcU#5y3O#Yl|GGd?b&MWcE>AIAhOLc-p$a=7Vt(3oC+9??S-nm)rr24lI~BT?lulzoGmY{1J#(Q}Lg=y4=V(Y|MSfdC(Vl;g z15VX5*;tDdxhKONV*FwBS^z57$CLWYRm zV3N~Q`$zkU*wg&e4CrtPKfP8pdFEa163*2iiWg3y4A8wut(CAUknY339iDz}%k0>7 z;|Dk_mG)435eKx-JW)!^}XyG&8S$&;UJvva+So z^&E%-y@OB|XI3knH|C!6ct{)0Xx$~+*xVfVaa1b(d_eO-(A;2!sAF5SEJf-4*pH1> zFHhnivp>bc%=Lp3F9+?HHe~Ehav%b5O0EiYKSB*ibJ(L@*BAf?U}Y=nu;Ww{te$jH zpi}c3@@pGo?b^fx$T_r!WMlO(jZ_WVDQujOF+W$0t_XzvxohMtl*o~oZn1OHrl6qy zynTzWANSrMyT;M{W=BtOAt?QKF3^D@7g7B=#lBxk&H9;?!aNbH_yO z6_5xJ8Z->~cqso{S{(D^5f*imTQY&v7c7K4zRX77Gqn?s&pl0Ga^iI_E{w14xD!zz z))p5j9P;FN%!gY@0q^-5$%;7ZXv|%#_HESXI$fJ|h$ld$%hxwHx68eqNNo z{2R@ub!;#jO-s*6Z*AI;QNQJCrX5DM#+{=$^fT7Ae?`UlQBf4I&{-0)HLxV7h#^y} zGmGFnJ%qPj4#Ej5e>+Gi#8pyc_D5%~I=cE9^!oj#ceSi}lxnNHg^yyY0-B6Pg(rys z()CS~!}ym~5c!N7-<%xzY8~`)BRRK7;fhEw8c4xmeXVI?RXvq$)=@@0M1fcqVjUsj zbY&6BQb!dR7hv#`J$~L&$^89dSr@4XmXA`K>s8JSUISh1LRoiIUJ5q)DpPhDd_zg ziFVl-Ixfv}tn1^S(OAT~kTp&c3Vu0|v+csP)e-{s*E@OanK&O)?))(%WP>CL>9!6+ zG-B%!>`x_rkM#33lhl9v-fVT=7%pCCe;Ds%os9?6^rT))H%wagld+m6Ub@(Hs=TqI zB}Y}Fsx2PLxOH6+)GWYgXT7;md`@J#RPnsnBFJ`7@!hiU*`=K7?%NkHQKB{l2M!a` z&OY=CxWN7Kp?avoO)fTUkayRcHs&|Y6)$3wvAv;~_K*5jEL6Du%hivJk-fKP%?Cd@ z1KGaq>=t|VX$vF1UrX>zVP~X3lb8=ye4b6J=iHC$T0FwiQD7Bk)LIpbmO7MaR-B7wHD0hDjWA`#7cWk=sx;h z>1W2hNaZAtDW;d%!1a=q!OG~<)*X^z1uC;gW9J&>74n*@NP;-e&93W3o9Ol#>$$p!@7y zEiXr+h7f>?2L|7rg4I7ab7;*fe;Etm{wbjh0JqtwLVSwP>vLu!9(kld`<40v%rHTk z;+!m5z)vX}jQQZZ>ih8X*Om?)pRpxs*QD#`rooBR91*+AjiOX3fYI@DO{8(#^)ZD+ zw6EZ_g3h`cPiOaxvr-1v6?A=!%l@vne53lF)VEFoGl0F1uNiOPE z!^Y2^?#*3B8=LVf?i;S?CDg&8)y?6N`bBaI9GQp;yLfleMaV;CC{?hKoCYEVvOBq8 zS&UP_uXMxjFQADma=kRFj2OV2i{Gy*XF8XrB0~Xg_mR<5cfGV{K>8^Ti?6|QYnJgu zAN|tvpNi1(uyULEM9YV9IlrS#G08lPT0CW{QlVT=67RR&t&I-)4oL%LKkBKsKX1Q( z+l`KEf(?97HMrln%;HZSB3m-1TwTtb&?-~xbp+HfQln_S5l3^@p0HD&j3@YC(|GrC zR(p)5*k-@eIL>%Erbt@uJ^XZt+9#i!NqSrzJ}i2bu%{{9a@Zra-|nG#>8n}HVn~FI zG}TbZ*u!GGQn`BVQdM?Bb>#?^>8?U88#Bz*S81V8;x+5!%`b71%qbJzN7i(;)BH00 zIk@NyuX?^Em7}P3JW0c5Q1Pd1vN77_c1(Bf!<35ehN->}=I36Vk@O<@T&00pT4sT&d?U!^fvc{4wFIgFIP0A!dAMk2b>G6 zo=2oJrp2VibZF$LR9PR0L9X!TESaRmQYD5&NiUBm#G{E&bq8T{3^MWJrA>I~nHIjG zWCYK*Kj#IIMy0&=lgW{Y4R^)D0@}prKWhb@Ft`sog3+ZGnYq(f#JnF8b1ha5vX@_O zrEukA!>Hnd`vb%syLjbOUk8YYaCkU=j!k{*nJMNuPgljtexFB<)h6#6|0-d>fx{%e zdyHac7sr-87Lg)?DwiE}sE#X>)qg&)Vn#?)g_jWW$SctQM%JYInDT+$uJVunH79p! zl19}t^sjAFi4t)e*qelPQ(EN=B+al5wD&X zGR$of%AI9q^o?Wlo1!J)ssGM6iyW9rN znQE3&xn56PufbC^AfQ-?*=K!P3^wvq{gA#3j|E_2&7~MQh=qy2nZ=|)RLd+Uq(nRz zTIJdUjeg3q>8pxIBQf$^M~Hc?G!onfgQ|&Da%qDfvGKA|ew?&C?gm4hj%d0(J>1Gx z4Prd?z7pN$J3@1sO}kK~iv|4P`wPb)5zb8Y(Y4mQ+06pSL~DNMMMO~`fRdr^f*hFM z$`b)6OFzm(>nE7U|0pQ9Sc)3-tqHxgJCnQH>%D?$2d2*#TTb> zl&trcu?ycIa(>c<3%xKQhoLr1@!AHNeG@${)T}M>3OzXdP3p^7krG)u(zIAwLK6GU_(atlUKjXya*8npn zj4s8|osNcMx68ee*{}F)zdL67Xj(qeB4E=!C!`jo?4xWYu5YdUWyxhV+tPX}Z=I>{ z%bZJL1D<^+4_3uJWTR7s_vTLEy15#sWjOx!&jR<($HojCtj8sBzvFWLDy z9Ti?Hd*8a_gF$O~yk}n0@cZBAR7yAzsjc1ga4~20$(#e}GY6&ob1uk0oU|?b8x&}I zGe7%l#QdF=2bIdiaKjAc^Pviy>NCX87ATVRlNd>cc?cpJWhP9skB02Mr~rvv#wU^P zq6yslA8&&d3^k5RMb@FhMe;MMA1=l0B2W(Uh7Ic%g7KP%#QaE(6Q$xz<0gA2~hoK z{_|6v9qu=S3{QpC1Y|G|ikkWEe@LkRGf%8WV)U;Z?EhYKEWwv-M@EwT{cr@VoCK$S zUO*@jD-$3Nz=F@nejmELr?o@rZZ>z6f;;VdZ2`FKe8|A2z`8%m$Nh|e5-<(>DnK&q zvo&_GNTN%HrI2yJPVR!PB+CUE9v>e6@#CvWc&2B}iC8hItR*K-B9%-WwO`sh{vDUM zXR!ix*6t(W2CpvRaySr>P$E&tXK0mV5Wipp{Ew)vsA_RYqAwNGLkBXymaC2r8;6c^1nX4+-%Fek11KQJ^uW%5yL5IrVw+C*AzWTxby3`D5_^BU}7G z<(mKG0;q7i8A_9Onq5CP(J)ZgGoI(!s8T_qVaSw-*&HEXSjI#XsI-$%9rUMNR(=7& z5twb?UAU@V58RP&5u5bUgxBwXYz@Ymo={T{oxW%M;5%T;{TTX0Z33__pQg#^%^Vp!z3;mR+yc{ISgqk!4x9V_=F-c#jN#Eq4xynfqb$e3j zvsFrhk|-Xkc?js-<`vB2?Rof}hl6Vp?@qBy39*!QYg*e>3Xr5^6MHfB$}gxIj!Kfv zSh7yo@W6JyN)F~#bNc=3v%x``1n0IDjc=A08{5!+QOlosD+^3S5qdl^lidN}t%VQH zyc^zr{+E+%-D$gm9A46KnLr}9@6(Q|MbUmNzEIt&IKGy9y4s&*$7s$?ulFo|ZV%lKQA_4{8BH?Z z;|QW9A;XUQS=BI0%l+3p=^^2G_Pg7}ZPttUIhgnF+3Zdqr~BbId<&TCYi|8I^>+J| z@AU*A6u?-^+hM5`t?7Ez0!3tXMnoVbH~rRL&dh(YYV$C;0SQozPEJgucEJTC`k0@4 z$H?1ROa{{|>T~B>k?KvSzCH9E-tXKLIZ%)?W#A&^AQ+$|K9s6ixHzpGk0QJ&);6E0 z6mQ2$J-HlqUv9W);Gy9hwz~LpU3|}n!8xi#`~_-1KoOflKIFF4iTjo#k1g@OZD)uR z9b;_Y)6*$3ST}0?_JdPw+r{W7mH=rV4fU*fJv4I8j^YtzYk z>btLg_Ps)K_iW5oJ(+5wb?^j`QV+*-v%THEYd~`EJm06zw1yWslV3s@_iMIxFOjT8 zkY^6#9?L%~8lBfDS}Q7rdXK7Kx}cd5ns$kKnn z$AiOfIaQ@9!!NJnDAuhgZV( zw*z&e?KT7VNRgMp+L4%oZ4H%*IF2++_#E|Hkt|K>sG~%MbeP36Ya+a~d86GMYvYd= zo>d0ZWLf;ie%S^q&oo9CnSSmsw0!=szfaK(MCVZrA7Lk|UrgCOJdtBA*2*7x80FN(rw?rQR-{Jl9aSj7Vgn&+Vdf%e&3J zCGTW=ore+Caf8R7uXbrqSN$)v`O0lO@GN23uwB2qv+EPZ#;R?;z8FsiB|&T?p#M$f zOpBlnSWr@8EGoWs!X{^Dj-z^%5t&-lHcu+2zwNP-3;j$Mp^iUIMQyz;N-#hrgH zyWJY7WWv8Qg$fcy9WS%;H9KEy#zc=B4|4gstiE!d!yxsXOUw-XT-b-jyMQhu~X#b|GI}`&!bnrPw zE=YKvKe;`eY;I!mvrL^4hGjKVmKYmb<8yOr!Ra+YCmL5EenCd=Qc)7Cr1EL3CuFtV zZ*^&Hr$2rw4sCb&XY-)IWsBoM3N4w}pLN|^*9YIhinV9WJcH)$w~N{SGxO$#KCTCX zxzUV4m+x8H&-a&b$<7k&A0Ly2;eMSp8ER*jocu_sG42AN*ZODK(G9QuO9;q2F(kTw>A7#zV5uG+elI5hIL+dp-x2L9XO>paiHuRkLk zwU%lQclFz^U0WC=mxl=5Y^Bgjp)=n)9-ZF^tBCT46uXW!LzU-|$JPF{40XBCqZ2(alE%JQ&%2X47$Ea#V#rI3N%U*PhW&h< zHjCimcRuBDHSNLu)-;kfQ>)Os0{fhI9-F?3dOveT*B7hT-kn#y#9J~NLl(Zjr3HNW zZ?-SIGGDFw+$Cz#zBpX6T1>}%D*4rY5h;9sT5KI6$Z^x4N!s}!+lM;9g#hTuSaQX! zqcxQ`%nR2G zGR#mU(Jpd;L=#cV(z02jT4>JI@983W%}rl@4VLiJBg4x&Q{8?*AU-=XNd6*Bm?^q6 zBcK#*Os4^c6Hu^a(j;7GIlKdWkdqxe_XOxfc&mA1KgK{GM(EVd-!Ds_H4lrQw34E9 zL7OGS&EMMX)67p1xp^FoHqE^?tU0}-c3vfJGCQ0#&uU(R)gx!20ZOs(1o2rOCJ6TT zM;$zjfN=F1-J*|Yd8dcT4JU<4G0Q?!C=h_dt8sId3CG)Hv+7l%j{#(m9NBaQ0twYCT0ggI0~_=KU|0X=U1kf^ zW~CKj=ozBnrk(y0yvX~NR!h=0I|B6xRG_RMkk?1~D(^RP1i!r7ECqU}vU^5p{?B(a zRJ?wn12S=WlfNsQRu@ZEi;a3h*!mr!01}qqlyEFy+2`t8;?>idK(-B>RGcsuSN+YB zKuGi+x5arFBmd45Cu}|0j1|BX38da&1Hx$4fGU~|AcqDA8ATZn(BjpK{kd`9X17;~ zG9+mpH~a<{4#)9=N)tCzrzB50U6bk2)Vuw-!FMsk*)QFB?0Dv+C!I9){vr)lWxoR- z(2+k63=8>OZYBoS*h6)Ok$?{tCrV=beto019NS7k)OQ_0BF5o}d`bF8Bflp8RE&*s zy!1U?-HL!;^c6t?CPq36YaPl1mZO3h;s`HzO=CRA_zK|xRQN!xlUCs~XD_`yoa^)# zYeE9T4=u%{wqKdsZ~xe)Zz2N(A>Dzfafm<&p};{EO{f47YS6``m?=Wj)v+a4Oei5~ zQ%VMcgB`q@0h}|S>=*`|SV(+pGkI2H@pH#TW9V#C=zF%Q89j7V*=5aDnW04k}p9) zK_&Du6f01oF9>R{lcrrUx|VVjhRDh_tS`^KqSLOWEtb9rVun`kd*Z$UzEaU^d^;+Y zB29S*o@!@hmLqZcGy?@emL-qj;k=iAmqud$%Hk&oT*SRmdWQw z5@-lMbu!1T5CIXSg{b4j23!E=2Kx5S(^|s~qm8mf z!ELJ4Zv^)l@Rjb#VXD^G2yfaA~umf)_S8C2W}V z9GI+KNpIS5T6oDgw$y5yOg=2x*0_7bfJb#wLUQduGsMyKd}~e@44`2 zDgauwCTIW^HTA=vo);K|oWEzv=lY|GppUn(R#({N(aKVq#}B@20r#UMCS5{q8;`xh zWR1oRL%*Btu}pzBw?iEzrC%o={B8%*^wAO&9Oh%_i1315=N6NBNC0@a;rDQB-+9x& z=VIWtV7j-r*Xn0=VVGN|^jn^? z%p|C-+(QP)pZm)?cY@#lCuuYv~!V+1|z1bG777G^XRJnV;l zWv2!bsNFzagY{*T*x zx5^AW=u)CyKBRnyo|Fh-A5ytc<~ocHjd$FTm$hGLHPuL)TOZ$_%$KYEq|)bnRQO=u zxi7XLUqU0i!1F%WI0P%tGL0NHi2UApU;IN+H??Nu<;u8S9Obh-7A9Cw=L5>h#L{4N z%20=;cBO8U>=`zV2fxcZff-|WFW<+4f`irznE9N=MC-6Nq_%lb1yKMt5;WX9FiJlWd#izAk{#?@?db+d2Cj(&7Upvp%y1qJZt5N?lmpHvM zCV;{-eA;i!=594MD7f=NRbZ@?g{fTO(58BBDMxZZ_lS704q_{aJqM{VN_T@;-Ew}* z_I89uwl>Hq$c&d}ZYoqQ)GOg*je7C>TDqm$eQvACf~?VCCtiG>CFaJi(<{OYe6gk@@Dc zEj#pJfToOq+h)GmadWmp`wSeSW#veU%-7{)qmRt@TzRF{HQVoYk6JFt=cMn|q{VsL zai_rDL7nw%1)uNjIVh{1kM}o$BER;_t4aBswq`U9Gt<+(&qjG(5U_su^Q$}meY6f} zMFo!AH=SBY{qMn>b#vpH0^p^SBRWRLbT(sT;C}7t!Em|=a(BLGOwd2sNcS5tKfx-k z(R!|GWJF#&DTcvfGb*-2E>naG=S{E!kUWhBd?<@;UU%Vpa(iZMrIwuW3~7QATm5UM zTdEdgJ=WyTa#J%8mX50{p152|YxuB!hD?Q@gf+#PKcA$-SSj67`P)wGeJj10(gTMd zcwJ!V*aZoj+r z>R2t0?>8UfSF#-6XF(P^KZWG4wVW*L%(|iIWB=|YI}0t-pE>p@lz3;w4*XK9Ur2;r zgwf_8Ie& zXHG@{KE|R_#toNhweaaNKl>Uci60sdXfd*NO$u}Ul8A!G5+gA7zC38TTJ>gW{uOk0 z)Mh`L##P;MnKlQz@akw<^SkQ}$9?d7iY2BlD>E}QYhAGeq!ksbRf`os?!u7^2rO|z zAk%U2@#&nFryG$hPGhe6<0B(m8BaI6*+SXXn$-q?Qn5nj)|LsZ&h2>h(5)@oV*{<+ z`TM#<3TV+rd16Qe+$M5FGX>m9!MJ*QuXpDuN{S9iwL#;tp=cW`U??$wp85nq1BvE? zM$zv3cV-7mNtQJ!8mTe2R~R)nQYezB>qVrI54K&gD$m}3T+2m5VTM5Hw{4)6kFYoI zQK`E=-eKA)$59LN`rqmbCwh?)IV%=L(|$}FeWNTR1A)YPz%xMUgK+?-GSV}3=%)G_ z%`E)8oz?2YHJ)GU3h?Iv!V{}$%CZZcQ7`QeOvEdxXGHT>NU<|l6t6EzK5yswTxD_vMn zaBr-UFf~L9uRjNwR#R73##2|9R6h532b-;IyV*%UZa*7?RkhiHCVz7vCe+uz1VQ&C zA|e8GH!w-~Iq{yg=rOmgRKdJJ3WMs`!RNg5R=3fnKk9Xi(5*!>vpy@T2&%ex2q90m z=kSkL_U<$-haV|kas~E&WYg>zi#M>Qm3=0}zFF_Ke2nxfBUCIG8G{g=^xZt?r~|KF zwLym*a|;z=bck545}gS_KZIxHXNhwD2iEH~-G$|4J;?O!`QD}rzx%1^z-YalA(Mbbq!L(zT;x{YRw;-m-i9e z(;S9^Yrr>1AK8$5VTz87RHTtx>O7)N%VWT z0IZME&((??d5Oa%#PIq)jmfWP>~3!-CMP3^$jHcuiUwZzX=`hPb6)Ucp&l%^s7}{2 z>$lchOl~CU+LanY{X|7YSqYZgJ)P&O^ij|WSdG31e}4XI5zKLfx&B;r9v1}F;uv33 zPZ{d>Fq+PDwKrW1at-tY355ObLEW%UGt<%0S?lzL1I*zmg3GAIE>~7C@3MGAK#9bT zd>x=ERot>k1jpK+pjFs*M4;tN%H7a$qyCuMmDk>^2`b|Z+V_AYrhi=)fv)aN>_jPkHqySK3|LP*> zQA88*kg|OYE-M3+K(pz-=Ieg6+#)F{Ij83gxjI^TZQQitrh9jJn9QV0BQy19y~o+f zDQZYE?e9}Ej=qzQS6yD* z+#61zV7g{R>S&~Dh0WcE5&%kUvSNaZEC<>Z2V1#(zzxKCXC%DuV?-YMYxV-UWac8v zkCcuy-KapON3pTD^M7(9U%U|=rqnMfSo>w=e1l|L-)CGXaCrnIL(QHC<`Ep2ccR^ABLOzpo&dN%yV{HLJ8cC}P7ti{a;rNXnd$ zwyiAGLI!rT{VW#Tg-w1sJXho8BI6n3laA;q`axUcQ7rZvGs0P+#G*+6F+wp?*phcw z?W+xUXdWFrb%ry!TFt^$=X=^0;A>073^;tm;$ho5zRS)YzTB-ZZDjHaGDN~wDSWq| zNq5}^xV+)%K6^9=VT&eFUu*8FPMav$$LVk3Z{ue?d&=`dVM5rALTxOd@}|HGFKHEd z;n!irHN~7Qx6z*09)xQICGojN%wS`nU!@A|L0%5{Ao&(YbFXupBEd-CUBWOz*Gx&rU`&)e*jd4(ZF(CA33+=PKdn2{06K>;vfPG^FA`H15z!Usy~&%D zz>(wR6rqYSAf~L2MQ2VEn=$A|{SrNtqIEib>jH;05CMP-6f1cb_p1>PO2F`Zfa?<$ zAS$Eamf~1RK~M+}9;K#??!|}esHG_4H7ywxBl2=kDhJHP#4*8}Wis^ZQE0U*hc$yZtO$AQ7VmOn5!kEK zqKL521~$r`iSP!Dt~<7sh=@j~uPe=OiBx zn+qE#z7Fkb=?)iOfyEU_Q(-0?Jv9_NrN#JhJ6;F5)?~eM3uyK_kCC9l47I(pUwh~* z{PteKHkL@rS4>FawS-Kd*7}uP^6SfblbGrFjaBH&433;m6Z<&1)po1FR@=T(E@!Hb z_cK()qVU~2=lsEmXF@6L+dW*m5zd4SvaXRQ!DdKS+{v`Hwv%<^^759hexEp#%ZQ{p zydQ}asIql$jTbVqd6Kt(mQjK)AA`n^m51H@ICekb!%m&{8>AJLAMY)@M-+t(+m5c* zG@h=gc(PRiHhh1v`psQ*Ox7CtYQAUq6kyW$Iv`6TPI{nPL%IRU3lMjw1K` z0^)j*V%x`y-`(|TKG&?i?{+Y2v{2P#S^RSetB%k%=P6qkVDrZVC#L-7vU>1wTDM6f zBuPoKubbj#&G)WyuOc;Ti*+RbZr`CFaUn5ogH@~$T47Kq+ld#m!U|bfZ852*L)UJK~_S*Xq^8yqmStu$+@bL;uU|_M3F~MAM&Ezujd{5S{PZPd0?1_cr?* z$NsZhKl%B%T8apXmxZzi-W=<-Im!8@3U};4Z?Z^79LYf6LheqjQ|Na(ct{&NcEXG! z_7n!1HF|o6dF_Pe{oC|0*v!>K6Ad!MkV&7L#F8_nh)LhNLo1$k?RV@kzTBYEBNyI` zJQ2R#s80p+_}WP(ZIIYNXE1GTi&;EeM2E=xD&d@J;53zNRQIM>c$&q*PlU$18v1EH zr35Q*DkyW2}MBZln!YD0VzRHK~kh!y1N@u zQc=1Qq`SLQTBJl;M39u0{^s&`zHf|goOhgY&O6TjV{c`zwRqP3-1nTAFj|%ZkE{9M)igVJ;AbgkgJ}^r#g)><`1N|tD_QkERE(Qt*E!AH- z@3-*TG`RPdhorTdll)3wS4Zcsl|4&V^FHYc(Wb65`;)Tc`$05JVO{71LC;yypHeCuI5os}f*22>Nf@b!?JPV4XzpPl~-b@tPLh<*-KP=2SDZ~<0 z*pIuXDsxH(I9!Bm5H$VQBR7+dPtLT%Z?lRq?nFxK4{PV$z*U|l>A1x@ZKluT_0{Y~ z{7keCYu{07p;^ntd~njH%V$)~;n0{LMDZgYMlJz1fBx(W=(+h^q&VjgjD4Zc@Hgd_ z4RFGjnl^R&Tp@L-%Qg97LgD8JBK@Y_p5*8opT;+yqbo0cIQK|boYNw7`_8aPi4{xK zIAlw5t^L6;V>i9lTf{B zv9?y9{LHL;N#!>00=cl?$%FWXEv2s61>#m7!=L*jB1dxtH=|T;MJkVcf!+9?!D4kd zbs(M#0Q!K<{h8o9z8Zj_orZ3qWI(NfPyPwXm>|m6Zh~OeoDGzn{J@ zz2;sqO_fy8@?cj6muBF$+3fPOul%p5Wz=cSQtrz5KB;q8^wnhHC~Oh$Q&xLU`#@AL zSEa`x4-@BD_V9LXg9!u?nz13v1LQuqmvYq zMmL5$VK(6*i3}PpfsbLDLere0YFgA=-*y6Og8I^F&nbqX}i8WCG_bz|S)$S@$t+?GxPQTXE7a!XMF_m4* z{=}W|ipQ?Iu6<=yyNP+|;EVZWYedC?;?FB%lPM#VGZa+_a)N+61qIV{~UI_+zEE-sn@8K2$wJPOB3A~$auwds}m9Ua$=a9G?vOXS4S^_Bx3&{ zNr*a_`g+i#m6fmoj?i)LhX`ImETulqaMabV?W&7%?>?>#*V;|@l9PeQk^wxnPArXT z?0V4@jV9yIM9Dw2rKRFUx*JR@uHx2}sY>41V!Pqs8GBP-LHl4M?My#m@{?H1>apiR z#;tI*^UFVPw@4H&v%2OjHwo}oK69T9mGl0R-xzn$iBdc76xi?HjI+`1MMDJLk;hv# zq*}mw7{$=Zt>?bx-I8jo`Xm<3BpFQsmO8{}7n?VrN!3SoBLy z4);l-S?hupZGrGg!$psnhxYc5L_0rjQR}iG!wt8ywPz!;N#}bv8s(X%j}B*cOlR+} zy&>e;>$6h4i0y0coH4zN8`)|+fXDUr!sw#3(An+POtAQLNeMG)7@Ggk3a;(>>571u zY=O3TnbY#Sz(tR#`H42o`(DFMKlZ|kYT|D;Zj^YEd4KOJ7bjj35%hc-b}Q9&O;Xn7 z^c{uYlY?U-F1Zv9eE#+z*ZA4p)(uW2e_@}#Ya7I(;+}_bhgR}#7s8JtU?N51>WE9U z{>9JPLJUk_@7fCDSC~h&iMNIf-VpQ1eDx1rfWBg}l|GjnHwezQ=KSPn2Fh&CFrr=@ zZ<J^8*}2sV`zs+xE{&2(zi(P1xv=`Xw4G=1ku#z(e|<>?)gQ0&!P zO*y^z8kyeG7`>|FYjL43fFb1HDeb58heO`>c`uRsWj%KOTPR?B9hUD5HCK9zf*F;v zu6h1@rn~A_TaC~M`?VnctJv4~$pl4NS;b@-N`C~^t&?DxVNxCi`IktFP@6rl@tybI zX7fuCw~LdBAE{UH_}E#KIM^ zNU0+xWY<}m?z3lTC^#Ha*h^IFW~D7ai9;3H7R<|mI=}CwUtu(HP^z6aQTohOFkQPk zC+cU;=`pYV$x-fM;8PR*IW`O&3-uA6l|-{Ws;wPL@7U9S`k#wG8LE^En-Gb&OevaX zcnyWmY~t%}1QZ=|vU3?6@at`VZedT_IF!!?>6(1`DDBm|UhmQPs%8A=Gt5W}jVYdu z*kZOYRoq_d9m->6e+_muEv@gMg(mWvp|&kYitAOH4j~l|P~VY>29gXG0(kQX2tdE> z1&^%Cith{z3@q=~O{{CL>W}da^!MX*V!f6G(yq!iAf+tFaC@iq>Jr}jvKtfY;>P3$ z)5_^B*WkIO7?YLHjqhx1)w_EODTIXl2>U(KRBJHjAFQ7y zxa)Dc%8VE3d6)K?)LiBE2-OxRg=_E76hqCq$Wr~)zDv`}P=5OCx9VRrBVrXfgI*)A z6l)5W2G;E_QfqY+e=Y9kMqFq!MZOIWjhZq2X}h3VI7c+n`khmpEzXn>;MD{MY{D4x>DevEqO>(CA<5$`=DmQ&} z&bu7WzmESlkSZBXWGl`vQnR|w=TGc?)Jtc#A$B{*UW6N^Er5piK$=X{QbtlMfuEz>-pKM z<)`)~b)vhvtv)ab<=9tXs%pa_73Es!M!S;p)GvNEwM!OR?@{IvpL=HIKOMsf5p^`e zLt#xTt>@EK``xU!&7TO}yI8m-arCXPpmf3GckEPd({6t!lfgS~>4$;zl`p|AQ0auDj^L)HORf?Yh$@iaXSoi=Q;T2WCoJ>SoHSZKppP zEcef${OQ}QohZzXc_WlxkwXFYr&?j%S^tU(M~rdHV!6&LkFoX1p}$VuO`(br9((z# z8!oNcit%p(46*}XExtQKJF2k3pVY-p^{p8-|{Jgvb(Bzst_8lD^iP<%{K_L42^|>-3 zvZw&3;_Gi`Es||j>_8fsuU4ha_w5(klvu!|%MupF&LsFF-xq2vPx#-sZ zK!X}O)9p|wb6HMTm^>Lb4FAH@`|$my!t3qt2Lmu#dEh=ZEBq}_ji?xiIX9Bh3E*Cy^-l5<*UvbN^8keXa7Nl&deTTm&H#Yu=>07Y2 z1&V*!#dGpdtUAXMa`z|67#3#7O%*b6Y%%0~)1U2ecYG+wrrb zUSdi-dl9qgFuu*bSaP=0Y}b`4aByie+H1Anft%bR zf%d8^GGx`!DS_8ZHjCtPn2)Xvq@cKXL=k=nyI(uPp@LemHzn_XKiFxY^x6K8ZM|mC z^19KwPU$|$=>q+!ySQf#1ww|N9^W=6{LD>%97l?uN$+N32dVV+NUkh!oYikJx*csl zpGniHyed8x?%0TD=dzwVyw^(jy{tIo^?of0g<0y@km+{ny{A|_H|MxNuBV6Mci@PA z_<{&w8Mxm_f15qLXWF-&E&D}e@Bka?Hle=r%!r+Qs*Bo=ww7_|halXt<6l^)Ub%SC zFe5ir<#$eeeuH`KZX*7VUTGyi^l#>E;I1uiq^-(Nv+en9zdb93{e z<{r+YsI19iZN=0FhoCMCL#|*2ZIe@@jE0SEV8!upYo^xc>?n{XAxD!8{jYb+<|dRJ zZy-{fG~?KK`Q*p=YgFkl)}ZmqkUp#@eitA?Q{tc;$3h!)J+O^6`(emKclO&jZ_an^ zHOhoo%-UJ-IgB0e?pWQpCK2+up-MVBKNU-tqcf`5f8vYqTTYPp&Jl*8 z5}5>Sd=N~K_&6NHPRMmi`!SCZj%XAkb8}S$-ir$jaf)l0Bs2w;-fU$Q3Nqs@ca_}M{)y8C1t&mfS5SRIQjG@-QrO?mE_A^ z=Hr{q^TewBg^B1TRt0^6ye<>|t;bsg5`3Q=R3~5Ui@QHoUk*eVMvZsTTlVghJ_@1c zz*HZ8E1)M}xK1H%d^NJ_qAY3jVUOiovWc;!1TQI1bJ)U;Q!LT8OP%MM34MgQ;kPr|r<-RE*NaIu$uHI(Ri10s9&F{s2N$S?^Zt^PrCZTv z7CU>Pwc0*)Y)e$~h0s#|&FaJAk8w`82k!ITo`K*I7cydJWX#@6Eh=J1aLe-w36UXQ zmVe7&LO=(MZA(f!>;Y)LVU2G{?j^O`9*I5-rl73*-u zPoU5YI5ON$6ml}9!CxEw^dy{^18n;&P*Z|x6*_7Fkct}HxuoZJ!!k=Y05bIt3|K)y zcO*@eQRb@))hmQ^6=7qQ$)RFH;c%s$pGHIO=EiZ)yL!L1p!jroxA&rqM6P&QUt`SY z;}j=JoW)zC*|8&dhTB)%RyY&ynRYz6^4gp8opVH`@FV4MlPw+W z$wVOO&r6IKd$KOjdexWfj3#yKK37_XCB|wB{^F$ihs1|R3hxR;I^@Tk8)mPo<3+bx zJhasEG&sq5kK%Gc9end>^V`e2`P9tZkNAm3O)%*sqzxUUxtI1m*km1qavhk+(@;0!GU?ABPU{(}%Jg0hXs} zUmc%>^9f`A*gG{2DQFYYaAE2y;6d0cw56}hqjxH+$Q%y9jDoejdIlB_4sndyMHS_x zavN=@YnrDX+ic?IT6E+Q(njem;)gY7+x`6q=k-2Dvw|b5!EH4La{Q^d!UBn%pMI7k zRf@7KIByF*X3#tIj7%K-rQrCvDN|(e52H*DdBnPmuR0xNXns#XZOElp5Efx(#@DY8 zJvC|ApEQM5I7UY|6YOKHy2nksU`Q&7ZFodtpx%qPRLfx-=dM$8jd>KD6Rn*PzGlVEkoB#~a+6 z*yuTbD!Jx!VzPAosZ3v=>~2hKY(h+3litg`0HuiQF-}a4QIwBQtO$BThf42=9o)v^ zAqe?t@3E<*q{aEIJ@=tz9=oOg)bD%K5NXGHrEJ<70l`(!`15{>5ekLTR{j;77jSpJL#~vzC(Q^C`qoTMT92YU(d7HlPbKCdG z@E$F?oUW=%r&2~8&GYwn*z*xR9(1<^gz!<(HRz}(Za&6RLe-*+`{H~ZER;_LY1~&j zN$!#J%pBBZGFhhrKqq;FDn5L%w#DW9+i{w|k4`DH0-$%r`00-^f&gaZ-bvRuic2EaIuRk4l>e; zmBddT(%xxIJa|M_wn>od(J$;RtU2TLObVitRLr*%t&3 z(GJ(!63HUbWy9IR!jEH!cUWgDg)Omgsn36Ik;pWMb8TwSXZx-`YvfPlUpX1delycj zYXG#HiEoaE(4QARiwxhul8zYr2}SB0?`uM&wI=6>F;TLad~!vzoEz1EER}|^f*iH% zmuDqZDKCkO7LrC1Y27d8hAkRT2h>S87xL9MPkyGxf7)iE(w-`1;de@Ma|jmjIkvJ5 zKU&4Y9-1vZDXj>H38+z2d^LYQk(hFvD^CPIYf0Ffv8~o^bj4NCK3=YEwf>eNxP|`k zJyn$6-eq{C_2@d@+5%Tuq5YHjl84eBlJ;p68FMA+rtGJ_>fC<4gPKm>F2dsql@<-S zQ09E3n3rVhbK~C;tF_OucrM%MvT+}*L2!&}EGLi%9n5Wi&Mpw|Q&Lh=Q#rzZAdeO} z2O_COPxl767Rn4e7u#Qv86mhWzGf-091IKZ)ajJ;-tqJPnk7S`W8eO>FPb2<%Pido zc(b18?`#r1_YAk4c_@}Uf&BZx#S#n>lO?)m@R`A#OIcYAP5k8#Xb2N5r0AEV=rHWt zUP=Z@eYzzR_xnTl#AujCGbh@eI=0j4-cNp~a@_elledf+vu;|{y?eZ0SRf3fB~YUI-Hi-_OW>7gZGQ9KbR*Ncqy?kOCd<4s>*{FyOJ~$8iN8xWgyvCs(x`N9olbc3^+j|}%1f-ZB zIpZr#CP;XvzRw(sEVR3*h#LpGiViPt57OejP3GHueP*ZMpWr_C#bHi6E5EI#(<4j{cEbcSNDHBqf%hB>vUVV%y<4hc``!e1|dy9F<@#Xy9^>1{I zzZAf+p1*igC`-TK%eO7upPINj>psVyNgSDMFlUqZO_bBNw&hyy&pevR*M|;tGS3Am zTfhSYDizhcPWhI%-7kGx{eEeXq5k`91%;tZ}UCbu0_agTPJ=Xd*jq3UKh#LH$Y(ayE;oZBo#|n9IJ8{QDCJK zu$>h>o<(|J+FxO!wSkEmd^vyMGvU}?j6S)e+v@8BAII6SmI=ig%uC;N+6UdrvNs6XS}hKXG%xLJz7Lg$vP8DScSlQNT!b_4t)lG+>=+$ zXL**1XmjCj4>4C^(3MX}k&W{Hdmg~s6VkiGXz9EXSV*BP02aEW($14JUcFjQ9wBIL-)^$9Gu+oVeH=zT)7Z4lQ#g599W5oXnBqY9-Ix|o=SQp{rRiq;1~Wmnjrh(JaeZ9x|v0FTe6y?!|!G9SsD*~d`-t= zrId`%?fll^uBMgUo$yaT>W;zhSKQ|otoRrUii95*M0*l4S>_tu^y?i>M5q-N6t>6n z#{!Z&TZHQhT2z#kM+;O7Gad!Mk)(|uSUEm3D%ap6FP4*GWefse9_U+O-0lu1;nHjR z+sB-~2{v&0NRgM7(?v3YYlFnNPbwVO=db=$jy^Tt84D;}Y-7owF4Z5q{l4Xf6sH`lx}W2pUH8QI886R6znD#y9u*Y z?upktAu*DxXxxk`EU--N>1=7{kPR+5CO4d_M_!b(QLuqFgm{qtr*&HR!=iyrApAuW|5EGq;5r(9NPYo1zN6f`Fwj^v1lBNLwRb+5%usq7xAN*;BgGrq zfxT-aI)6UXM_PX?Ljone(s=d6c60<-f^T5Ff(^MnQ@itPD4Ff<-J)ZP`_^^PC3%bf zaaK+aF)^|2Y+bp@KwPm_brSTs_@kg%-=87T_ckQ$K{6-Dm42in6@VFVp7XU&k!kKf zLLn*CgO1nhYWGovAe-S1$hZ}*IH_^ zP`*qgv~U?P2HBf3y!5i8QX}9gv~JU+qn>WO*|x;XhU0a6~%;8eOA2aHd(_p z>_=B@0*JqC!L6$4I~;h^jb1SKs}AL%oTwpnk@Xc7ZP&Oefq}lU45F@t@xZ6`O+Q8( zAFftVvk&#;)3Ug-iZA2nFHSyDMn`GUulSif3*rBK6A^P;&4ueq`dNbNI*Rws2c;GB z(c;YKFNe?&Wxr6)4<&Lk65pt4P8+Fz>YT9Mnc6BfLi6XfSuoT&{8s6D*&g}!Zlk$= zA2vduN@}J06Lq9u#c)f>WqnMc!%&qPlNrtl zz*2Tc^3mK@* zoy=_%kMJd*yycaM@h3}i=%3SZ^_D0#oqETC zI1XrWsm^4H;F0~^AIrACw+EM!%W@qtn8=&^@#9DBT3hJY9vB?7o~hx;<|BU#n4&f> zFHf~l4Fa@)yb6Ah0knnSa7`$E{v&Hx>GyR>P3L`dD5q@v!TKtB-h;A{@0I) zcrcNU6ZB>DB_)=4TwyjCOv$k65zaZcSV9Qj9WLv_C&;~mDI`wZ7(owBD;WB2_Y$Sv zl3Xi;2U3`k7vdpi!2bF`1g5$qzS(WQ0uvS$#{3QBb4mWY9@GELkuAL!)BZvD$|7GC zd2n7idFN0d{kF4I$5{SO)VRTs+~q^2!X>XI;2|3CS75hBF}&_vf|_YvQxhf3+6{Vt z@%2T^@8h#{k6&c9GsPRH4je@A2|)@{cOC+x)6`5{*by%~!9+-g#P-N~e?gxV475m! zDDOXJl&QGSg9GEozS%KIj`1Phn~))JIt?PIstn-$U&uDs=n%?;LaR9zYAy6wsI))1 zZOSI2gb?i#KC^@hLr`uyh_3p%%>*MweY>8TiQN z8SCNMMIa5ObC!M-gXGhRjkAK*yR57pf7&5JF9Lv``6z)VKUOqw%wyEU4xj*l@D_9; z8e-r|DeF{QPITgl7qMJl^#;?1S*i;$f*nMr(M)Nm6~vm5Avd7p#L$Pw2sNWCzpG=v z{QP{FR8cvRufXcF(|io>rpFPaVC;S&Y?F&w#}((lo?zvlNWX$j!Kv4zK`3oC_!8O=fDS-^0Z0Q5bRRUp z+>a-IjzFl1O%9^T{zm>TD7MI_i_NVYy*JUEIz;{i=zi@RJO-H#VIuH+r}DhxL*>Tl zzA&f;Ha1eh9ymcgOltpGHCael3{+SBZ__lRvvW+X1qY zmXd;ls9+ZdMI4qQBL-e`2sdf2+z2y!yup}@gX5b2Q-%sq9YCh&xWR?gY!C*z2imD* z$O?Y~j}ZumJkSya*>tTT?J2_>ygOnLlQJ_iKhQrBJNWV_$AbE#0|U8TS+}1i^GSwS zBn5)6ejy$Ip7&pS1S!AeID~CH1iUUMcs2=h(O%F%-|fPe61O+w7mCImDjW=G(77Gn z4e5I-`&s-%p*lz&aGk|^%@0U%JVnwVSeF7{D{0(5g-@d3Gq+!fRY&zVB#mO2bD7eG zIO-xuN&n;>IwE}}J}L_JtmKkWrhjM%rhn||y3M&vsH(%p&-c5!T=2W{Y4$w)1(DWe zeH>aYigX)~_D96=f;-{<3mU-#aK0oiv2a>q)2iW}z%RRV{WYS^^76{TziH1`+=|+t zf8DNm$6m0=3k8=K4}o~0N`$@y_t9r9Bbd|(S_6+@`Wp6Yq{35b;`;y$b#|NPAdsIt&orh6c(Uwt?pNnRhq}IMBt`6 zz*iMTcF_@wlGk8`he@AI728}x$z+*_Z2(noUWgq-t#h_+4^mA`OkjVNO^_|_pA=EM z_asaN5lG$+m2z~fn6ni(XNvh+QBiS&v#xE+RWb3N3782V1>+J)E32w*j0wALYJhnf zs?zl0;#bhZhVU;S*+|*%#93m#9qKOP3(E|jpI?v^71cqyoFcwI$or23xbH?QicJGw zctY*bWm-u!)sBnI6G1}j%xIdOC=onXdsj=zexUE-muW3PzJ|p1gzm$PZKKUM$g+^6 zw`ykSK78JuYh;T@E0FW1u^RS+mKS}WLsglm@3nS!a4^`!=a%f9x4s{SbG3jc7L+%^ z0_Z557omLDxmCtM?<%q&JQd8*{f{xdQ?bVdk)m7N$=U|7K-pP>| z<8#~mrmUuhfq=#`3ISW4tIG=%8fe`4R{m)(18o&FbpRVP(5=%r*Rl{3*wG=G3%z_n z@6XTAkyR=dF0PS*fiah6_{K<=4Tyhmw7hyWJUmX|&&m8&agfsi{^ZCf+kPusdP8CR zxxfFxxZE}5VpciPrQJ-3iTUYRU&Q0yqai|Fnu2NY7E7ZwOo|Ru&-h;nz$j7_+EjRZ zG@_a0k$R%*)_0zs=PD{lU9!ezMoWbd+EJj@2jVcwM@Q=p%SyY3-%K)5MJ*R__e_nA zxqCdJueZ*@$m-1%NA2v_3X=hN@92{K!S@~ z2&q0D+J&)2@9tjshGRGm7jo{Y9J;K8iRM_8XjQMnAu@|VdLA-a)b3BHAGYe;A#c?&T&qTMbP``=hXi;iR*VML?#OqERP_A z9l47j@UOpy^Rob(VvJAGbrhN6$3`p&;3*n@klP?#kC*6DARxH!f`(OVK8pG;8?WgL z+Rz~of80f&wPXBshtg(&3PP9ER~Wm&_t; zrYo``QX)k}$Qt@yTh+6(vmn@ylHaCAe4 zxI$Ti>%e*mcrUJjzA z@6qP8x}HZi`#^@0`EZI*z{8JF3WUaUrXcT=m3Tl5M6A3!AoES5De>Y=I08#)}} z6Yk!<`y&My5mW=mVNePj@3PWQ9jEEfPr)sCdA>gamj?B86~bgFm?jqzQR_}1ccc7C!CsXViw01hN>L&-=IKN}ZUDFhB+)-eo#op7M77lt(E9*DsA zE!hC}eg?bGu5RhgLz?$b8DKHq!8y_*1SR}t+tJK!ScGaQ zb|P5r-i6r!UpA*J{Qwa`{Plv?(wMI>;KJnm?{3UjDLNj{gEjb&gy#(q4WZN$$5Z3J z`(vpy81mmrZo04M&ks+ai&B)$@0;7!< zHv({`ee|YCt6CO6Nc{5eGIVO5KzBHxmSb3p+?<^6TSw-65B`ffDKjP#Wl}5r#%jyl zXkd^r>V4M?;BS9_|7IsQ4l?Bef`C@zB9~b2pY&WVn4uF^u z@hfjoy&*kpx_Yq?!p)EFT!Ljl^@sOX!S03h5=digYWj{+$Z>rpkrwCdIUK_~JGI1>t*Q_>=QnM%)p z3saz-K=u#IgdsdG(7E~O4UPw}4uBvwjdQ?{3US$xhq$-@_h^HL=oOyWUjs;xZg9Y0 z^8t24E^BP-gMy+WY&S4&TR~T<&3y`l4vZp+VgOO50A`IStO9eO3FFZ z%6|U(1sjbV0Uf^Zs-k)<;1l1u_B1L@-wv!a{ET{rpyA-y0x=(i8h!*I1Q)n@0QaWA z%E2h(Sg(U9gRvBV8osRy~ z3X#;Nk|F%`JFw_go@dbItRPqs>NshPx>0SB1lhC~Y6Op2iyzoO1Wh!r;5q_CgmlQ@ zB^$^u@S{5%`E?)C*n?J~t*MGZSx~SoU=_)u=5jxi221U@?ehnE0qbwv4nMkzii*6p z>vJ8Sz7*eepIzM-h4};jC8}}`LxVhYpr5ktg9yTxiDHl`NO>;~wR_l)8l@(hHNIYd z1=~)GOE#br8xP|Z#41+WEa`A!xPLMXw-JA!`a$|;w8}ySu?C7Qr1s2Fb2#)`wYC>< zyut1Q5TOIbjba;O^!+7!cZdlHB`vK6gZ4n+coYaE=YW??=BqrJIs^!(z4R3DBMGOD z`P^Bp$G&Of-1h#7T9F11C@X2AUhgObwsVqglkVGSNl2h5ph-b47eQWp%09GiA5vMF z&icl8tC?K}$U$eh3os?**c!lqz$t^U3DzfzZy=D^5b8T;$2&X6X99Nf-oOVZ3si3) zlK3p3=lJ=F))-%*2ZlfJkcTt|D>{05Y4P!x2qGdPVB8HQ0+!=zH8Zvlt*7GHHJ>Xf zAshVz&@fRS!q!9vap>v)Ze7#S(P1-FgVKhiQ~=}j9J+~+V6x8P6Xdm=i4tAm|4J3I zCgFjv6vY11ymwR)T%D2`U!W=H*|TS<3ab)hK*S)(Ll9Nd<@!vACCNvwTV<{c&S`ey z{j&>*goB?ybA!ylDf670GzI#2SO_b&F{+4H_;HqDwUGuRSX|a@pkzS4;WnZA!>!CO z_@1kH2k{qjf$R3H#}NNi5o~*8(=?1#$z%z7PsFAMvFR>7eWzz{!=Yba6eDcbCs0P_ zgORZ#wK-JQaPHv7-lVu+r2MG|EFloQw;bzlhju{>g;)e^)pE`zWFPSk|NjTYJ`h02 zo@b>(!3u;B4UTc5@NV2{_DKFpK&-y^lWkhB9U@&*+XC_PmY2m-s+fCZMry5+D@>CN zuL`VisZE<{tWwi2dgzN{Fma!2ygl_D!z$pEdBKlKc}-9fiJAb8Av2)IMD;%Ivc7l% zW-1Tz8Kr>5WJl1-)DDC@Z0)Ttk?4%xtzhAwB9yWGuOs=t``FU|X%H>)_aUE=`hVk@ zsQ=%GKMDqBl8v*(ZoV8c2QeV@O0c zZI+JW&pr@Hld|cE6y9>12}nR~WJDf){2={v&e5XsU#ce zt>lkScR@uPNAyh@iO;J3&Hd@}1`jd>b6_z58FFZ_ax5;5*f_CJ zvkz9{^^uc$?s)wC;4UKevMa=1a5hhwK7PQ3A<=$#dDk5XSNF?ZcNk`;On41YRFOvc z%kD5DFhvkc@0!n8YsLw!?yrvM!*18p)3dZJ82gQb@oFqrkwltDRFvwKKS3n^%Oq27 zQs>6GC)6f%K}yw(X=RF;F~3$;kQ1!Hf&{Zw;CmGVV5jn(m$B z1&3ZLaQ8Jq?ksKElZ}K%q2T@}QCX0xls`R=jf=Y;_|YPXeLw&P!2FBSx@89K0On4Q zw%7(LERvq+-BKpJ*+~ke+>X~b00ohScYhrl8xdMffx#8P7)6i189_J~FTg?WHG-Wk zX;3X{EXGWKe*W<3NhDLyto!oYC#2}yJr$LeKvRVw-pK&tf zG$&mylxA!JJ%ZR3uj{G_8c>V(36#qqCZvn`Kn#NqDOQYU$X{1eD?zUJu!J4@LPDLht&RQmy`W+{$KraRlFRMRjQ$8D2}3aRq2i-2 zeYYxAB{b;>!baFb;0w!iXNVtA!!H9c3yH>Tw$1@2Td2Xs#j|Vf0S|Rq`(n7*79gL* zXBa{CZP)7Nz5px=%xpk>fo}_-VlYj#Q88UiRoiFyWVYU^)_VFYu=ZzM%`XVMkquc} zM*xqwdnO?efQPKGqygYB83M-c{F&6YgTWvhfJu+eF38B>#e;PqS*9wOnY^iB2MGW= zQ7}*gK+^(U3>84R1`M(Q?GGk)a5ZjL92_|RgNmu_%(hH4v{I_72;|!V0iDC028|{3 zD?NulKjohxfd%AtfY_Zjv*r6El-XD_4bB1#$mk^{=zjHtcapNX|Bg|4!o-pGQeY-a zyMaC2DP&gXCiR91FCfZ1V+Gc^^(j(}95@lCj|MXLs(n5NSM*l0FV^A3y6<^f?VtaJof^Sk7WEvDgZ!@k&zLY ziz?v?pkRbQBsM&9zRxR;i8=Wjo^a7Spd5oF0}EPhGovAc_1!6~ZOiWh6I(!E!#u{0Qw~tw~VK~WM z?TN_BH&?)4Y!08BClN>XQsD+$B*D(w{|1uhP4aOl62W~FOjYRQw(oxc$2|H+&|?Jb zdymuKxd66D{YKA$H~T_85Z*KdjB6UmQaESBQ{bd0;@(K{^Y=n4QY3NwvYkgZSj+83?m)Zc<3bZnzX~{P@UATEw@qvI3Nh`hl}K3n8FX z*GAo`Ltt^pH37h;Sz|pNLB>lBR|5K^0X~#J*0Ka0J|QNpTi~~t!S=Q98fu+5S%B^n zWblDl(iL(GMySkymUMO8dgUQP4US-4@x6D>8+ZL$2 z0>dZbrZ1Cz!QCyob$n4$18rrn0R>m#0NMbxgC%_qVF1QL8gOgq=*+^>4Gs>%Ap(

PB?7;a-Du!1{5x8w#@q6P?6Ma4Y?a^M%#_uRnuGhV9i2bT%DwZ(q~ z*`CNdSAg=8auVa>CTs2VzfGkg3rnd6%(n6mC9#YK8_ae-b9u97Bi(t>=>W!}C+{c| z6B8}2;XnBs8uT789bAc-spNG`PP*!ycopT$*))&jykjeCG;wqt)w3)CZ^fF9{ayq-w>;DuA%7i~F^?wOGfRX{95D>bQTo`h4a?<`i zUqNHU0_U~l-$4@y*0PD8ZP^mzk zxa8zB;Pfj$e-e_AAnV=vxoNk%{d^zZqLm^%j+^9;FgiT1?I$Ng4oZ@HTR@CJ$c-6C zP%)7m*&x-4SgJV@C^Z6!@jF=|k=%#@BgS!YP zR!4vd(!g}QIZY5Mbwsg=#N7(4Pe?$Sgc3On`8sIFyn|@Fg@KX+8yYl1&@g~%)`1Gq zxZS{pkEJ9_h1&}?lTsus!W`=VPK*${I^PGS0rFcORC(a+kRd27qv_4M89^6J5Ojbk zjWN`Yc2A=SaWFvoM#)6InSXOxK{!9KoccLsesUmhxn-LcjfW<4D`o*H5mZBp}ED zCFsQWeDT-z=4>JeoI*~+iB9MeZKNaV;(ma$LqkIWr;v+!I{W&;Nrm$kZtq zmkO~6&+nkaTpdZj8dp)&1+fTbYrSPh0;Z-R(~Ym$H=y+j1}IvF;69GZT9XLEulzPp z!QRy-bqPVOu%Mu%<{_xHTxH88!89O!LxTm7QjKs0 zSO$havehQjob<71`fYStadAJW2!n4^IR=U)tqXnt=1TNi^~U%jdn-Ysg!>BPLDSbX zNzgNzn$7|DkOP{0g(rrEfVg>d;tkC_4ZA%QMA9h!KyIO|Ho9^6j+|dsnNWTVq>_RH ztqNlq;NOswLD}mfyoS)IvDQ>jP=Fu2yCc5sSkGDi^EQ)zVp|*O)xnq|QWKVy1yL-9 zePEPD+H}core-S}DFA23!|+C1DEk0+~;9#BH zhiR1i;E~z{tJSP#-&lV2yhP;If|isn33>$`4prFNY~(zK|^?hh94=i_Zd6| zNMry<1m5WfqOMxuA(YMl$&mF3a&g&#zyq>4>GG*!JM0L`;l!n?W7IN}W8?(^g9LH_ z>g9SC?XEI+4-nJy?Z%zk1Oz@}>77e;0E(uyqnQ8K$RdK3K z1qE=QUFU&>B1I}tso|JF%hxPGIH*${0t$Ic#+w6!WkIS<104zW&0i!m?OcNP#w58M zP?#_F)2}G+89(*)y$rl|G-dXSi zix6_2A#4U0c@9?A{ECX(h#$+#Fp~ma2mnY|N$Gt;0u~}H6VTi*U;z!V@NiZ00I`sk zV*!EVDuw^7M|#(93xxG_Gstbs>E1}`7C71~&)R00sH}}sfO%N773?rSP!y)WfZhVc ziYdC!_}*Jm9y)xir%^AIl^tM*076JzQ!&s)Pe_AWB~0e7hM$0e2>i?-+09tj0JlK# zpW2|hXN;`3KFat4|1ND0$j)3o{156AQGB900OVLM(R|p!=lD1r@?zQgBHLCxs zR9gUg38bl;vu$0#L%0knsH(b3pawjI&NvMgG^hrCBLu2_2Gs(nBml&Pp`3TaDj;|? zD70Tp{@ec1DK!{nfczn;T>`@q%>IjsAV!Z*OsqE>NrMdp(C{58&yVt6YHh%TIy#h+ zpDnBJF-zLoZbKYWAa9$f|a3_AHb3gbQq8lvcn4E_jEf@G#)>1==$&x zTL6ZH0)2Qt2e?|Tr%F<%t-&F-6~*|hV~HE#4P`qJHlgrMPyZ=1^9O7q6o2T>1?fV| z5EB}3@m@=U9*87JfP7(HK6{3|9s=Z#JZ8`t58=i-iJ52#4kyyE0H6}56Q+ifAfRH9 zwnvD(JxeJl3L_XFHyV18a?i~{?9U7hnJk^b@C@fVo*tyR2S$ZMBvt;9UtkH1lVUy% z6PBXoin>#bGM*rf=gKAC1J(#$9MF_OnS~Qbgh+z;(o5wATEZE4rUZS@-5)>h0cMFX zfXXfKmj)3{{{8m9ySAT4CFK?7&%(En&t(6b30g5KGF>Of-JOKe={i804GCI zOib6yYv>YK=%^s?f{y^y{oUPNp!zVw_2o9w|0ITwg+p;4hxUc0X1#G=6cE)s2=a)J z1?!M}W}yTF`K8Pl;gTA-FWgRASOD~V6t<^kT>W#HVkUU##Kh-szJ1}Dw~%N560b-U z0U+rtKhKEHHJs|%-_nA0D4--HCQd{8MpkxUDl(AN-?R0+R-NmpA(^C&^*nczH7z@| z&ZwuKApGCH)$LnzS2&M;eKz58eRwCx3IRLINh<~W=Aw@OHEi%lxq~*{ZlI(bgRFkQ zv3DW*yL7&?NRB{mKw^?GjEAfy7AV-XAYY8qc&Ei2HwFB^AEL*ws83=VU`C6oS4v_z zxTACK1Zj1(HqKW-ndnQ0;vbCgtmhpf!yl$ zoKkfdZSZo+;n!GG|9L|_{?fmzuoonuds%$Fo^n{9FbKajQHT;ZcPXU1yW7kQhjRE1 zqDNzlPnLCL|1-S(W`GyUzu#7me~aMu?cpO*!4z@ue&krFs;b6(trR7O-*$3?X1&4p z|EcV|ZKZ z$oKesACKSn?_d4Xz%7i+j&quNJ3sHE?BjR)fs$6Fjro7y_LxFf>< zKDU>jtz-XxZ$XKg9Vs;fq&PKj>%xC`I>36xio||ijg1D7);IMb(Srj=_e~dby=>(n zb!0f!3UJaR=IZYfUv7Vy+xz2OFGbWb4)HfrraM?MAS;x56^V;2(#9yw@KrMDitrF; zI^nrZlYzg7!^JkbudTv^PNQ3Q~J2z-(tePv-j4jg-Tw}xfLpe1eitkZ=m za~PD8bX_s8;q~!)C=`n2!-&REs{%-wk)8b}<9#as_clX}(!yvm*3^7~N%Mtc9oF>f z8EE%3DjSBgm(PYh6h(mQUtdsg&vJMQlnpyOiYCk=JZ%oO^3fpebZL0wSSPv56UR%a z1?mO5dBoq$`f5k7>jtrBnyW{oApKQOPow9wDa_Lh@;ORl7HOsdxrDt5jfMS>tVE3- z&9DV0;jc5-I{E>=267S(HFA2cN5Z-XMrUHl2W`B8ppbb*b-d{2d2{n@N2!B{ z$1a3)e0$R?M9)69`b9IpGJmZ#`P3`Z7a+mgy zy{lhNyENGxj+Rky0hsqU*5S68Xyx0tqQ9Q*$!GSp9(KMs+?$+%F6(a|(qA;cz^NKi z5_SlbQi58hQWzuaT>%*xU!cQY*#AzClYl+DuanoRjn_1Rp5B~v6o^Jt)#S?2*nxBB z$kP_eiD-XC&Z}0Dc|)!twal z?|-LJ1#=6}e0&o7_q;#D)%Clijs9x!d5sXS5B9ZPG~ZRT+}Pa^DtlzpQCD9dcjqu# zOUPTXJW4n&5;V4OPQGzgo;ivy$Aqs8jL!l=S2eEe{Rxl6wy6Qu_|;Dt=5Kg5T5+_9 zfLzS><6?c~+e%ustK^9J_=RfQxteQ6NvBmAzYI%e8&J@5CTj3z+I?g~EEWgPY;Hdc z%kNet!x$%&cN7#zs(!qTt@qtlRmTdel=r{RTMu{5TAt+PD z&OurS1u~?KP|59vKt4^rASMKlC7m6*@=UeYQ$92EqUZbeTW`NUC5wlnM`k2dq;!Ay zD2(N;=gO2oUZgKL3jVlt0H=RWH}pdPo9YSBBTBN-(1A1jrCeID)+)dxu^;xW4*v_b7I;TW32l$1*c$PYg|d z_HJMQ7!$nZG8?vfHBwlESG4=+m|i6@kn%{q;h-UJMg4c0EUH`)rbBo%MC52*x;I*hv!=Z%S!sLuSRS>%7^ZS_weL^jBBUQD5K+;}L}%{J-F?K^a^ZC9t+7>Vq*KO7?Gz?( zBcVB|W8vdJHv5mXY8c9IgipfS{TNnLYJuWTjq@EBCMdbse|z#OynFKFVYZ zrTlkIpWax>$xB}f=)F>+aD1pE zsc(UlROHRkV_0kX!eZ$)UF4Q|ic?>(=er+Ghfi{kV}KIpS@P|$z(3FAbMIjtrQg8* z{V`UjqQ^e<*v2ER=!xe?kg)?zB&(#<%HwufK!?%r(+MN3mrBR%bt3+(uMe(~zJl{| z|6BY7nSE@L#eYcjNSnV0LHi|l*l@`K(m9VRu35+mKg^j-Kfdl^j&okWOhGB-Hye8u zqAyS%O@Oo4KKZ#x6K$^W_|cab59Du!PxY40GkH;h-6A(^$ml^Etp=Q?CxC_fl5#5!gR7R5RVIcZ|*D-oL!I zlwC@!>`m!*KdT|8HXg~s=v?FBZ#s#Cq-3%Evp92y1Ho$a$sAwIz>%=aveDgKl1*Gf z0xT_nVK-P+1NS&xqLU`?^+6cZl`SF8uVY%B6Jw$Q<22Te##*S}3Q|0g$f|e7CJS!^ zg$WRSd?~N5t!Od#K%sv`FF>OEj{1Dn75T29G7_mY=%l|XYtIKrY2(MzbZH?&5d29< z#4x>uF)=Mr+d(h-BS}`ruFuiKYNe3~+vZ>(g;LkT-I$roX&-9xSt!;4o>&AdkyPFO zG;Uxj8G5T^OP*$kmwFZW=j$6lcp$WOSe|xpNrl|}qfg&SK+yND9@xhNd#eblYA7o; zgJXZ*gyW9wOHkR%l1d#I+^zKEPJYyaIv z4T=6^H9_3O7})4ooK8Glaz3{=71Uo;R8&Xs z7CfpW>Zg6IUOY2By&W1uL4mviehw(e1at_PpI|xyUa{@*5kLY;bvy>Z9eYrtSF8ccgu*lA3X&&K8L~&4n3@*3S%J{U$443g zwv_^SVPAj&f3>Zgvdn-G*akvBxS%tz2fq6y9+v^f%p#18fiW^{sv(MEG%q6g;h+RC z2LPGpM{aL$he^{Ex3pxDy*cWK<*9tMIzc4%CE%$O=Pu~VR)7=+p!MIuYFK{75{@V; zCI%HNeBfj5&>aU56*aO;-v5$+xG#i&r(nXpk(C)bFMKawnXFj_9I5(Mv;zoKT>;n` z{!Sg4oSC@|fsk-vu<`EJF)$pIa1Mn#NDW0HL*}#@=J#tJU65s}05-U#c*&z47Y=hTYQhfSW+zIa@2TI;@6Yg{xPZZTZv>hh zQqv<$5I!7UT5RR9L%0hNU6RA%vbKJg2*#a2s7S@LQ3wsF-a!%uFL@n)z!V!S47wAF zLr~sY-G`Q7mtj5Bi_3KEfa7ix_^EOBaljmXVYicjZo^Y^1`a3q+WH#`SKp68?Frk0 z3$_}-rJ%P;v~dUe%nKk(fFEJ2xM+E`E@T{`e-MMAKIR1FiI&SiUMsvjP(COJnG%Pf zls>9qSx;J>I1}g%$QM{Om%v5`qQ-RCjv!pu;Ap^sd88hSaDcMsAW#6ZVOwpX)+9lK z{M}v!jB@VE<;#t(!@PerAqiwV7ZTDww>l|H>vRUf2n4GRz!-tVrS^c~6EHHud(=R| zQk>P?e7e6DyrHn0ux|T6l;sASiln^{5-wxpU`iZ9f7YB3uf| z;u*gsEOG{1HAr%HHkc_t27UoUHl%idaRF(et}DXS39RST(;GEK!gMiw%v4JE2LRaX z36Q@0mKhu&9MN@9f`9`7z;pORWF-2f`?^p@Enj;j2fw_0JIH|I)E>k85#0! zBVR#VD1PdcOW7VA(m*|h!EQ_+fyyCrlM(@r6SmG>Lm@z{>g!VzYL7rtaaL-?`O1~E zoGx1x4xOvp`5SL#f}ab=y}R+UYlZ|{aYI$P#zW@ZNQ>A@5w^&<{N?*b@7CLePmEwjIXHj#fG4Ld;k`^S#3LnvXt zB^_xS0aSz*GUUj+%>m+L90o{nT0|cvR(Umd0CRDqD1cMf(2x#BQm+5kn6+;KASo>f zI6+%Kj&pM#0;-R6f{E1f#F2mzKxRNgu>`0&BiZ?M%__8RpM)aH);0nK*-X5-J zJN}Ihy~PYHjc7_Ihs-MACye=qEeqy>KY0(*6nOi{RQ+W=V3AQI6BTBrTRyB zX$zp^*%f!a?q0e3#mZ6QJ*CG9s;GA^GHFJjdVsMkEuiKqItdp;i@b9k{C9boEZC;+ zyX}ewyq96wgzC!d8xJo?sxdtgcj4U%tbUu|nK%3$>4ZHz=*O9%N#*W$-=ZjYZ^g&6 z=arH!lyuYkp&doS#!NkK%`;AmucJ*&Oi;+XtApejK`ky&$oz zx%jH%VS?tQKkI}-@Rn!CoSOf#SyMU)_(jGn#=x2bRvqvd)j_;D+Lj!vqviBW6-SVt znAqOeG$W+o$e_n}riY$mw_KcVEi073&vE)(Y>hR0?SF1O!@CU5MJH*Ov@ zC$_gX$3YfBG=>5RIRfrrU&D_fLz-^f_D6SIke=GG{GkbH6<>=wnw;!FHlVDS~20#+F$}bT50elYE zkbrduXvW>$9l{<+Qc(8rH)AIY7qklqit5E1gxewBqdFn?tgeKwa)vb44Sxp*2a}$k5*H^$To%;=f7`?QkD=w*0x){|=hhLn4JjwM z`K5dZ8@ME29r0u0ta@P@?Mj|@4w+NZOVHa9qtGY|xk&P17maD#FX z*NW3gDZj3ujM&{t;#Q+WFoj3bY*-VJbk)s>0AEZacL5c@24+@p6gmrj0`wX5@zUU$ zD<93y|BrJO5E5FQ>&W*<%Pnie8r-l3W`Z;v3Q~SSL0f1-gDC`kAz&5Li|aN%Zy?41 zO8~g_RU`P>O@1hNX1Os~JcxuohOs^7@f;Uf1T-MMy%6 zM?H_65`qS>(SxeSVXjqg0>_o+85tRwBkv_p6i|0IOc&jcEy_wxmW1OiK0bcRS3pn@ zGR}`*)_~k}fr3P${Wk$Z9Y13$90U-sfsW83CeQr*1^4)Br^E$$Cq@GJq%YEa!T$t; zJ<{ibQ*jCM%6bd&%~kTF#uC6(^i0{f*X@L;HqRUYmuOi%@t3d5@Lk32l>v+f=x-8O zcpi-#Rd=6mlt%Xibaj1N8GJ|YKXIvYeN$&HiaktARbBHq#8Jzopl#EVusqF6&J6mR1UyzvC#Tc|VY0 z`5}g7X7QSsg85*@5lo$@sjscAIOC*v(SGv)@}ZEo$SDJu+BtG06Tif%NL9-0Cq{Z({pqaydeow$%%Znq7&(B7%v5yGd|CzT>=P~XgugrJnnCN)U}+#IkRzOcEm{Gz0XW%5 zFY=|g8c1P%@ars&gQHO#t$JhJ^+IrAK|wg01~9|1*9_U5(z!0qS^eLPh5dG3r`$gs zaO&TW^=r=$SW)2FF%4?F%SDQjwL=-td&vnu?)Sdr6Z|GeTr@!C$0&OD-@eV|y^M_2 zLw3mw&pY_fvXd(qtcK{ihPvyA1yQ4!sz*BaNj;=lor|i@NvavwRyz--cHOtrpgDV8 zUU~K(16r4-ndXv!^p>f=#>HpnMGgEJb)=sd^H0V!b2f)O*R*ZZ5nuaq?o`)%-+^m} zqOyamaKFMT#k+U%OVX{XFFc(U)L|sxwHrA=#3oh5ENig7yB~o^c>F$U48**2u*|Rx z-n<8jh8L|4uK;(De4)0gcFpL)pDf3w`yNkGmM8QB0t_=x-G}LnuUKwBuBci7Xz-w$ zX%rNq84n%=un)^%P^Bq<_$JQTj64n$eZGl(L8C!dgpw)dV<0{LL^6GKPkM1h=wSDf z{`KQS-Xd{W0a@#Ssr%P;8pQG&n}6srxbhSzgBrz2opcx-8!oZZd$uoMSf&02^BnW% z=&d9y8);%j!Sm{k<4$}kr?FUBVTmX#LN3{=E*Sn6rSGvNq`z0Q_qKK}xCgAV{h%MHJF)!6c>Sd2cnp&Bf`n;Ty0y=fn zOYP)18L5@%Hp zK6e>K3%uE$dhBw*XmxT=1Tl`oj`)9xQ$GtsC+}rk2xc{z`7Ci;OVK*zi~h>^x2>95 z;fi~zM5$b}O$BQiT`D{+Z}0R&mmi_Q-sS`DX<`Wv-d;(<(%s`+YvP*P+FEd+jjt3H zx2G!LNM=v6W;J6dXkE}?lw7g%n_^+uYWKZ7{QO$~+VS!q+)j$7@ml^Djvc6w;@F}} zaoHx@3WeXZmRUdhVD)j?Zfk-&LE+dd;r5)H?Yrw&ecEDvXN9lFk22<5xf?{>tdRxM z;?V`LLMPs*BowTW>t%|?6$w)fE7gYren1lOY1f2`)9J_d9<;5RpSKoF+f7d*8Q@MV zJKm;~m-qFDFx5h&u#>nW5jlQup?j4CpU!Sk?=Y={!Rm?#1cc{F-fU~JZf2zJ@;@M@ z#AjE|zJIME-(xdRuQ?VwbtU{u+jZVo=S)!D`;wJCVUG2N+C5=&;~jf%`E%P{ZhDz4 zP+aC|<{zseH7?@F$(aZXvR?1wd+tbN7-H9w5vW8l7Z?S5Gzi}5f3Gkw9rj}(<{Zmc z&c-Rjmp{yOhY*=(uiUdD-lo6COO|3VMdGy&*qGfA2DF2&+PL_fdp*bfzb-BwWSS9lW*MIqiV#%BSm}IgvruKZ7C`*IJz?*zl~M;$ff+CVe!7L#znFolYGJ3 z$y}Am22FG~YF<9(e>QT=nBf(6Se@Z@5ruvL^%mJil^u1Z*#!+8=_6^D`vIo5wO{5` z#%k?kpDs&H@R2U4f30KZIAn;|Lz^dyAFBX zkjy-{(K5&V#Omb3hayOFzFp_P_)sF&vH5h@=ij?2B>9kSZx2@;D zCHy_TvVVO*nM6qhZ#*6!PKb0-VveJyIIE||qZHGB`MOBPij=#e6tZe~V)&?Y{A-VMnq)a13FDA6#N482ZjtcwVvBYP6Gc%RnO z9~Vm!C~4gGds^eVb~c?2qoAE|O5T`0DkKL>UUEC4vj9(trNUR*VbS;qMue(hF1C`K zX_ftDUbvwHoQ@e8>42xNgCYw-!rw`g`7(LD7_B;a$VMzrG>uWY+{SxepTIoq9!!Ja z-QAaOZ5!p+yT+#&>bCAD_eaiD?{R7gcWGLKIbG8Z1to7R;r7f8y)AwFnIBv|CmMRhaduT+b=%K1gJho!v zCG}**bpyGFJXY5y~<7vRjGnGzh8BCsUDy^2?PK*~OA!F~40gxISc4Yg-DOlZg4>h=uG z9O>W6wW0B+T(KK3Y@WGVFWz&ZcWv(g3@3jAKPXKrAwzXeF{!B|2Yc|kwNWN$qODXb zR3BWml@t?oY4y5|0v#_;@e>X2;7;c)i7bl4e`+^$Qc8sX(b+VRMC|bGu`B&xt(-A> zh+@kr)CthaoBf_@!j?6MNF6s2#>Lg@E)>;jQJXVE`@!)-VQIuoI}1f-ED|s4fdqw; z-U$~jWhQ0+^Ftss#*^`H@Q*ArGbC357G7Zj?zjB}^Lox`|pUT!0p37Y^?BtF$@HXrFs{JH<-t)v{ z=o8k+WOoaeKii)L8y*HtT=eg8q5Szy;1qQkMzZK~XpS8v@5nf=yOGbF)n1Jdtnr=jPYZSRyoJRACBf9k!x3|o4n0lXjcZ&wdRSftpS{!W_|n(( z15L#zIb1vnb=4m?goTkdx;b0lsH0QWc!4J^c%zMk8>GERK`wfsN$vl1ZGAU>Uj284 zkcB2wLI_1qv3creMk zTa>Epu}6+zUwx4<(&()lU&V8X#W7YDl19FOqq(QIm5Q2*f@X>S z$W3LS{ToERqAQ)Ks)b{E;YGrsVZV)@OD4-c`8W$ePWcxMZK-p z+IsRaw~7DiDE%{%82XFCyi{&qa}yp+dWAl0lq=%iOU+_O70D9cHy%6n{e)GMv}^O& zLljP2n@xy#00a3pXZ>!hg?-ev?J)g)k!W{Q@QG7{$B2`zl~oC@j*7Ja#Je<-*0gKbN5B@D2&VKIA!^%p^OY}N-cu(`3!S-@2_<>ku`Ie11+ej~58$ zoULTe_tJY-5;8aPS2kSRJY=QN)H-rGiTUDi`D2FBXN=(~Cxlk4T>l=vmA{hq*kGYt z>$HTM19}LR9!$<|bZUe}iMQwGPq$2#Yx#Cbc_)zp4nyV~Od;r<0)EbPZ08^H z3${?=CD}j+aBc71yN-jO8Mi6uKs-G0U^+J~EsaI_CxVWIhhx#_ak=WnsxW677WOwG@)5ssU76eMrm{&)vyzcJaX z^WA$?RMZ)v|CB~Wr+ynUPbmJ4#(k<-M!kFHP#5^&S69(|rfrVW<+o?gmz@E_s0{Vp zI2EquamPD3e8Q!WlcB%Sof=rxESDYkvbbzy?tGqO@J~i!V8Z)r?Oz(NebR2KxtW^x zgY^=BpAy;gW0|)+>{>(nVxoV~wC}jD3Dvl>B!&cd?^5)x(Cu3uZazt#wvgX=Dbp+!?L^(tl zOF4o+-d!UeE9I!2bBai-??=lg{yMyF>h~NS89&k(jv|^-d@<2|EBZBlBGG6s{IJnr>=tKGS^daAg!lEmv|u_r;^t`>xgQBwR-L{!Op%6f?@| z3x8a{fEg1E65H0oa^L0h279p}q2M^4hAJIhh!;kDvMXH8+{g>l?HTlc zWsD6iCz2GH(f3_HnS$VDt0=FjvWZqvF-FqbJU!^I&-YEQlF-{%(t7S|c+X*p7+ZQT zV)L|5KY=YA$MCf!!;F4%T&=W#1!uzt8mL2fe{R~*sG&hg%I%enhQ*M&= z*fAyo;WK!NjgnbfZDs|j@?z(SNe*7prOAg(XA~`+5)Y8$n7rM4^@r{now35=hW-XD+ zO{$hRJX}nD01D^F0rmgw)$DQlBgPu z1rJ`TjFL_x+>}bFac*p99wlr}$%6`u7XM}|CEKC7@+p;U&7wWTQcwsFf3W9`PAW(#mPGa4;g57)9g?1|+h0#W61Q!E~)Zmkt_V7is(p zZQSg&`Fs%d=lvB%Y*Dk_sm-AJkbc!_qtm+yrJBAM-EA&06lo#vBByWH8ecpl@~Riu zo+iVmqtWOo(jrPp@;7u8b5VUCV>~nzo7m`XQ6&VCAQq$e)ChOdH)w(_1q+skl+r)C z%IBZ%7-J5Cy`uq~%=;#f@?}|Onh@}m){E=aL;uW~Bgdu)@SS2l#G_UxuM)V`e7YsK z&UYgDx1M_r3*~3&3r^$;$AJwBDR2`-%r0DrD~bhe)S30e?culg)Sv~=37bg+!L-3e zj-fETsn5uM(lJZ%iS3WKZUD)A>#yze`KK)3vbvmvs8-fr_zo)NE|*8UQIWHeVvHkkl?wI+qzF%(@X+!bG?|~3 zYIzRK(2}B-?j{Y7MBz{^20lu-`)HS2OjJ9>5S0J6^-~@+HUe!-ijBa_c)M_{Xsx>5 zx}RiAzN5ILj6KO)uM$VA6Umr%W6|to5i^#RzhF#~H(kCWl=03_&byIB6o!!$T~O(u zDdpyFtl$?*>i@Ik_giuNb=AQk?$oP$o)v%q?Y-U_2PgIj| z@srxO_zg2LzcO7!GaOS&j&BzhTo!#EV%+ofVd-T2w#uJ$kFT3|G}5}%<0J_P+eM)& zw@)gDAle`$qSXr+gtxvhs)7hm@{l1mtm z$^U>L2!=%JiM`Y)`Q575S8w$^{VNu&9@J>ATp~u2U!B$XsP=I5GUtea%B8!}4*DlD zychA8uCtI~Y0(UJJjR^M71~58Ihu64JC*)MmAAj55#d?#@Pa{J#eZb&3e55!$ZYJ; zCgC4S5oE$o(k~usoMT5uuAaq#3>w#s6`Q3PSWT>mDt-6`W%@=;V# zIcQrs2Po_knCHNgS_|J%Dj?9j|MME(IcpZGsCnz_Ib}Ut#(EZarCh?!jy_px(u}_j z0=ZKn;PXDu6+PqOy+Ij(A;!g0F7eX`B~B~3BA zr{Wb5lG+UdocGM;Rf6T`im~yHH?fiPS6@Y_+~YeX-*m-Ef^eM9eJPzyE`X7}sK5k2 ze*LyTuihV!i4@PB>pv9X!MesOpk zI@ypN2@>98ycT+v49{39O0ZAGBFa)vQr)-(864t8FwA<LfeD#fnY0aVJlmVtpIat(EoY+Picy+4GxNRYTkosVN2Xp8lcA;XYR9FuZ;jKNgYo-L3cU%lJ9db&E7wZ17A)g&jwKSvTCODc72sei^-{cTc4Mg%19M8HsWZKZV`Y3 zPkdXO`<`0qk}TJ3EV{6ODGr;^Y=SZ&AlMV)gnt`%lT3`ICy$ZX?skN8AT>kN$8wK{^8PR#A zFZ{!vmiH&w-D`3eO602ipr#%mgxe7?|6TXNi6ivyn_sm(0TzAyIN`+3;mwp0zBmPv zD82|RC6PByhgD-ofWlag86oRp2qQY`lcmX-<}D1`*oCPg!(TTR9#OI^W;2G@ku6iu^>zuDrp;S+uw^!J99 zeSfHRs*%bY{6Tmv4N|a&m%P0k^GRyy53WLer-(u^t?;{|@h#(~?6`p1p)PBSTmi-Y zO~1k2yVr|`O46ZgG1T5r@=(&Bk$h;gw)ySp3qt(-_VJ({0$=y)`mVEO(~KuG!t?s|YaB7; zU_4Vkm(%IY)n%qg+*swun?<$%7Osn62*M{!%?(n!UGqBnU7*1R=xX&04avj7AV`6Z zuvnA8L{_>NdN1w2rhKi`_d3$}>!keGk=)ZGe{eW{+=*-cYK<;yldZwmJZ|ZP@ZGpz zKHRq+7AUoOCHRs8?On`qd*fK9kB9Z53i%~+s^cP4%lT-W1_tER08;4-s$;V(^(Osw zQY%>ygeZxNca;q-aKPcl>!^vl!ZaDB+%L;c_*G!04L(WoXci20c6Qk*lQX@#+ch$) zTacm=F1gT++(loYjJ@#8F2WQ~Dv5GuQx+c)ugCW5n+aX0k`vnd4dTQygIkf=sv79z zh!dA1#t+Ulzj<#RGE<$gcdf~}DbdQxyT8~O+NAX+ek&4!-&2Xs z!@BhMe(g(Mohr^iwyM5SsQ$jl!aW--F1yRVd8h6;g5dLW^S5gxNUr8s2r%4sZO9>g zL38=^q6dqX{x6r!jcezZUZAgkR6cVPtsaF>kVGSw&SQ4%_herO(aRzvI37;&LyQIS zWYYOChI*mf{aMX)oFb_sjJtmikC4JNQ)s;G^9euEeP{cX3u&AIIW~%HG4~`ZQqfqA zARoN8!8G`Me_6}~>-SRoci;!F<}$ao$G!jiOWs9^^2`I$EuqtLh(^yLpwcy4XtwCj z>$_(_Nf&yD<;k>X+hcHhH}F79ys12LHopA9D{SCE%bTMo!y7jI{>e}sOy%5Nh*o2u zp`^YuP(s_=pz!qvit=Sd=eXO_`$XH{sBm>Tl2WnhTy%q~@QtSnf2m(HAYvF7DvSx0 z8Mb!(hcR;$BSE-_4Fty8ik?_r3|fyWf;3(OC7BhTWAZX&3lok;=?Z6EHao82dx7f7 zt54VDhGi9u<1l0-_yTX`3^F8~RA1ihPqOrioM3;V`Z+UlQ53E->na`T5T%B&xd<)8 z#vS+R@RN65E0%5^=lQx7c%?t+)_w~Ih_O+)-pG;!icFhZ*Idq=It)k&#?v>IhmR~j z>SPv1r{Cfao_G}2s`7D4(n%*3HqA6aD2V*c{Hed+H(TCuJQ6wH@}Wa%aC7#Q0rfp6 zbqx!~T7(U8h-V;s;cjw9Uc5CgqL?Bs?7>N^58iR!`Xy7(G*|2ruBpAs$Zpo4vdMz&D16Us5k9K^d7G)5ci#SH zq2eNi5KGRx#f5-iSp%_Cr^vxo$)jUx6Q!2LhzPCIx#fI2@{?%&NSQk`mPWN=m#v?@ zYR9aZ*~ifD^DeY=c(6Mz%!C;nBa*76$~WRO_F0{(fnDQ zujj=#Ju(#gI-XLfpO0{?{N7SmwEHF@Y2lt(QCUKr!`Q(Nc_)SwqgA0}WbB+Y%G1OA ze2=VGhfYwq-hpkJoq?UJ(TJfGkHO(DWSvFx?uv+v;K_+uF4-t)=>`wg2awp5di4 zn@{gQJl9!4iG^bP3e^d##{oMSZSY^zB?s1mO)r{>SP4C~Bfu z$<@bEe36Cwt~;d}g(5b$pPWdJEU2@7P?gv!w3tilelBMrePI)WGniD4Sk}>zyYZ7e z+K?s^x$io6h39!Xnt&la@u9wMX{t^o)ts?3ZG3N!Wo(E>B`;tTeP!jjyE%(*>p{Iv z!a^z_m-gDw({1AI2O}-&qDUfnhT`Mry2MBmCZE80!RPMxk%*z8uI+LA_@oo6lF)~F zm-^o^?qkRNH%FI$zgZ4<-Vzp(5aU*H`2ig`?+-uGB{G zFHC2?mtMtg!rXK97|Mx1>z<--#iRYsup0WsN40^MhN2U>uPj!*RF3|qpxHd#*Xfh+ z@sB#sW0Fno@MqTupXr6uuW3XUjgs7{ufcQg@!C8e&3Pccrt7pQK+6pD?s+qc$E7HI z!peYq-;nqie;#`xui!%Ptp!IND)MN1acgRXM-$7@HTh;R8{R3;s*{tsld^pxErujhFW;yp1(x7gu%G{gJG^q;zpb zjEsp*bcu&xrp9VukwHclk1_joDWiFQ^>dL8tC38_+za0>QgjbR&rVZ9O#6~ouz)*r$BS%c$WKN6 z2o;Y#K6~?@plcux`+G+5m?(zNlxu{q^1*jOo78c$r*_5E%2AIEs1HK2JZG4!HlD5K zj-%f2mj<%v8ZJ&=;AFgE)iF4#hq2cP(S2K**feQ;|H7hpTuL5Yr{U8&-Ee1Y#|g0l7mGxp@UmJb+Kvi0VR&L^vtf0yD!;~E%-nEFqY z=b&Dt#95oup3b57X>5HpQc>)AExxv*3N>qoeK&WZ?oej#Ma|{uk`_~usJHqN0DOFJIO$?$$_p z^g1gq_=P!!n!zrGy#n7QBJMnfi#uu9Brvubp(-W6(n;JOw$S@qgS!~E{u zI9hjKUFH;j-L)lNss3UrU)8AM$78E!J8M-u* zG(yOt%cxjuc;4eBBN!MqMET6bW9uD_x;l!(;!P}y$ri}XH%ViDNo(mdQhK!TmN4&W z@ZZt79;riE{B-bUH%-yR^}%dPo~gxTIjVch{JaA(^WB#`H!NWcSHM*Dh8gELh0~xG zk!Bp+r7=r?&_+iCi-_NE5hMNyd~{m8432CTSY}O!J)y(cmB9d+)E&jA2)O`xS0svH z*-DR9HUAp+@*F*$<~|!k11}XD2a(99%*ZSrOWV9Z8HP3xVxT3Rj;zJsai~z&MC){K z3P{Z7P3BQ2l|^G#$g|g0bT;06-k&zsl{=fsa&xM&7}~nkH~6aBwiuh6$Awi*?(0V+ zdk5tn4C)A%vN=_aTNzEy&5hoYWP4&&z1qH)|0P3Z(!KXN>@8oG-+o(Aip{BFOl z_aAGV?D?oR!ku - Download_on_the_App_Store_Badge_US-UK_RGB_blk_4SVG_092917 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/static/images/scratchlink/windows-store-badge.svg b/static/images/scratchlink/windows-store-badge.svg deleted file mode 100755 index 21c139edd..000000000 --- a/static/images/scratchlink/windows-store-badge.svg +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/static/svgs/extensions/android.svg b/static/svgs/extensions/android.svg new file mode 100644 index 000000000..2a13660a3 --- /dev/null +++ b/static/svgs/extensions/android.svg @@ -0,0 +1,14 @@ + + + + Android + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/static/svgs/extensions/chromeos.svg b/static/svgs/extensions/chromeos.svg new file mode 100644 index 000000000..a26c55c29 --- /dev/null +++ b/static/svgs/extensions/chromeos.svg @@ -0,0 +1,14 @@ + + + + Chrome + Created with Sketch. + + + + + + + + + \ No newline at end of file From 465a87d453480cccf69363b9b04ac03f7344d088 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 23 Sep 2019 11:16:44 -0400 Subject: [PATCH 22/38] use Scratch3Registration instead of JoinModal in /join --- src/views/join/join.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/views/join/join.jsx b/src/views/join/join.jsx index a62e7d449..4b2b9e6b5 100644 --- a/src/views/join/join.jsx +++ b/src/views/join/join.jsx @@ -1,6 +1,6 @@ const React = require('react'); const render = require('../../lib/render.jsx'); -const JoinModal = require('../../components/modal/join/modal.jsx'); +const Scratch3Registration = require('../../components/registration/scratch3-registration.jsx'); const ErrorBoundary = require('../../components/errorboundary/errorboundary.jsx'); // Require this even though we don't use it because, without it, webpack runs out of memory... const Page = require('../../components/page/www/page.jsx'); // eslint-disable-line no-unused-vars @@ -20,7 +20,8 @@ const Register = () => ( - Date: Mon, 23 Sep 2019 11:25:10 -0400 Subject: [PATCH 23/38] pass createProjectOnComplete prop through join flow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …so welcome step can display correct completion text --- src/components/join-flow/join-flow.jsx | 2 ++ src/components/join-flow/welcome-step.jsx | 7 +++++-- src/components/modal/join/modal.jsx | 5 ++++- src/components/registration/scratch3-registration.jsx | 5 +++-- src/l10n.json | 1 + 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/components/join-flow/join-flow.jsx b/src/components/join-flow/join-flow.jsx index 6849531e6..2d40452e2 100644 --- a/src/components/join-flow/join-flow.jsx +++ b/src/components/join-flow/join-flow.jsx @@ -155,6 +155,7 @@ class JoinFlow extends React.Component { onRegistrationError={this.handleRegistrationError} /> - } + ) : ( + + )} title={`${this.props.intl.formatMessage( {id: 'registration.welcomeStepTitleNonEducator'}, {username: this.props.username} @@ -79,6 +81,7 @@ class WelcomeStep extends React.Component { } WelcomeStep.propTypes = { + createProjectOnComplete: PropTypes.bool, email: PropTypes.string, intl: intlShape, onNextStep: PropTypes.func, diff --git a/src/components/modal/join/modal.jsx b/src/components/modal/join/modal.jsx index bd17ac6ab..7edc22a3c 100644 --- a/src/components/modal/join/modal.jsx +++ b/src/components/modal/join/modal.jsx @@ -6,7 +6,8 @@ const JoinFlow = require('../../join-flow/join-flow.jsx'); require('./modal.scss'); const JoinModal = ({ - onCompleteRegistration, // eslint-disable-line no-unused-vars + createProjectOnComplete, + onCompleteRegistration, onRequestClose, ...modalProps }) => ( @@ -20,12 +21,14 @@ const JoinModal = ({ {...modalProps} > ); JoinModal.propTypes = { + createProjectOnComplete: PropTypes.bool, onCompleteRegistration: PropTypes.func, onRequestClose: PropTypes.func, showCloseButton: PropTypes.bool diff --git a/src/components/registration/scratch3-registration.jsx b/src/components/registration/scratch3-registration.jsx index 220c01433..0dbf68289 100644 --- a/src/components/registration/scratch3-registration.jsx +++ b/src/components/registration/scratch3-registration.jsx @@ -8,12 +8,14 @@ const JoinModal = require('../modal/join/modal.jsx'); require('./registration.scss'); const Registration = ({ + createProjectOnComplete, handleCloseRegistration, handleCompleteRegistration, isOpen }) => (

Date: Tue, 24 Sep 2019 18:43:29 -0400 Subject: [PATCH 24/38] wired showCloseButton through new join flow --- src/components/registration/scratch3-registration.jsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/registration/scratch3-registration.jsx b/src/components/registration/scratch3-registration.jsx index 0dbf68289..f88031b8e 100644 --- a/src/components/registration/scratch3-registration.jsx +++ b/src/components/registration/scratch3-registration.jsx @@ -11,13 +11,15 @@ const Registration = ({ createProjectOnComplete, handleCloseRegistration, handleCompleteRegistration, - isOpen + isOpen, + showCloseButton }) => (
@@ -28,7 +30,8 @@ Registration.propTypes = { createProjectOnComplete: PropTypes.bool, handleCloseRegistration: PropTypes.func, handleCompleteRegistration: PropTypes.func, - isOpen: PropTypes.bool + isOpen: PropTypes.bool, + showCloseButton: PropTypes.bool }; const mapDispatchToProps = (dispatch, ownProps) => ({ From 6fc57057794afb371c83e5d7ae12643553ee21ec Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Tue, 17 Sep 2019 22:17:39 -0400 Subject: [PATCH 25/38] revise country placeholder --- src/components/join-flow/country-step.jsx | 2 +- src/l10n.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/join-flow/country-step.jsx b/src/components/join-flow/country-step.jsx index 92ca4559c..9b697439a 100644 --- a/src/components/join-flow/country-step.jsx +++ b/src/components/join-flow/country-step.jsx @@ -30,7 +30,7 @@ class CountryStep extends React.Component { this.countryOptions = [...countryData.registrationCountryOptions]; this.countryOptions.unshift({ // add placeholder as first option disabled: true, - label: this.props.intl.formatMessage({id: 'general.country'}), + label: this.props.intl.formatMessage({id: 'registration.selectCountry'}), value: 'null' }); } diff --git a/src/l10n.json b/src/l10n.json index 03720759f..2e0eba247 100644 --- a/src/l10n.json +++ b/src/l10n.json @@ -185,7 +185,7 @@ "registration.personalStepDescription": "Your individual responses will not be displayed publicly, and will be kept confidential and secure", "registration.private": "We will keep this information private.", "registration.receiveEmails": "I'd like to receive emails from the Scratch Team about project ideas, events, and more.", - "registration.selectCountry": "select country", + "registration.selectCountry": "Select country", "registration.studentPersonalStepDescription": "This information will not appear on the Scratch website.", "registration.showPassword": "Show password", "registration.troubleReload": "Scratch is having trouble finishing registration. Try reloading the page or try again in another browser.", From ccffa35037a87bd62e6cc732154228cf7da92de8 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Wed, 25 Sep 2019 09:57:40 -0400 Subject: [PATCH 26/38] change gender string defaults --- src/components/join-flow/gender-step.jsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/components/join-flow/gender-step.jsx b/src/components/join-flow/gender-step.jsx index 1aa358183..aeeb970e6 100644 --- a/src/components/join-flow/gender-step.jsx +++ b/src/components/join-flow/gender-step.jsx @@ -66,8 +66,14 @@ class GenderStep extends React.Component { } handleValidSubmit (formData, formikBag) { formikBag.setSubmitting(false); - if (!formData.gender || formData.gender === 'null') { - formData.gender = ''; // default to blank + // handle defaults: + // when gender is specifically made blank, use "(blank)" + if (!formData.gender || formData.gender === '') { + formData.gender = '(blank)'; + } + // when user clicks Next without making any selection, use "(skipped)" + if (formData.gender === 'null') { + formData.gender = '(skipped)'; } delete formData.custom; this.props.onNextStep(formData); @@ -155,7 +161,7 @@ class GenderStep extends React.Component { id="GenderRadioOptionPreferNot" label={this.props.intl.formatMessage({id: 'registration.genderOptionPreferNotToSay'})} selectedValue={values.gender} - value="Prefer not to say" + value="(Prefer not to say)" onSetFieldValue={setFieldValue} />
From dc5506f2461268922e1e0d76b8c15a05328219b4 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2019 15:10:31 +0000 Subject: [PATCH 27/38] chore(package): update scratch-gui to version 0.1.0-prerelease.20190925145604 Closes #3380 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cd265563a..80e423896 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,7 @@ "redux-mock-store": "^1.2.3", "redux-thunk": "2.0.1", "sass-loader": "6.0.6", - "scratch-gui": "0.1.0-prerelease.20190918163722", + "scratch-gui": "0.1.0-prerelease.20190925145604", "scratch-l10n": "latest", "selenium-webdriver": "3.6.0", "slick-carousel": "1.6.0", From 3d536bae411cbbf2ced108780e656d324b940b3e Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2019 15:10:35 +0000 Subject: [PATCH 28/38] chore(package): update lockfile package-lock.json --- package-lock.json | 96 +++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 49 deletions(-) diff --git a/package-lock.json b/package-lock.json index a70ed6992..57c8ea9d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@babel/cli": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.6.0.tgz", - "integrity": "sha512-1CTDyGUjQqW3Mz4gfKZ04KGOckyyaNmKneAMlABPS+ZyuxWv3FrVEVz7Ag08kNIztVx8VaJ8YgvYLSNlMKAT5Q==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.6.2.tgz", + "integrity": "sha512-JDZ+T/br9pPfT2lmAMJypJDTTTHM9ePD/ED10TRjRzJVdEVy+JB3iRlhzYmTt5YkNgHvxWGlUVnLtdv6ruiDrQ==", "dev": true, "requires": { "chokidar": "^2.1.8", @@ -479,17 +479,17 @@ } }, "@babel/core": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.6.0.tgz", - "integrity": "sha512-FuRhDRtsd6IptKpHXAa+4WPZYY2ZzgowkbLBecEDDSje1X/apG7jQM33or3NdOmjXBKWGOg4JmSiRfUfuTtHXw==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.6.2.tgz", + "integrity": "sha512-l8zto/fuoZIbncm+01p8zPSDZu/VuuJhAfA7d/AbzM09WR7iVhavvfNDYCNpo1VvLk6E6xgAoP9P+/EMJHuRkQ==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.0", - "@babel/helpers": "^7.6.0", - "@babel/parser": "^7.6.0", + "@babel/generator": "^7.6.2", + "@babel/helpers": "^7.6.2", + "@babel/parser": "^7.6.2", "@babel/template": "^7.6.0", - "@babel/traverse": "^7.6.0", + "@babel/traverse": "^7.6.2", "@babel/types": "^7.6.0", "convert-source-map": "^1.1.0", "debug": "^4.1.0", @@ -510,22 +510,21 @@ } }, "@babel/generator": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.0.tgz", - "integrity": "sha512-Ms8Mo7YBdMMn1BYuNtKuP/z0TgEIhbcyB8HVR6PPNYp4P61lMsABiS4A3VG1qznjXVCf3r+fVHhm4efTYVsySA==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.2.tgz", + "integrity": "sha512-j8iHaIW4gGPnViaIHI7e9t/Hl8qLjERI6DcV9kEpAIDJsAOrcnXqRS7t+QbhL76pwbtqP+QCQLL0z1CyVmtjjQ==", "dev": true, "requires": { "@babel/types": "^7.6.0", "jsesc": "^2.5.1", "lodash": "^4.17.13", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" + "source-map": "^0.5.0" } }, "@babel/parser": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.0.tgz", - "integrity": "sha512-+o2q111WEx4srBs7L9eJmcwi655eD8sXniLqMB93TBK9GrNzGrxDWSjiqz2hLU0Ha8MTXFIP0yd9fNdP+m43ZQ==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.2.tgz", + "integrity": "sha512-mdFqWrSPCmikBoaBYMuBulzTIKuXVPtEISFbRRVNwMWpCms/hmE2kRq0bblUHaNRKrjRlmVbx1sDHmjmRgD2Xg==", "dev": true }, "@babel/template": { @@ -540,16 +539,16 @@ } }, "@babel/traverse": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.0.tgz", - "integrity": "sha512-93t52SaOBgml/xY74lsmt7xOR4ufYvhb5c5qiM6lu4J/dWGMAfAh6eKw4PjLes6DI6nQgearoxnFJk60YchpvQ==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.2.tgz", + "integrity": "sha512-8fRE76xNwNttVEF2TwxJDGBLWthUkHWSldmfuBzVRmEDWOtu4XdINTgN7TDWzuLg4bbeIMLvfMFD9we5YcWkRQ==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.0", + "@babel/generator": "^7.6.2", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.0", + "@babel/parser": "^7.6.2", "@babel/types": "^7.6.0", "debug": "^4.1.0", "globals": "^11.1.0", @@ -686,33 +685,32 @@ } }, "@babel/helpers": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.6.0.tgz", - "integrity": "sha512-W9kao7OBleOjfXtFGgArGRX6eCP0UEcA2ZWEWNkJdRZnHhW4eEbeswbG3EwaRsnQUAEGWYgMq1HsIXuNNNy2eQ==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.6.2.tgz", + "integrity": "sha512-3/bAUL8zZxYs1cdX2ilEE0WobqbCmKWr/889lf2SS0PpDcpEIY8pb1CCyz0pEcX3pEb+MCbks1jIokz2xLtGTA==", "dev": true, "requires": { "@babel/template": "^7.6.0", - "@babel/traverse": "^7.6.0", + "@babel/traverse": "^7.6.2", "@babel/types": "^7.6.0" }, "dependencies": { "@babel/generator": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.0.tgz", - "integrity": "sha512-Ms8Mo7YBdMMn1BYuNtKuP/z0TgEIhbcyB8HVR6PPNYp4P61lMsABiS4A3VG1qznjXVCf3r+fVHhm4efTYVsySA==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.2.tgz", + "integrity": "sha512-j8iHaIW4gGPnViaIHI7e9t/Hl8qLjERI6DcV9kEpAIDJsAOrcnXqRS7t+QbhL76pwbtqP+QCQLL0z1CyVmtjjQ==", "dev": true, "requires": { "@babel/types": "^7.6.0", "jsesc": "^2.5.1", "lodash": "^4.17.13", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" + "source-map": "^0.5.0" } }, "@babel/parser": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.0.tgz", - "integrity": "sha512-+o2q111WEx4srBs7L9eJmcwi655eD8sXniLqMB93TBK9GrNzGrxDWSjiqz2hLU0Ha8MTXFIP0yd9fNdP+m43ZQ==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.2.tgz", + "integrity": "sha512-mdFqWrSPCmikBoaBYMuBulzTIKuXVPtEISFbRRVNwMWpCms/hmE2kRq0bblUHaNRKrjRlmVbx1sDHmjmRgD2Xg==", "dev": true }, "@babel/template": { @@ -727,16 +725,16 @@ } }, "@babel/traverse": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.0.tgz", - "integrity": "sha512-93t52SaOBgml/xY74lsmt7xOR4ufYvhb5c5qiM6lu4J/dWGMAfAh6eKw4PjLes6DI6nQgearoxnFJk60YchpvQ==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.2.tgz", + "integrity": "sha512-8fRE76xNwNttVEF2TwxJDGBLWthUkHWSldmfuBzVRmEDWOtu4XdINTgN7TDWzuLg4bbeIMLvfMFD9we5YcWkRQ==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.0", + "@babel/generator": "^7.6.2", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.0", + "@babel/parser": "^7.6.2", "@babel/types": "^7.6.0", "debug": "^4.1.0", "globals": "^11.1.0", @@ -1018,9 +1016,9 @@ } }, "@types/babel__generator": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.0.2.tgz", - "integrity": "sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.0.tgz", + "integrity": "sha512-c1mZUu4up5cp9KROs/QAw0gTeHrw/x7m52LcnvMxxOZ03DmLwPV0MlGmlgzV3cnSdjhJOZsj7E7FHeioai+egw==", "dev": true, "requires": { "@babel/types": "^7.0.0" @@ -15561,15 +15559,15 @@ } }, "scratch-gui": { - "version": "0.1.0-prerelease.20190918163722", - "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20190918163722.tgz", - "integrity": "sha512-2BM65eEvknpgrlkZgYP31H4v4rUsYuacY2NBBLwZ+zBScIsYMrfuxr2zPtvtxOkjYf9yH6TJef8/i/YZmPtfhw==", + "version": "0.1.0-prerelease.20190925145604", + "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20190925145604.tgz", + "integrity": "sha512-NEL0AMDJaNNnl5mbN0YNL4rxxQW8nlsxAn9ZYUGScqoCLdHB30+0ipEWCnVLacIHCkbq1FcPXD237IOvnfT+8A==", "dev": true }, "scratch-l10n": { - "version": "3.5.20190917223712", - "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.5.20190917223712.tgz", - "integrity": "sha512-kWkeUD5An4RwbAVZU7OUqS25D1rrdI73ZYhNn3tQhGu545JaITZf37AvVQW4DyZU+wFHQ1G30nWls7bXZLEPbA==", + "version": "3.5.20190924223720", + "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.5.20190924223720.tgz", + "integrity": "sha512-5bNisTEe9WJI+w3TQQ7XVutoFZuBKtHLLfijFd2vOkLv93XoxEQvpW62uz34uyj2tX9N8JPzCB+KaJalC+Z8Mg==", "dev": true, "requires": { "@babel/cli": "^7.1.2", From 915d2d3a5768a2f3623ec74d2fe23a293c9b5560 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2019 20:08:18 +0000 Subject: [PATCH 29/38] chore(package): update scratch-gui to version 0.1.0-prerelease.20190925195008 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 80e423896..469e2c514 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,7 @@ "redux-mock-store": "^1.2.3", "redux-thunk": "2.0.1", "sass-loader": "6.0.6", - "scratch-gui": "0.1.0-prerelease.20190925145604", + "scratch-gui": "0.1.0-prerelease.20190925195008", "scratch-l10n": "latest", "selenium-webdriver": "3.6.0", "slick-carousel": "1.6.0", From 13cdad9662c54c73e0caa9cf48a33fca43f0fdaf Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2019 20:08:22 +0000 Subject: [PATCH 30/38] chore(package): update lockfile package-lock.json --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 57c8ea9d8..912d58157 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15559,9 +15559,9 @@ } }, "scratch-gui": { - "version": "0.1.0-prerelease.20190925145604", - "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20190925145604.tgz", - "integrity": "sha512-NEL0AMDJaNNnl5mbN0YNL4rxxQW8nlsxAn9ZYUGScqoCLdHB30+0ipEWCnVLacIHCkbq1FcPXD237IOvnfT+8A==", + "version": "0.1.0-prerelease.20190925195008", + "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20190925195008.tgz", + "integrity": "sha512-SDFpAQZDVO5jbwMfJ75hTmaUUuVJcer/AzhWMzxTes952a6d2qjvzNYxQLkjojhmkbQMv0icch5/TNloHga3rg==", "dev": true }, "scratch-l10n": { From 260be874cf8202cf4c6a404258bbfe0c67a22c72 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2019 21:15:06 +0000 Subject: [PATCH 31/38] chore(package): update scratch-gui to version 0.1.0-prerelease.20190925210032 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 469e2c514..5b4296c66 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,7 @@ "redux-mock-store": "^1.2.3", "redux-thunk": "2.0.1", "sass-loader": "6.0.6", - "scratch-gui": "0.1.0-prerelease.20190925195008", + "scratch-gui": "0.1.0-prerelease.20190925210032", "scratch-l10n": "latest", "selenium-webdriver": "3.6.0", "slick-carousel": "1.6.0", From 68ef754386309325754c2ca8a62d18a7703260c3 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2019 21:15:10 +0000 Subject: [PATCH 32/38] chore(package): update lockfile package-lock.json --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 912d58157..9c1862204 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15559,9 +15559,9 @@ } }, "scratch-gui": { - "version": "0.1.0-prerelease.20190925195008", - "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20190925195008.tgz", - "integrity": "sha512-SDFpAQZDVO5jbwMfJ75hTmaUUuVJcer/AzhWMzxTes952a6d2qjvzNYxQLkjojhmkbQMv0icch5/TNloHga3rg==", + "version": "0.1.0-prerelease.20190925210032", + "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20190925210032.tgz", + "integrity": "sha512-+W8an9boz7dBKSFpsr3VyCchhd4E2AOJAbmvI+0/t5BLRzMGnLJggs8epFIhiK336yapmEYdvxZgbsYzHmR94w==", "dev": true }, "scratch-l10n": { From 66d6524d07b8bbb0a53ac482b6315c5980bd96d3 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2019 14:05:04 +0000 Subject: [PATCH 33/38] chore(package): update scratch-gui to version 0.1.0-prerelease.20190926135045 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5b4296c66..98f158732 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,7 @@ "redux-mock-store": "^1.2.3", "redux-thunk": "2.0.1", "sass-loader": "6.0.6", - "scratch-gui": "0.1.0-prerelease.20190925210032", + "scratch-gui": "0.1.0-prerelease.20190926135045", "scratch-l10n": "latest", "selenium-webdriver": "3.6.0", "slick-carousel": "1.6.0", From e8e6662eba165c609377c977a0c512c0ffa70266 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2019 14:05:08 +0000 Subject: [PATCH 34/38] chore(package): update lockfile package-lock.json --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9c1862204..a2a4bd879 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15559,9 +15559,9 @@ } }, "scratch-gui": { - "version": "0.1.0-prerelease.20190925210032", - "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20190925210032.tgz", - "integrity": "sha512-+W8an9boz7dBKSFpsr3VyCchhd4E2AOJAbmvI+0/t5BLRzMGnLJggs8epFIhiK336yapmEYdvxZgbsYzHmR94w==", + "version": "0.1.0-prerelease.20190926135045", + "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20190926135045.tgz", + "integrity": "sha512-NNn+GsRBflB+CeWFX37iKZtQc10TnDnNOw3JobXLoiQQzIZiL8N/Hg4BSyfoWprj2tA6g08rkQEjMtC4i16tRQ==", "dev": true }, "scratch-l10n": { From 2f9716cea529a25049627e24a6818d0f1e49bc9a Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2019 14:26:01 +0000 Subject: [PATCH 35/38] chore(package): update scratch-gui to version 0.1.0-prerelease.20190926141105 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 98f158732..98d034369 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,7 @@ "redux-mock-store": "^1.2.3", "redux-thunk": "2.0.1", "sass-loader": "6.0.6", - "scratch-gui": "0.1.0-prerelease.20190926135045", + "scratch-gui": "0.1.0-prerelease.20190926141105", "scratch-l10n": "latest", "selenium-webdriver": "3.6.0", "slick-carousel": "1.6.0", From c28b51d75bcd36083d679c60f1cb3d69c164eeaa Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2019 14:26:05 +0000 Subject: [PATCH 36/38] chore(package): update lockfile package-lock.json --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a2a4bd879..93d8b746b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15559,9 +15559,9 @@ } }, "scratch-gui": { - "version": "0.1.0-prerelease.20190926135045", - "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20190926135045.tgz", - "integrity": "sha512-NNn+GsRBflB+CeWFX37iKZtQc10TnDnNOw3JobXLoiQQzIZiL8N/Hg4BSyfoWprj2tA6g08rkQEjMtC4i16tRQ==", + "version": "0.1.0-prerelease.20190926141105", + "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20190926141105.tgz", + "integrity": "sha512-0/OLyeiU1qa+e35dsy+P/sz0V9bdgbID5Rs1VUuxpj+F+8QFUNuoZTWwKgqtAIb4M7Q6zg5ve+1iDrnzOMw3bQ==", "dev": true }, "scratch-l10n": { From a3359a487ee591b85642482dc1217cb6f0f9ae12 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 26 Sep 2019 10:39:36 -0400 Subject: [PATCH 37/38] Update routes files --- src/routes.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/routes.json b/src/routes.json index c1494768f..4af0ff35b 100644 --- a/src/routes.json +++ b/src/routes.json @@ -295,7 +295,7 @@ }, { "name": "wedo2", - "pattern": "^/wedo/?$", + "pattern": "^/wedo/?(\\?.*)?$", "routeAlias": "/wedo/?$", "view": "wedo2/wedo2", "title": "WeDo 2.0" @@ -309,28 +309,28 @@ }, { "name": "ev3", - "pattern": "^/ev3/?$", + "pattern": "^/ev3/?(\\?.*)?$", "routeAlias": "/ev3/?$", "view": "ev3/ev3", "title": "LEGO MINDSTORMS EV3" }, { "name": "microbit", - "pattern": "^/microbit/?$", + "pattern": "^/microbit/?(\\?.*)?$", "routeAlias": "/microbit/?$", "view": "microbit/microbit", "title": "micro:bit" }, { "name": "vernier", - "pattern": "^/vernier/?$", + "pattern": "^/vernier/?(\\?.*)?$", "routeAlias": "/vernier/?$", "view": "gdxfor/gdxfor", "title": "Vernier Go Direct Force and Acceleration" }, { "name": "boost", - "pattern": "^/boost/?$", + "pattern": "^/boost/?(\\?.*)?$", "routeAlias": "/boost/?$", "view": "boost/boost", "title": "LEGO BOOST" From db8cc6f80f2b1b9a5a75d919516a7e0b3237b6eb Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 26 Sep 2019 10:43:36 -0400 Subject: [PATCH 38/38] Make app store links open in a new tab --- src/components/install-scratch/install-scratch.jsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/install-scratch/install-scratch.jsx b/src/components/install-scratch/install-scratch.jsx index eff760cff..eb1c40beb 100644 --- a/src/components/install-scratch/install-scratch.jsx +++ b/src/components/install-scratch/install-scratch.jsx @@ -63,6 +63,8 @@ const InstallScratch = ({