From 13d67eb2a399e54375375b48567346088cd4d2b5 Mon Sep 17 00:00:00 2001 From: Zainab Amir Date: Tue, 19 Jul 2022 04:14:04 -0700 Subject: [PATCH] feat: Remove discount banner code (#601) --- package-lock.json | 67 ---------------------- package.json | 1 - src/_style.scss | 49 ---------------- src/base-component/BaseComponent.jsx | 29 +++------- src/base-component/DiscountBanner.jsx | 70 ----------------------- src/base-component/LargeLayout.jsx | 15 +---- src/base-component/LargeLeftLayout.jsx | 60 ++----------------- src/base-component/MediumLayout.jsx | 62 ++------------------ src/base-component/SideDiscountBanner.jsx | 38 ------------ src/base-component/SmallLayout.jsx | 55 ++---------------- src/base-component/messages.jsx | 5 -- src/common-components/Logistration.jsx | 2 +- src/register/RegistrationPage.jsx | 17 ------ src/register/messages.jsx | 5 -- 14 files changed, 28 insertions(+), 447 deletions(-) delete mode 100644 src/base-component/DiscountBanner.jsx delete mode 100644 src/base-component/SideDiscountBanner.jsx diff --git a/package-lock.json b/package-lock.json index 0d923938..34c8ece0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,6 @@ "@fortawesome/react-fontawesome": "0.1.18", "@redux-devtools/extension": "3.2.2", "classnames": "2.3.1", - "clipboard": "2.0.10", "core-js": "3.21.1", "extract-react-intl-messages": "4.1.1", "fastest-levenshtein": "1.0.12", @@ -9390,16 +9389,6 @@ "node": ">= 10" } }, - "node_modules/clipboard": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.10.tgz", - "integrity": "sha512-cz3m2YVwFz95qSEbCDi2fzLN/epEN9zXBvfgAoGkvGOJZATMl9gtTDVOtBYkx2ODUJl2kvmud7n32sV2BpYR4g==", - "dependencies": { - "good-listener": "^1.2.2", - "select": "^1.1.2", - "tiny-emitter": "^2.0.0" - } - }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -10722,11 +10711,6 @@ "node": ">=0.4.0" } }, - "node_modules/delegate": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", - "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" - }, "node_modules/depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -13794,14 +13778,6 @@ "node": ">=0.10.0" } }, - "node_modules/good-listener": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", - "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", - "dependencies": { - "delegate": "^3.1.2" - } - }, "node_modules/got": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", @@ -24386,11 +24362,6 @@ "dev": true, "optional": true }, - "node_modules/select": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", - "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" - }, "node_modules/select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -26240,11 +26211,6 @@ "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", "dev": true }, - "node_modules/tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" - }, "node_modules/tiny-invariant": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz", @@ -35403,16 +35369,6 @@ "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", "dev": true }, - "clipboard": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.10.tgz", - "integrity": "sha512-cz3m2YVwFz95qSEbCDi2fzLN/epEN9zXBvfgAoGkvGOJZATMl9gtTDVOtBYkx2ODUJl2kvmud7n32sV2BpYR4g==", - "requires": { - "good-listener": "^1.2.2", - "select": "^1.1.2", - "tiny-emitter": "^2.0.0" - } - }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -36439,11 +36395,6 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, - "delegate": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", - "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" - }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -38789,14 +38740,6 @@ } } }, - "good-listener": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", - "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", - "requires": { - "delegate": "^3.1.2" - } - }, "got": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", @@ -46779,11 +46722,6 @@ } } }, - "select": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", - "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" - }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -48294,11 +48232,6 @@ "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", "dev": true }, - "tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" - }, "tiny-invariant": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz", diff --git a/package.json b/package.json index d2e22e18..f1ad310a 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,6 @@ "@fortawesome/react-fontawesome": "0.1.18", "@redux-devtools/extension": "3.2.2", "classnames": "2.3.1", - "clipboard": "2.0.10", "core-js": "3.21.1", "extract-react-intl-messages": "4.1.1", "fastest-levenshtein": "1.0.12", diff --git a/src/_style.scss b/src/_style.scss index 3d8f766d..f09d0c78 100644 --- a/src/_style.scss +++ b/src/_style.scss @@ -364,10 +364,6 @@ select.form-control { height: 282px; } -.variation1-medium-screen { - height: 300px !important; -} - .medium-screen-svg-light, .medium-screen-svg-primary { fill: $light-200; @@ -503,14 +499,6 @@ select.form-control { height: 240px; } -.variation1-bar-color { - stroke: $brand !important; -} - -.variation2-bar-color { - stroke: $accent-a !important; -} - .medium-screen-svg-line { padding-top: 0.5rem; stroke: $accent-b; @@ -526,21 +514,6 @@ select.form-control { width: 4em; height: 72px; } -.dicount-heading{ - margin-left: 7px; -} - -.hover-text:hover { - color: $black !important; - - .hover-icon { - color: $black !important; - } -} - -.hover-discount-icon:hover { - color: $white !important; -} .large-heading { margin-left: 7px; @@ -703,16 +676,6 @@ select.form-control { font-weight: 400; } -.discount-banner { - background-color: #03C7E8; -} - -.dashed-border { - border-style: dashed; - border-width: thin; - padding: 0.5rem; -} - @media (min-width: 1024px) { .mw-500 { width: 500px; @@ -748,12 +711,6 @@ select.form-control { flex-direction:column; justify-content: center; } - - .dashed-border { - border-style: dashed; - border-width: thin; - padding: 0.25rem 0.5rem; - } } @media (max-width: 1199px) and (min-width: 768px) { @@ -807,12 +764,6 @@ select.form-control { } } -@media (max-width: 550px) { - .variation2-text-alignment { - text-align: left; - } -} - // Smaller than Extra Small (Mobile Screens) @media (max-width: 464px) { .btn-social { diff --git a/src/base-component/BaseComponent.jsx b/src/base-component/BaseComponent.jsx index 507beeb4..bd55c7fb 100644 --- a/src/base-component/BaseComponent.jsx +++ b/src/base-component/BaseComponent.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React from 'react'; import CookiePolicyBanner from '@edx/frontend-component-cookie-policy-banner'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; @@ -11,26 +11,15 @@ import MediaQuery from 'react-responsive'; import AuthExtraLargeLayout from './AuthExtraLargeLayout'; import AuthMediumLayout from './AuthMediumLayout'; import AuthSmallLayout from './AuthSmallLayout'; -import DiscountExperimentBanner from './DiscountBanner'; import LargeLayout from './LargeLayout'; import MediumLayout from './MediumLayout'; import SmallLayout from './SmallLayout'; -const BaseComponent = ({ children, isRegistrationPage, showWelcomeBanner }) => { +const BaseComponent = ({ children, showWelcomeBanner }) => { const authenticatedUser = showWelcomeBanner ? getAuthenticatedUser() : null; - const [optimizelyExperimentName, setOptimizelyExperimentName] = useState(''); - - useEffect(() => { - const { experimentName } = window; - - if (experimentName) { - setOptimizelyExperimentName(experimentName); - } - }); return ( <> - {isRegistrationPage && optimizelyExperimentName === 'variation2' ? : null}
@@ -43,35 +32,35 @@ const BaseComponent = ({ children, isRegistrationPage, showWelcomeBanner }) => {
{authenticatedUser ? : ( - + )}
{authenticatedUser ? : ( - + )}
{authenticatedUser ? : ( - + )}
{authenticatedUser ? : ( - + )} {authenticatedUser ? : ( - + )} {authenticatedUser ? : ( - + )} @@ -84,13 +73,11 @@ const BaseComponent = ({ children, isRegistrationPage, showWelcomeBanner }) => { }; BaseComponent.defaultProps = { - isRegistrationPage: false, showWelcomeBanner: false, }; BaseComponent.propTypes = { children: PropTypes.node.isRequired, - isRegistrationPage: PropTypes.bool, showWelcomeBanner: PropTypes.bool, }; diff --git a/src/base-component/DiscountBanner.jsx b/src/base-component/DiscountBanner.jsx deleted file mode 100644 index f9961f4c..00000000 --- a/src/base-component/DiscountBanner.jsx +++ /dev/null @@ -1,70 +0,0 @@ -import React, { useState } from 'react'; - -import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; -import { PageBanner, Toast } from '@edx/paragon'; -import { faCut } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import ClipboardJS from 'clipboard'; - -import messages from './messages'; - -const DiscountExperimentBanner = (props) => { - const { intl } = props; - const [show, setShow] = useState(true); - const [showToast, setToastShow] = useState(false); - new ClipboardJS('.copyIcon'); // eslint-disable-line no-new - const getDiscountText = () => ( - - 15% - - ); - - return ( - <> - setToastShow(false)} - show={showToast} - > - {intl.formatMessage(messages['code.copied'])} - - { setShow(false); }} - > - - - - - - EDXWELCOME - setToastShow(true)} - /> - - - - - ); -}; - -DiscountExperimentBanner.propTypes = { - intl: intlShape.isRequired, - -}; -export default injectIntl(DiscountExperimentBanner); diff --git a/src/base-component/LargeLayout.jsx b/src/base-component/LargeLayout.jsx index abd11af6..8ad5e005 100644 --- a/src/base-component/LargeLayout.jsx +++ b/src/base-component/LargeLayout.jsx @@ -2,17 +2,16 @@ import React from 'react'; import { getConfig } from '@edx/frontend-platform'; import { Hyperlink, Image } from '@edx/paragon'; -import PropTypes from 'prop-types'; import LargeScreenLeftLayout from './LargeLeftLayout'; -const LargeLayout = ({ experimentName, isRegistrationPage }) => ( +const LargeLayout = () => (
{getConfig().SITE_NAME} - +
(
); -LargeLayout.defaultProps = { - experimentName: '', - isRegistrationPage: false, -}; - -LargeLayout.propTypes = { - experimentName: PropTypes.string, - isRegistrationPage: PropTypes.bool, -}; - export default LargeLayout; diff --git a/src/base-component/LargeLeftLayout.jsx b/src/base-component/LargeLeftLayout.jsx index e9667b28..29bbee2e 100644 --- a/src/base-component/LargeLeftLayout.jsx +++ b/src/base-component/LargeLeftLayout.jsx @@ -1,73 +1,32 @@ -import React, { useState } from 'react'; +import React from 'react'; import { getConfig } from '@edx/frontend-platform'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; -import { Toast } from '@edx/paragon'; -import { faCut } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import classNames from 'classnames'; -import ClipboardJS from 'clipboard'; -import PropTypes from 'prop-types'; import messages from './messages'; -import SideDiscountBanner from './SideDiscountBanner'; const LargeLeftLayout = (props) => { - const { intl, isRegistrationPage, experimentName } = props; - const [showToast, setToastShow] = useState(false); - new ClipboardJS('.copyIcon'); // eslint-disable-line no-new + const { intl } = props; return (
- setToastShow(false)} - show={showToast} - > - {intl.formatMessage(messages['code.copied'])} - -
-

+
+

{intl.formatMessage(messages['start.learning'])} - +
{intl.formatMessage(messages['with.site.name'], { siteName: getConfig().SITE_NAME })}

- {experimentName === 'variation1' && isRegistrationPage ? ( - - - - - - EDXWELCOME - setToastShow(true)} - /> - - - ) : null}

@@ -76,13 +35,6 @@ const LargeLeftLayout = (props) => { LargeLeftLayout.propTypes = { intl: intlShape.isRequired, - experimentName: PropTypes.string, - isRegistrationPage: PropTypes.bool, -}; - -LargeLeftLayout.defaultProps = { - experimentName: '', - isRegistrationPage: false, }; export default injectIntl(LargeLeftLayout); diff --git a/src/base-component/MediumLayout.jsx b/src/base-component/MediumLayout.jsx index 3e6576b4..8a8ba044 100644 --- a/src/base-component/MediumLayout.jsx +++ b/src/base-component/MediumLayout.jsx @@ -1,36 +1,16 @@ -import React, { useState } from 'react'; +import React from 'react'; import { getConfig } from '@edx/frontend-platform'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; -import { Hyperlink, Image, Toast } from '@edx/paragon'; -import { faCut } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import classNames from 'classnames'; -import ClipboardJS from 'clipboard'; -import PropTypes from 'prop-types'; +import { Hyperlink, Image } from '@edx/paragon'; import messages from './messages'; -import SideDiscountBanner from './SideDiscountBanner'; const MediumLayout = (props) => { - const { intl, isRegistrationPage, experimentName } = props; - const [showToast, setToastShow] = useState(false); - new ClipboardJS('.copyIcon'); // eslint-disable-line no-new + const { intl } = props; return ( -
- setToastShow(false)} - show={showToast} - > - {intl.formatMessage(messages['code.copied'])} - +
{getConfig().SITE_NAME} @@ -40,41 +20,18 @@ const MediumLayout = (props) => { role="img" aria-label="" focusable={false} - className={classNames( - 'medium-screen-svg-line pl-5', - { - 'variation1-bar-color': experimentName === 'variation1' && isRegistrationPage, - 'variation2-bar-color': experimentName === 'variation2' && isRegistrationPage, - }, - )} + className="medium-screen-svg-line pl-5" >

{intl.formatMessage(messages['start.learning'])} - +
{intl.formatMessage(messages['with.site.name'], { siteName: getConfig().SITE_NAME })}

- {experimentName === 'variation1' && isRegistrationPage ? ( -
- - - EDXWELCOME - setToastShow(true)} - /> - -
- ) : null}
@@ -92,13 +49,6 @@ const MediumLayout = (props) => { MediumLayout.propTypes = { intl: intlShape.isRequired, - experimentName: PropTypes.string, - isRegistrationPage: PropTypes.bool, -}; - -MediumLayout.defaultProps = { - experimentName: '', - isRegistrationPage: false, }; export default injectIntl(MediumLayout); diff --git a/src/base-component/SideDiscountBanner.jsx b/src/base-component/SideDiscountBanner.jsx deleted file mode 100644 index 2702d880..00000000 --- a/src/base-component/SideDiscountBanner.jsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; - -import { FormattedMessage } from '@edx/frontend-platform/i18n'; - -export default function SideDiscountBanner() { - const getDiscountText = () => ( - - 15% - - ); - const getCerificateMsg = () => ( - - - - ); - return ( - - , - certificateMsg: getCerificateMsg(), - }} - /> - - ); -} diff --git a/src/base-component/SmallLayout.jsx b/src/base-component/SmallLayout.jsx index b0302fae..60d83671 100644 --- a/src/base-component/SmallLayout.jsx +++ b/src/base-component/SmallLayout.jsx @@ -1,31 +1,17 @@ -import React, { useState } from 'react'; +import React from 'react'; import { getConfig } from '@edx/frontend-platform'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; -import { Hyperlink, Image, Toast } from '@edx/paragon'; -import { faCut } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import classNames from 'classnames'; -import ClipboardJS from 'clipboard'; -import PropTypes from 'prop-types'; +import { Hyperlink, Image } from '@edx/paragon'; import messages from './messages'; -import SideDiscountBanner from './SideDiscountBanner'; const SmallLayout = (props) => { - const { intl, isRegistrationPage, experimentName } = props; - const [showToast, setToastShow] = useState(false); - new ClipboardJS('.copyIcon'); // eslint-disable-line no-new + const { intl } = props; return ( <>
- setToastShow(false)} - show={showToast} - > - {intl.formatMessage(messages['code.copied'])} - {getConfig().SITE_NAME} @@ -34,13 +20,7 @@ const SmallLayout = (props) => { role="img" aria-label="" focusable={false} - className={classNames( - 'small-screen-svg-line', - { - 'variation1-bar-color': experimentName === 'variation1' && isRegistrationPage, - 'variation2-bar-color': experimentName === 'variation2' && isRegistrationPage, - }, - )} + className="small-screen-svg-line" > @@ -48,27 +28,10 @@ const SmallLayout = (props) => {

{intl.formatMessage(messages['start.learning'])}
- + {intl.formatMessage(messages['with.site.name'], { siteName: getConfig().SITE_NAME })}

- {(experimentName === 'variation1' && isRegistrationPage) ? ( -
- - - EDXWELCOME - setToastShow(true)} - /> - -
- ) : null}
@@ -78,14 +41,6 @@ const SmallLayout = (props) => { SmallLayout.propTypes = { intl: intlShape.isRequired, - experimentName: PropTypes.string, - isRegistrationPage: PropTypes.bool, -}; - -SmallLayout.defaultProps = { - experimentName: '', - isRegistrationPage: false, - }; export default injectIntl(SmallLayout); diff --git a/src/base-component/messages.jsx b/src/base-component/messages.jsx index 13191f8a..96d61392 100644 --- a/src/base-component/messages.jsx +++ b/src/base-component/messages.jsx @@ -11,11 +11,6 @@ const messages = defineMessages({ defaultMessage: 'with {siteName}', description: 'Header text with site name for logistration MFE pages', }, - 'code.copied': { - id: 'code.copied', - defaultMessage: 'Code copied', - description: 'part of 15% discount code copied', - }, // authenticated user base component text 'complete.your.profile.1': { id: 'complete.your.profile.1', diff --git a/src/common-components/Logistration.jsx b/src/common-components/Logistration.jsx index 033af66a..93aba74a 100644 --- a/src/common-components/Logistration.jsx +++ b/src/common-components/Logistration.jsx @@ -61,7 +61,7 @@ const Logistration = (props) => { ); return ( - +
{institutionLogin ? ( diff --git a/src/register/RegistrationPage.jsx b/src/register/RegistrationPage.jsx index 5d926ced..ccdf193d 100644 --- a/src/register/RegistrationPage.jsx +++ b/src/register/RegistrationPage.jsx @@ -85,7 +85,6 @@ class RegistrationPage extends React.Component { failureCount: 0, startTime: Date.now(), totalRegistrationTime: 0, - optimizelyExperimentName: '', readOnly: true, validatePassword: false, values: {}, @@ -111,7 +110,6 @@ class RegistrationPage extends React.Component { payload.is_registered = this.isRegistered; this.props.resetRegistrationForm(); this.props.getThirdPartyAuthContext(payload); - this.getExperiments(); } shouldComponentUpdate(nextProps) { @@ -178,14 +176,6 @@ class RegistrationPage extends React.Component { return true; } - getExperiments = () => { - const { experimentName } = window; - - if (experimentName) { - this.setState({ optimizelyExperimentName: experimentName }); - } - }; - onChangeHandler = (e) => { const { name, value, checked } = e.target; const { errors, values } = this.state; @@ -795,13 +785,6 @@ class RegistrationPage extends React.Component { thirdPartyAuthApiStatus, intl)} - {(this.state.optimizelyExperimentName === 'variation1' || this.state.optimizelyExperimentName === 'variation2') - ? ( -
- {intl.formatMessage(messages['certificate.msg'])} -
- ) - : null}
); diff --git a/src/register/messages.jsx b/src/register/messages.jsx index 63b3c1eb..3fcb5c08 100644 --- a/src/register/messages.jsx +++ b/src/register/messages.jsx @@ -300,11 +300,6 @@ const messages = defineMessages({ defaultMessage: 'Did you mean', description: 'Did you mean alert suggestion', }, - 'certificate.msg': { - id: 'certificate.msg', - defaultMessage: '*Offer not eligible for GTx’s Analytics: Essential Tools and Methods MicroMasters Program, ColumbiaX’s Corporate Finance Professional Certificate Program, or courses or programs offered by Wharton, and NYIF.', - description: 'Text for the 15% discount experiment', - }, }); export default messages;