From dc00eb85553b709914b86b16c4bb0b6fbe637244 Mon Sep 17 00:00:00 2001 From: Bouncey Date: Mon, 3 Sep 2018 11:23:18 +0100 Subject: [PATCH] chore(root): Ensure development environment --- .gitignore | 2 + CONTRIBUTING.md | 4 +- api-server/.babelrc.js | 21 +- api-server/.gitignore | 1 + api-server/common/app/App.jsx | 86 - api-server/common/app/Child-Container.jsx | 25 - api-server/common/app/Flash/Flash.jsx | 43 - api-server/common/app/Flash/flash.less | 20 - api-server/common/app/Flash/index.js | 1 - api-server/common/app/Flash/ns.json | 1 - .../app/Flash/redux/get-messages-epic.js | 17 - api-server/common/app/Flash/redux/index.js | 58 - api-server/common/app/Flash/redux/utils.js | 43 - .../common/app/Map/redux/fetch-map-ui-epic.js | 38 - api-server/common/app/Map/redux/index.js | 121 - api-server/common/app/Map/redux/utils.js | 231 -- api-server/common/app/Map/redux/utils.test.js | 319 -- api-server/common/app/Nav/LargeNav.jsx | 38 - api-server/common/app/Nav/MediumNav.jsx | 42 - api-server/common/app/Nav/Nav.jsx | 60 - api-server/common/app/Nav/SmallNav.jsx | 43 - .../common/app/Nav/components/Bin-Button.jsx | 22 - .../common/app/Nav/components/BinButtons.jsx | 35 - .../common/app/Nav/components/NavLinks.jsx | 151 - .../common/app/Nav/components/NavLogo.jsx | 45 - .../common/app/Nav/components/Sign-Up.jsx | 50 - api-server/common/app/Nav/components/index.js | 3 - api-server/common/app/Nav/index.js | 1 - api-server/common/app/Nav/links.json | 15 - api-server/common/app/Nav/nav.less | 345 -- api-server/common/app/Nav/navPropTypes.js | 5 - api-server/common/app/Nav/ns.json | 1 - api-server/common/app/Nav/redux/index.js | 76 - api-server/common/app/NotFound/Not-Found.jsx | 31 - api-server/common/app/NotFound/index.js | 1 - api-server/common/app/Router/Link.jsx | 85 - api-server/common/app/Router/handle-press.js | 50 - api-server/common/app/Router/index.js | 1 - api-server/common/app/Router/redux/index.js | 8 - api-server/common/app/Router/to-url.js | 41 - api-server/common/app/Toasts/Toasts.jsx | 91 - api-server/common/app/Toasts/index.js | 1 - api-server/common/app/Toasts/ns.json | 1 - api-server/common/app/Toasts/redux/index.js | 47 - api-server/common/app/analytics/index.js | 34 - api-server/common/app/app.less | 26 - api-server/common/app/create-app.jsx | 104 - api-server/common/app/entities/index.js | 331 -- api-server/common/app/epics.js | 13 - api-server/common/app/index.js | 3 - api-server/common/app/index.less | 4 - api-server/common/app/ns.json | 1 - api-server/common/app/provide-store.js | 11 - api-server/common/app/reducer.js | 23 - .../common/app/redux/fetch-user-epic.js | 42 - api-server/common/app/redux/index.js | 220 -- .../common/app/redux/night-mode-epic.js | 64 - api-server/common/app/redux/utils.js | 9 - api-server/common/app/routes-map.js | 17 - api-server/common/app/routes/Admin/README.md | 1 - .../common/app/routes/Profile/Profile.jsx | 267 -- .../routes/Profile/components/CamperHOC.jsx | 77 - .../Profile/components/Certificates.jsx | 183 - .../app/routes/Profile/components/HeatMap.jsx | 125 - .../routes/Profile/components/Portfolio.jsx | 74 - .../routes/Profile/components/SocialIcons.jsx | 143 - .../routes/Profile/components/Timeline.jsx | 173 - api-server/common/app/routes/Profile/index.js | 7 - api-server/common/app/routes/Profile/ns.json | 1 - .../common/app/routes/Profile/profile.less | 102 - .../common/app/routes/Profile/redux/index.js | 31 - .../common/app/routes/Settings/Settings.jsx | 122 - .../app/routes/Settings/ShowSettings.jsx | 41 - .../Settings/Toggle-Button/Toggle-Button.jsx | 55 - .../routes/Settings/Toggle-Button/index.js | 1 - .../app/routes/Settings/Toggle-Button/ns.json | 1 - .../routes/Settings/Toggle-Button/toggle.less | 21 - .../Settings/components/About-Settings.jsx | 217 -- .../app/routes/Settings/components/Camper.jsx | 92 - .../Settings/components/Cert-Settings.jsx | 336 -- .../routes/Settings/components/DangerZone.jsx | 105 - .../Settings/components/DeleteModal.jsx | 71 - .../Settings/components/Email-Settings.jsx | 170 - .../routes/Settings/components/EmailForm.jsx | 170 - .../routes/Settings/components/Honesty.jsx | 95 - .../Settings/components/Internet-Settings.jsx | 113 - .../Settings/components/JSAlgoAndDSForm.jsx | 134 - .../components/Portfolio-Settings.jsx | 178 - .../Settings/components/Privacy-Settings.jsx | 177 - .../routes/Settings/components/ResetModal.jsx | 65 - .../Settings/components/SectionHeader.jsx | 25 - .../Settings/components/SolutionViewer.jsx | 80 - .../Settings/components/ThemeSettings.jsx | 38 - .../Settings/components/ToggleSetting.jsx | 55 - .../Settings/components/UsernameSettings.jsx | 187 - .../Settings/formHelpers/BlockSaveButton.jsx | 24 - .../Settings/formHelpers/BlockSaveWrapper.jsx | 23 - .../app/routes/Settings/formHelpers/Form.jsx | 87 - .../Settings/formHelpers/FormFields.jsx | 100 - .../app/routes/Settings/formHelpers/index.js | 35 - .../common/app/routes/Settings/index.js | 8 - .../common/app/routes/Settings/index.less | 2 - api-server/common/app/routes/Settings/ns.json | 1 - .../routes/Settings/redux/certificate-epic.js | 42 - .../routes/Settings/redux/danger-zone-epic.js | 53 - .../common/app/routes/Settings/redux/index.js | 175 - .../Settings/redux/new-username-epic.js | 30 - .../routes/Settings/redux/update-user-epic.js | 230 -- .../routes/update-email/Update-Email.jsx | 172 - .../Settings/routes/update-email/index.js | 1 - .../common/app/routes/Settings/settings.less | 144 - .../Settings/utils/buildUserProjectsMap.js | 32 - api-server/common/app/routes/index.js | 8 - api-server/common/app/routes/index.less | 2 - api-server/common/app/routes/redux.js | 33 - .../common/app/utils/No-Props-Passthrough.jsx | 12 - api-server/common/app/utils/blockNameify.js | 41 - api-server/common/app/utils/challengeTypes.js | 12 - api-server/common/app/utils/create-types.js | 10 - api-server/common/app/utils/form.js | 94 - api-server/common/app/utils/shallow-equals.js | 37 - .../common/{app => }/utils/empty-protector.js | 0 api-server/common/utils/map.js | 2 +- api-server/common/utils/polyvinyl.js | 2 +- api-server/development-entry.js | 13 + api-server/package-lock.json | 85 +- api-server/package.json | 10 +- api-server/server/boot/a-services.js | 7 - api-server/server/server.js | 6 +- .../utils/cast-to-observable.js | 0 .../components/Header/components/SignedIn.js | 2 +- client/src/components/Header/index.js | 6 +- client/src/components/Layout.js | 4 - client/src/pages/accept-privacy-terms.js | 12 +- client/src/pages/update-email.js | 2 +- client/src/utils/ajax.js | 6 +- develop-client-server.js | 34 + development-server.js | 27 - old-client/client/README.md | 4 - old-client/client/epics/README.md | 0 old-client/client/epics/analytics-epic.js | 44 - old-client/client/epics/err-epic.js | 10 - old-client/client/epics/hard-go-to-epic.js | 14 - old-client/client/epics/index.js | 13 - old-client/client/epics/mouse-trap-epic.js | 52 - old-client/client/epics/title-epic.js | 10 - old-client/client/index.js | 74 - old-client/client/less/flexgrid.less | 152 - old-client/client/less/lib/animate.less | 3338 ----------------- .../bootstrap-social/bootstrap-social.less | 99 - .../client/less/lib/bootstrap/alerts.less | 68 - .../client/less/lib/bootstrap/badges.less | 55 - .../client/less/lib/bootstrap/bootstrap.less | 49 - .../less/lib/bootstrap/breadcrumbs.less | 26 - .../less/lib/bootstrap/button-groups.less | 247 -- .../client/less/lib/bootstrap/buttons.less | 160 - .../client/less/lib/bootstrap/carousel.less | 267 -- .../client/less/lib/bootstrap/close.less | 33 - .../client/less/lib/bootstrap/code.less | 69 - .../lib/bootstrap/component-animations.less | 34 - .../client/less/lib/bootstrap/dropdowns.less | 214 -- .../client/less/lib/bootstrap/forms.less | 559 --- .../client/less/lib/bootstrap/glyphicons.less | 234 -- .../client/less/lib/bootstrap/grid.less | 84 - .../less/lib/bootstrap/input-groups.less | 166 - .../client/less/lib/bootstrap/jumbotron.less | 48 - .../client/less/lib/bootstrap/labels.less | 64 - .../client/less/lib/bootstrap/list-group.less | 132 - .../client/less/lib/bootstrap/media.less | 47 - .../client/less/lib/bootstrap/mixins.less | 39 - .../less/lib/bootstrap/mixins/alerts.less | 14 - .../bootstrap/mixins/background-variant.less | 8 - .../lib/bootstrap/mixins/border-radius.less | 18 - .../less/lib/bootstrap/mixins/buttons.less | 52 - .../lib/bootstrap/mixins/center-block.less | 7 - .../less/lib/bootstrap/mixins/clearfix.less | 22 - .../less/lib/bootstrap/mixins/forms.less | 85 - .../less/lib/bootstrap/mixins/gradients.less | 59 - .../lib/bootstrap/mixins/grid-framework.less | 91 - .../less/lib/bootstrap/mixins/grid.less | 122 - .../less/lib/bootstrap/mixins/hide-text.less | 21 - .../less/lib/bootstrap/mixins/image.less | 33 - .../less/lib/bootstrap/mixins/labels.less | 12 - .../less/lib/bootstrap/mixins/list-group.less | 29 - .../lib/bootstrap/mixins/nav-divider.less | 10 - .../bootstrap/mixins/nav-vertical-align.less | 9 - .../less/lib/bootstrap/mixins/opacity.less | 8 - .../less/lib/bootstrap/mixins/pagination.less | 23 - .../less/lib/bootstrap/mixins/panels.less | 24 - .../lib/bootstrap/mixins/progress-bar.less | 10 - .../lib/bootstrap/mixins/reset-filter.less | 8 - .../less/lib/bootstrap/mixins/resize.less | 6 - .../mixins/responsive-visibility.less | 15 - .../less/lib/bootstrap/mixins/size.less | 10 - .../less/lib/bootstrap/mixins/tab-focus.less | 9 - .../less/lib/bootstrap/mixins/table-row.less | 28 - .../lib/bootstrap/mixins/text-emphasis.less | 8 - .../lib/bootstrap/mixins/text-overflow.less | 8 - .../lib/bootstrap/mixins/vendor-prefixes.less | 227 -- .../client/less/lib/bootstrap/modals.less | 146 - .../client/less/lib/bootstrap/navbar.less | 659 ---- .../client/less/lib/bootstrap/navs.less | 244 -- .../client/less/lib/bootstrap/normalize.less | 427 --- .../client/less/lib/bootstrap/pager.less | 54 - .../client/less/lib/bootstrap/pagination.less | 88 - .../client/less/lib/bootstrap/panels.less | 261 -- .../client/less/lib/bootstrap/popovers.less | 134 - .../client/less/lib/bootstrap/print.less | 107 - .../less/lib/bootstrap/progress-bars.less | 87 - .../lib/bootstrap/responsive-utilities.less | 194 - .../less/lib/bootstrap/scaffolding.less | 150 - .../client/less/lib/bootstrap/tables.less | 234 -- .../client/less/lib/bootstrap/theme.less | 260 -- .../client/less/lib/bootstrap/thumbnails.less | 36 - .../client/less/lib/bootstrap/tooltip.less | 95 - .../client/less/lib/bootstrap/type.less | 302 -- .../client/less/lib/bootstrap/utilities.less | 56 - .../client/less/lib/bootstrap/variables.less | 874 ----- .../client/less/lib/bootstrap/wells.less | 29 - old-client/client/less/main.less | 1075 ------ old-client/client/less/toastr.less | 16 - old-client/client/less/userHome.less | 7 - old-client/client/utils/flash-to-toast.js | 16 - .../client/utils/flash-to-toast.test.js | 32 - .../client/utils/send-page-analytics.js | 6 - 225 files changed, 164 insertions(+), 21387 deletions(-) delete mode 100644 api-server/common/app/App.jsx delete mode 100644 api-server/common/app/Child-Container.jsx delete mode 100644 api-server/common/app/Flash/Flash.jsx delete mode 100644 api-server/common/app/Flash/flash.less delete mode 100644 api-server/common/app/Flash/index.js delete mode 100644 api-server/common/app/Flash/ns.json delete mode 100644 api-server/common/app/Flash/redux/get-messages-epic.js delete mode 100644 api-server/common/app/Flash/redux/index.js delete mode 100644 api-server/common/app/Flash/redux/utils.js delete mode 100644 api-server/common/app/Map/redux/fetch-map-ui-epic.js delete mode 100644 api-server/common/app/Map/redux/index.js delete mode 100644 api-server/common/app/Map/redux/utils.js delete mode 100644 api-server/common/app/Map/redux/utils.test.js delete mode 100644 api-server/common/app/Nav/LargeNav.jsx delete mode 100644 api-server/common/app/Nav/MediumNav.jsx delete mode 100644 api-server/common/app/Nav/Nav.jsx delete mode 100644 api-server/common/app/Nav/SmallNav.jsx delete mode 100644 api-server/common/app/Nav/components/Bin-Button.jsx delete mode 100644 api-server/common/app/Nav/components/BinButtons.jsx delete mode 100644 api-server/common/app/Nav/components/NavLinks.jsx delete mode 100644 api-server/common/app/Nav/components/NavLogo.jsx delete mode 100644 api-server/common/app/Nav/components/Sign-Up.jsx delete mode 100644 api-server/common/app/Nav/components/index.js delete mode 100644 api-server/common/app/Nav/index.js delete mode 100644 api-server/common/app/Nav/links.json delete mode 100644 api-server/common/app/Nav/nav.less delete mode 100644 api-server/common/app/Nav/navPropTypes.js delete mode 100644 api-server/common/app/Nav/ns.json delete mode 100644 api-server/common/app/Nav/redux/index.js delete mode 100644 api-server/common/app/NotFound/Not-Found.jsx delete mode 100644 api-server/common/app/NotFound/index.js delete mode 100644 api-server/common/app/Router/Link.jsx delete mode 100644 api-server/common/app/Router/handle-press.js delete mode 100644 api-server/common/app/Router/index.js delete mode 100644 api-server/common/app/Router/redux/index.js delete mode 100644 api-server/common/app/Router/to-url.js delete mode 100644 api-server/common/app/Toasts/Toasts.jsx delete mode 100644 api-server/common/app/Toasts/index.js delete mode 100644 api-server/common/app/Toasts/ns.json delete mode 100644 api-server/common/app/Toasts/redux/index.js delete mode 100644 api-server/common/app/analytics/index.js delete mode 100644 api-server/common/app/app.less delete mode 100644 api-server/common/app/create-app.jsx delete mode 100644 api-server/common/app/entities/index.js delete mode 100644 api-server/common/app/epics.js delete mode 100644 api-server/common/app/index.js delete mode 100644 api-server/common/app/index.less delete mode 100644 api-server/common/app/ns.json delete mode 100644 api-server/common/app/provide-store.js delete mode 100644 api-server/common/app/reducer.js delete mode 100644 api-server/common/app/redux/fetch-user-epic.js delete mode 100644 api-server/common/app/redux/index.js delete mode 100644 api-server/common/app/redux/night-mode-epic.js delete mode 100644 api-server/common/app/redux/utils.js delete mode 100644 api-server/common/app/routes-map.js delete mode 100644 api-server/common/app/routes/Admin/README.md delete mode 100644 api-server/common/app/routes/Profile/Profile.jsx delete mode 100644 api-server/common/app/routes/Profile/components/CamperHOC.jsx delete mode 100644 api-server/common/app/routes/Profile/components/Certificates.jsx delete mode 100644 api-server/common/app/routes/Profile/components/HeatMap.jsx delete mode 100644 api-server/common/app/routes/Profile/components/Portfolio.jsx delete mode 100644 api-server/common/app/routes/Profile/components/SocialIcons.jsx delete mode 100644 api-server/common/app/routes/Profile/components/Timeline.jsx delete mode 100644 api-server/common/app/routes/Profile/index.js delete mode 100644 api-server/common/app/routes/Profile/ns.json delete mode 100644 api-server/common/app/routes/Profile/profile.less delete mode 100644 api-server/common/app/routes/Profile/redux/index.js delete mode 100644 api-server/common/app/routes/Settings/Settings.jsx delete mode 100644 api-server/common/app/routes/Settings/ShowSettings.jsx delete mode 100644 api-server/common/app/routes/Settings/Toggle-Button/Toggle-Button.jsx delete mode 100644 api-server/common/app/routes/Settings/Toggle-Button/index.js delete mode 100644 api-server/common/app/routes/Settings/Toggle-Button/ns.json delete mode 100644 api-server/common/app/routes/Settings/Toggle-Button/toggle.less delete mode 100644 api-server/common/app/routes/Settings/components/About-Settings.jsx delete mode 100644 api-server/common/app/routes/Settings/components/Camper.jsx delete mode 100644 api-server/common/app/routes/Settings/components/Cert-Settings.jsx delete mode 100644 api-server/common/app/routes/Settings/components/DangerZone.jsx delete mode 100644 api-server/common/app/routes/Settings/components/DeleteModal.jsx delete mode 100644 api-server/common/app/routes/Settings/components/Email-Settings.jsx delete mode 100644 api-server/common/app/routes/Settings/components/EmailForm.jsx delete mode 100644 api-server/common/app/routes/Settings/components/Honesty.jsx delete mode 100644 api-server/common/app/routes/Settings/components/Internet-Settings.jsx delete mode 100644 api-server/common/app/routes/Settings/components/JSAlgoAndDSForm.jsx delete mode 100644 api-server/common/app/routes/Settings/components/Portfolio-Settings.jsx delete mode 100644 api-server/common/app/routes/Settings/components/Privacy-Settings.jsx delete mode 100644 api-server/common/app/routes/Settings/components/ResetModal.jsx delete mode 100644 api-server/common/app/routes/Settings/components/SectionHeader.jsx delete mode 100644 api-server/common/app/routes/Settings/components/SolutionViewer.jsx delete mode 100644 api-server/common/app/routes/Settings/components/ThemeSettings.jsx delete mode 100644 api-server/common/app/routes/Settings/components/ToggleSetting.jsx delete mode 100644 api-server/common/app/routes/Settings/components/UsernameSettings.jsx delete mode 100644 api-server/common/app/routes/Settings/formHelpers/BlockSaveButton.jsx delete mode 100644 api-server/common/app/routes/Settings/formHelpers/BlockSaveWrapper.jsx delete mode 100644 api-server/common/app/routes/Settings/formHelpers/Form.jsx delete mode 100644 api-server/common/app/routes/Settings/formHelpers/FormFields.jsx delete mode 100644 api-server/common/app/routes/Settings/formHelpers/index.js delete mode 100644 api-server/common/app/routes/Settings/index.js delete mode 100644 api-server/common/app/routes/Settings/index.less delete mode 100644 api-server/common/app/routes/Settings/ns.json delete mode 100644 api-server/common/app/routes/Settings/redux/certificate-epic.js delete mode 100644 api-server/common/app/routes/Settings/redux/danger-zone-epic.js delete mode 100644 api-server/common/app/routes/Settings/redux/index.js delete mode 100644 api-server/common/app/routes/Settings/redux/new-username-epic.js delete mode 100644 api-server/common/app/routes/Settings/redux/update-user-epic.js delete mode 100644 api-server/common/app/routes/Settings/routes/update-email/Update-Email.jsx delete mode 100644 api-server/common/app/routes/Settings/routes/update-email/index.js delete mode 100644 api-server/common/app/routes/Settings/settings.less delete mode 100644 api-server/common/app/routes/Settings/utils/buildUserProjectsMap.js delete mode 100644 api-server/common/app/routes/index.js delete mode 100644 api-server/common/app/routes/index.less delete mode 100644 api-server/common/app/routes/redux.js delete mode 100644 api-server/common/app/utils/No-Props-Passthrough.jsx delete mode 100644 api-server/common/app/utils/blockNameify.js delete mode 100644 api-server/common/app/utils/challengeTypes.js delete mode 100644 api-server/common/app/utils/create-types.js delete mode 100644 api-server/common/app/utils/form.js delete mode 100644 api-server/common/app/utils/shallow-equals.js rename api-server/common/{app => }/utils/empty-protector.js (100%) create mode 100644 api-server/development-entry.js rename api-server/{common/app => server}/utils/cast-to-observable.js (100%) create mode 100644 develop-client-server.js delete mode 100644 development-server.js delete mode 100644 old-client/client/README.md delete mode 100644 old-client/client/epics/README.md delete mode 100644 old-client/client/epics/analytics-epic.js delete mode 100644 old-client/client/epics/err-epic.js delete mode 100644 old-client/client/epics/hard-go-to-epic.js delete mode 100644 old-client/client/epics/index.js delete mode 100644 old-client/client/epics/mouse-trap-epic.js delete mode 100644 old-client/client/epics/title-epic.js delete mode 100644 old-client/client/index.js delete mode 100644 old-client/client/less/flexgrid.less delete mode 100644 old-client/client/less/lib/animate.less delete mode 100755 old-client/client/less/lib/bootstrap-social/bootstrap-social.less delete mode 100755 old-client/client/less/lib/bootstrap/alerts.less delete mode 100755 old-client/client/less/lib/bootstrap/badges.less delete mode 100755 old-client/client/less/lib/bootstrap/bootstrap.less delete mode 100755 old-client/client/less/lib/bootstrap/breadcrumbs.less delete mode 100755 old-client/client/less/lib/bootstrap/button-groups.less delete mode 100755 old-client/client/less/lib/bootstrap/buttons.less delete mode 100755 old-client/client/less/lib/bootstrap/carousel.less delete mode 100755 old-client/client/less/lib/bootstrap/close.less delete mode 100755 old-client/client/less/lib/bootstrap/code.less delete mode 100755 old-client/client/less/lib/bootstrap/component-animations.less delete mode 100755 old-client/client/less/lib/bootstrap/dropdowns.less delete mode 100755 old-client/client/less/lib/bootstrap/forms.less delete mode 100755 old-client/client/less/lib/bootstrap/glyphicons.less delete mode 100755 old-client/client/less/lib/bootstrap/grid.less delete mode 100755 old-client/client/less/lib/bootstrap/input-groups.less delete mode 100755 old-client/client/less/lib/bootstrap/jumbotron.less delete mode 100755 old-client/client/less/lib/bootstrap/labels.less delete mode 100755 old-client/client/less/lib/bootstrap/list-group.less delete mode 100755 old-client/client/less/lib/bootstrap/media.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/alerts.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/background-variant.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/border-radius.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/buttons.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/center-block.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/clearfix.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/forms.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/gradients.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/grid-framework.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/grid.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/hide-text.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/image.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/labels.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/list-group.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/nav-divider.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/nav-vertical-align.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/opacity.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/pagination.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/panels.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/progress-bar.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/reset-filter.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/resize.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/responsive-visibility.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/size.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/tab-focus.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/table-row.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/text-emphasis.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/text-overflow.less delete mode 100755 old-client/client/less/lib/bootstrap/mixins/vendor-prefixes.less delete mode 100755 old-client/client/less/lib/bootstrap/modals.less delete mode 100755 old-client/client/less/lib/bootstrap/navbar.less delete mode 100755 old-client/client/less/lib/bootstrap/navs.less delete mode 100755 old-client/client/less/lib/bootstrap/normalize.less delete mode 100755 old-client/client/less/lib/bootstrap/pager.less delete mode 100755 old-client/client/less/lib/bootstrap/pagination.less delete mode 100755 old-client/client/less/lib/bootstrap/panels.less delete mode 100755 old-client/client/less/lib/bootstrap/popovers.less delete mode 100755 old-client/client/less/lib/bootstrap/print.less delete mode 100755 old-client/client/less/lib/bootstrap/progress-bars.less delete mode 100755 old-client/client/less/lib/bootstrap/responsive-utilities.less delete mode 100755 old-client/client/less/lib/bootstrap/scaffolding.less delete mode 100755 old-client/client/less/lib/bootstrap/tables.less delete mode 100755 old-client/client/less/lib/bootstrap/theme.less delete mode 100755 old-client/client/less/lib/bootstrap/thumbnails.less delete mode 100755 old-client/client/less/lib/bootstrap/tooltip.less delete mode 100755 old-client/client/less/lib/bootstrap/type.less delete mode 100755 old-client/client/less/lib/bootstrap/utilities.less delete mode 100755 old-client/client/less/lib/bootstrap/variables.less delete mode 100755 old-client/client/less/lib/bootstrap/wells.less delete mode 100644 old-client/client/less/main.less delete mode 100644 old-client/client/less/toastr.less delete mode 100644 old-client/client/less/userHome.less delete mode 100644 old-client/client/utils/flash-to-toast.js delete mode 100644 old-client/client/utils/flash-to-toast.test.js delete mode 100644 old-client/client/utils/send-page-analytics.js diff --git a/.gitignore b/.gitignore index c6f492a0fd..6c147de76b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ node_modules +.env + *lib-cov *~ *.seed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 12f5673aa5..1219610aab 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -310,13 +310,13 @@ mongod npm run only-once # Start the application without a backend server -npm run develop +cd ./client && npm run develop # If you require the backend server to be operational (persisted user interations/api calls) # Use this command instead # Note: This command requires that you have a correctly seeded mongodb instance running # Note: If you are runnoing the backend server inside a docker container, use the command above -npm run develop-server +node develop-client-server.js ``` diff --git a/api-server/.babelrc.js b/api-server/.babelrc.js index d5b359996f..558ac8759b 100644 --- a/api-server/.babelrc.js +++ b/api-server/.babelrc.js @@ -1,12 +1,17 @@ module.exports = { - plugins: [require.resolve('babel-plugin-transform-function-bind')], + plugins: [ + require.resolve('babel-plugin-transform-function-bind'), + require.resolve('@babel/plugin-proposal-class-properties'), + require.resolve('@babel/plugin-proposal-object-rest-spread') + ], presets: [ [ - require.resolve('@babel/preset-env'), { + require.resolve('@babel/preset-env'), + { targets: { - node: 'current' - } - } - ] - ] -} \ No newline at end of file + node: '8', + }, + }, + ], + ], +} diff --git a/api-server/.gitignore b/api-server/.gitignore index 9ea7742cbf..1b0e0e8be7 100644 --- a/api-server/.gitignore +++ b/api-server/.gitignore @@ -22,6 +22,7 @@ tmp npm-debug.log node_modules +compiled .idea *.iml .DS_Store diff --git a/api-server/common/app/App.jsx b/api-server/common/app/App.jsx deleted file mode 100644 index 7d32cdc6e6..0000000000 --- a/api-server/common/app/App.jsx +++ /dev/null @@ -1,86 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; - -import ns from './ns.json'; -import { - appMounted, - fetchUser, - - isSignedInSelector -} from './redux'; - -import { fetchMapUi } from './Map/redux'; - -import Flash from './Flash'; -import Nav from './Nav'; -import Toasts from './Toasts'; -import NotFound from './NotFound'; -import { mainRouteSelector } from './routes/redux'; -import Profile from './routes/Profile'; -import Settings from './routes/Settings'; - -const mapDispatchToProps = { - appMounted, - fetchMapUi, - fetchUser -}; - -const mapStateToProps = state => { - const isSignedIn = isSignedInSelector(state); - const route = mainRouteSelector(state); - return { - toast: state.app.toast, - isSignedIn, - route - }; -}; - -const propTypes = { - appMounted: PropTypes.func.isRequired, - children: PropTypes.node, - fetchMapUi: PropTypes.func.isRequired, - fetchUser: PropTypes.func, - isSignedIn: PropTypes.bool, - route: PropTypes.string, - toast: PropTypes.object -}; - -const routes = { - profile: Profile, - settings: Settings -}; - -// export plain class for testing -export class FreeCodeCamp extends React.Component { - componentDidMount() { - this.props.appMounted(); - this.props.fetchMapUi(); - if (!this.props.isSignedIn) { - this.props.fetchUser(); - } - } - - render() { - const { - route - } = this.props; - const Child = routes[route] || NotFound; - return ( -
- -
- ); - } -} - -FreeCodeCamp.displayName = 'freeCodeCamp'; -FreeCodeCamp.propTypes = propTypes; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(FreeCodeCamp); diff --git a/api-server/common/app/Child-Container.jsx b/api-server/common/app/Child-Container.jsx deleted file mode 100644 index 068b8170d1..0000000000 --- a/api-server/common/app/Child-Container.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; - -import ns from './ns.json'; - -const propTypes = { - children: PropTypes.node, - isFullWidth: PropTypes.bool -}; - -export default function ChildContainer({ children, isFullWidth }) { - const contentClassname = classnames({ - [`${ns}-content`]: true, - [`${ns}-centered`]: !isFullWidth - }); - return ( -
- { children } -
- ); -} - -ChildContainer.displayName = 'ChildContainer'; -ChildContainer.propTypes = propTypes; diff --git a/api-server/common/app/Flash/Flash.jsx b/api-server/common/app/Flash/Flash.jsx deleted file mode 100644 index 0e9f4b5ae0..0000000000 --- a/api-server/common/app/Flash/Flash.jsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { CloseButton } from 'react-bootstrap'; -import { connect } from 'react-redux'; - -import ns from './ns.json'; -import { alertTypes } from '../../utils/flash.js'; -import { - latestMessageSelector, - clickOnClose -} from './redux'; - -const propTypes = { - clickOnClose: PropTypes.func.isRequired, - message: PropTypes.string, - type: PropTypes.oneOf(Object.keys(alertTypes)) -}; -const mapStateToProps = latestMessageSelector; -const mapDispatchToProps = { clickOnClose }; - -export function Flash({ type, clickOnClose, message }) { - if (!message) { - return null; - } - return ( -
-
-

- { message } -

- -
-
- ); -} - -Flash.displayName = 'Flash'; -Flash.propTypes = propTypes; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(Flash); diff --git a/api-server/common/app/Flash/flash.less b/api-server/common/app/Flash/flash.less deleted file mode 100644 index edf583f1d4..0000000000 --- a/api-server/common/app/Flash/flash.less +++ /dev/null @@ -1,20 +0,0 @@ -@ns: flash; - -.@{ns}-container { - width: 100%; -} - -.@{ns}-content { - .center(@value: @container-lg, @padding: @grid-gutter-width); - .row(@justify: around); -} - -.@{ns}-message { - flex: 1 0 0px; - color: #37474f; -} - -#@{ns}-board { - margin-top: 50px; -} - diff --git a/api-server/common/app/Flash/index.js b/api-server/common/app/Flash/index.js deleted file mode 100644 index 5ad9a0c573..0000000000 --- a/api-server/common/app/Flash/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './Flash.jsx'; diff --git a/api-server/common/app/Flash/ns.json b/api-server/common/app/Flash/ns.json deleted file mode 100644 index 27f9bb8bd4..0000000000 --- a/api-server/common/app/Flash/ns.json +++ /dev/null @@ -1 +0,0 @@ -"flash" diff --git a/api-server/common/app/Flash/redux/get-messages-epic.js b/api-server/common/app/Flash/redux/get-messages-epic.js deleted file mode 100644 index 9e468a8abc..0000000000 --- a/api-server/common/app/Flash/redux/get-messages-epic.js +++ /dev/null @@ -1,17 +0,0 @@ -import { Observable } from 'rx'; -import { ofType } from 'redux-epic'; - -import { - fetchMessagesComplete, - fetchMessagesError -} from './'; -import { types as app } from '../../redux'; -import { getJSON$ } from '../../../utils/ajax-stream.js'; - -export default function getMessagesEpic(actions) { - return actions::ofType(app.appMounted) - .flatMap(() => getJSON$('/api/users/get-messages') - .map(fetchMessagesComplete) - .catch(err => Observable.of(fetchMessagesError(err))) - ); -} diff --git a/api-server/common/app/Flash/redux/index.js b/api-server/common/app/Flash/redux/index.js deleted file mode 100644 index cd217b8c1e..0000000000 --- a/api-server/common/app/Flash/redux/index.js +++ /dev/null @@ -1,58 +0,0 @@ -import _ from 'lodash/fp'; -import { - createTypes, - createAction, - createAsyncTypes, - composeReducers, - handleActions -} from 'berkeleys-redux-utils'; - -import * as utils from './utils.js'; -import getMessagesEpic from './get-messages-epic.js'; -import ns from '../ns.json'; - -// export all the utils -export { utils }; -export const epics = [getMessagesEpic]; -export const types = createTypes([ - 'clickOnClose', - createAsyncTypes('fetchMessages') -], ns); - -export const clickOnClose = createAction(types.clickOnClose, _.noop); -export const fetchMessagesComplete = createAction(types.fetchMessages.complete); -export const fetchMessagesError = createAction(types.fetchMessages.error); - -const defaultState = []; - -const getNS = _.property(ns); - -export const latestMessageSelector = _.flow( - getNS, - _.head, - _.defaultTo({}) -); - -export default composeReducers( - ns, - handleActions( - () => ({ - [types.clickOnClose]: _.tail, - [types.fetchMessages.complete]: (state, { payload }) => [ - ...state, - ...utils.expressToStack(payload) - ] - }), - defaultState, - ), - function metaReducer(state = defaultState, action) { - if (utils.isFlashAction(action)) { - const { payload } = utils.getFlashAction(action); - return [ - ...state, - ...payload - ]; - } - return state; - } -); diff --git a/api-server/common/app/Flash/redux/utils.js b/api-server/common/app/Flash/redux/utils.js deleted file mode 100644 index 2ebbfb9332..0000000000 --- a/api-server/common/app/Flash/redux/utils.js +++ /dev/null @@ -1,43 +0,0 @@ -import _ from 'lodash/fp'; -import { alertTypes, normalizeAlertType } from '../../../utils/flash.js'; - -// interface ExpressFlash { -// [alertType]: [String...] -// } -// interface StackFlash { -// type: AlertType, -// message: String -// } -export const expressToStack = _.flow( - _.toPairs, - _.flatMap(([ type, messages ]) => messages.map(msg => ({ - message: msg, - type: normalizeAlertType(type) - }))) -); - -export const isExpressFlash = _.flow( - _.keys, - _.every(type => alertTypes[type]) -); - -export const getFlashAction = _.flow( - _.property('meta'), - _.property('flash') -); - -// FlashMessage -// createFlashMetaAction(payload: ExpressFlash|StackFlash -export const createFlashMetaAction = payload => { - if (isExpressFlash(payload)) { - payload = expressToStack(payload); - } else { - payload = [payload]; - } - return { flash: { payload } }; -}; - -export const isFlashAction = _.flow( - getFlashAction, - Boolean -); diff --git a/api-server/common/app/Map/redux/fetch-map-ui-epic.js b/api-server/common/app/Map/redux/fetch-map-ui-epic.js deleted file mode 100644 index f0cf90eec7..0000000000 --- a/api-server/common/app/Map/redux/fetch-map-ui-epic.js +++ /dev/null @@ -1,38 +0,0 @@ -import { ofType } from 'redux-epic'; -import debug from 'debug'; - -import { - types as appTypes, - createErrorObservable -} from '../../redux'; -import { types, fetchMapUiComplete } from './'; -import { shapeChallenges } from '../../redux/utils'; - -const isDev = debug.enabled('fcc:*'); - -export default function fetchMapUiEpic( - actions, - _, - { services } -) { - return actions::ofType( - appTypes.appMounted, - types.fetchMapUi.start - ) - .flatMapLatest(() => { - const options = { - service: 'map-ui' - }; - return services.readService$(options) - .retry(3) - .map(({ entities, ...res }) => ({ - entities: shapeChallenges( - entities, - isDev - ), - ...res - })) - .map(fetchMapUiComplete) - .catch(createErrorObservable); - }); - } diff --git a/api-server/common/app/Map/redux/index.js b/api-server/common/app/Map/redux/index.js deleted file mode 100644 index 8fdc57c0a7..0000000000 --- a/api-server/common/app/Map/redux/index.js +++ /dev/null @@ -1,121 +0,0 @@ -import { - createAction, - createAsyncTypes, - createTypes, - handleActions -} from 'berkeleys-redux-utils'; -import { createSelector } from 'reselect'; -import { capitalize, noop } from 'lodash'; - -import * as utils from './utils.js'; -import { - createEventMetaCreator -} from '../../redux'; - -import fetchMapUiEpic from './fetch-map-ui-epic'; - -const ns = 'map'; - -export const epics = [ fetchMapUiEpic ]; - -export const types = createTypes([ - 'onRouteMap', - 'initMap', - createAsyncTypes('fetchMapUi'), - 'toggleThisPanel', - - 'isAllCollapsed', - 'collapseAll', - 'expandAll', - - 'clickOnChallenge' -], ns); - -export const initMap = createAction(types.initMap); - -export const fetchMapUi = createAction(types.fetchMapUi.start); -export const fetchMapUiComplete = createAction(types.fetchMapUi.complete); - -export const toggleThisPanel = createAction(types.toggleThisPanel); -export const collapseAll = createAction(types.collapseAll); - -export const expandAll = createAction(types.expandAll); -export const clickOnChallenge = createAction( - types.clickOnChallenge, - noop, - createEventMetaCreator({ - category: capitalize(ns), - action: 'click', - label: types.clickOnChallenge - }) -); - -const initialState = { - mapUi: { isAllCollapsed: false }, - superBlocks: [] -}; - -export const getNS = state => state[ns]; -export const allColapsedSelector = state => state[ns].isAllCollapsed; -export const mapSelector = state => getNS(state).mapUi; -export function makePanelOpenSelector(name) { - return createSelector( - mapSelector, - mapUi => { - const node = utils.getNode(mapUi, name); - return node ? node.isOpen : false; - } - ); -} - -// interface Map{ -// children: [...{ -// name: (superBlock: String), -// isOpen: Boolean, -// children: [...{ -// name: (blockName: String), -// isOpen: Boolean, -// children: [...{ -// name: (challengeName: String), -// }] -// }] -// } -// } -export default handleActions( - ()=> ({ - [types.toggleThisPanel]: (state, { payload: name }) => { - return { - ...state, - mapUi: utils.toggleThisPanel(state.mapUi, name) - }; - }, - [types.collapseAll]: state => { - const mapUi = utils.collapseAllPanels(state.mapUi); - mapUi.isAllCollapsed = true; - return { - ...state, - mapUi - }; - }, - [types.expandAll]: state => { - const mapUi = utils.expandAllPanels(state.mapUi); - mapUi.isAllCollapsed = false; - return { - ...state, - mapUi - }; - }, - [types.fetchMapUi.complete]: (state, { payload }) => { - const { entities, result, initialNode } = payload; - const mapUi = utils.createMapUi(entities, result); - return { - ...state, - ...result, - mapUi: utils.openPath(mapUi, initialNode) - }; - } - }), - initialState, - ns -); - diff --git a/api-server/common/app/Map/redux/utils.js b/api-server/common/app/Map/redux/utils.js deleted file mode 100644 index eda0044eea..0000000000 --- a/api-server/common/app/Map/redux/utils.js +++ /dev/null @@ -1,231 +0,0 @@ -import protect from '../../utils/empty-protector'; - -const throwIfUndefined = () => { - throw new Error('Challenge does not have a title'); -}; - -export function createSearchTitle( - name = throwIfUndefined(), - challengeMap = {} -) { - return challengeMap[name] || name; -} -// interface Node { -// isHidden: Boolean, -// children: Void|[ ...Node ], -// isOpen?: Boolean -// } -// -// interface MapUi -// { -// children: [...{ -// name: (superBlock: String), -// isOpen: Boolean, -// isHidden: Boolean, -// children: [...{ -// name: (blockName: String), -// isOpen: Boolean, -// isHidden: Boolean, -// children: [...{ -// name: (challengeName: String), -// isHidden: Boolean -// }] -// }] -// }] -// } -export function createMapUi( - { - block: blockMap, - challenge: challengeMap, - superBlock: superBlockMap - } = {}, - { superBlocks } = {} -) { - if (!superBlocks || !superBlockMap || !blockMap) { - return {}; - } - return { - children: superBlocks.map(superBlock => { - return { - name: superBlock, - isOpen: false, - isHidden: false, - children: protect(superBlockMap[superBlock]).blocks.map(block => { - return { - name: block, - isOpen: false, - isHidden: false, - children: protect(blockMap[block]).challenges.map(challenge => { - return { - name: challenge, - title: createSearchTitle(challenge, challengeMap), - isHidden: false, - children: null - }; - }) - }; - }) - }; - }) - }; -} - -// synchronise -// traverseMapUi( -// tree: MapUi|Node, -// update: ((MapUi|Node) => MapUi|Node) -// ) => MapUi|Node -export function traverseMapUi(tree, update) { - let childrenChanged; - if (!Array.isArray(tree.children)) { - return update(tree); - } - const newChildren = tree.children.map(node => { - const newNode = traverseMapUi(node, update); - if (!childrenChanged && newNode !== node) { - childrenChanged = true; - } - return newNode; - }); - if (childrenChanged) { - tree = { - ...tree, - children: newChildren - }; - } - return update(tree); -} - -// synchronise -// getNode(tree: MapUi, name: String) => MapUi -export function getNode(tree, name) { - let node; - traverseMapUi(tree, thisNode => { - if (thisNode.name === name) { - node = thisNode; - } - return thisNode; - }); - return node; -} - -// synchronise -// updateSingelNode( -// tree: MapUi, -// name: String, -// update(MapUi|Node) => MapUi|Node -// ) => MapUi -export function updateSingleNode(tree, name, update) { - return traverseMapUi(tree, node => { - if (name !== node.name) { - return node; - } - return update(node); - }); -} - -// synchronise -// toggleThisPanel(tree: MapUi, name: String) => MapUi -export function toggleThisPanel(tree, name) { - return updateSingleNode(tree, name, node => { - return { - ...node, - isOpen: !node.isOpen - }; - }); -} - -// toggleAllPanels(tree: MapUi, isOpen: Boolean = false ) => MapUi -export function toggleAllPanels(tree, isOpen = false) { - return traverseMapUi(tree, node => { - if (!Array.isArray(node.children) || node.isOpen === isOpen) { - return node; - } - return { - ...node, - isOpen - }; - }); -} - -// collapseAllPanels(tree: MapUi) => MapUi -export function collapseAllPanels(tree) { - return toggleAllPanels(tree); -} - -// expandAllPanels(tree: MapUi) => MapUi -export function expandAllPanels(tree) { - return toggleAllPanels(tree, true); -} - -// synchronise -// updatePath( -// tree: MapUi, -// name: String, -// update(MapUi|Node) => MapUi|Node -// ) => MapUi -export function updatePath(tree, name, pathUpdater) { - const path = []; - let pathFound = false; - - const isInPath = node => !!path.find(name => name === node.name); - - const traverseMap = (tree, update) => { - if (pathFound) { - return isInPath(tree) ? update(tree) : tree; - } - - if (tree.name === name) { - pathFound = true; - return update(tree); - } - - let childrenChanged; - - if (!Array.isArray(tree.children)) { - return tree; - } - - if (tree.name) { - path.push(tree.name); - } - - const newChildren = tree.children.map(node => { - const newNode = traverseMap(node, update); - if (!childrenChanged && newNode !== node) { - childrenChanged = true; - } - return newNode; - }); - if (childrenChanged) { - tree = { - ...tree, - children: newChildren - }; - } - - if (pathFound && isInPath(tree)) { - return update(tree); - } - - path.pop(); - return tree; - }; - - - return traverseMap(tree, pathUpdater); -} - -// synchronise -// openPath(tree: MapUi, name: String) => MapUi -export function openPath(tree, name) { - return updatePath(tree, name, node => { - if (!Array.isArray(node.children)) { - return node; - } - - return { ...node, isOpen: true }; - }); -} - - diff --git a/api-server/common/app/Map/redux/utils.test.js b/api-server/common/app/Map/redux/utils.test.js deleted file mode 100644 index ffb448dd8d..0000000000 --- a/api-server/common/app/Map/redux/utils.test.js +++ /dev/null @@ -1,319 +0,0 @@ -import test from 'tape'; -import sinon from 'sinon'; - -import { - getNode, - createMapUi, - traverseMapUi, - updateSingleNode, - toggleThisPanel, - expandAllPanels, - collapseAllPanels, - updatePath, - openPath -} from './utils.js'; - -test('createMapUi', t => { - t.plan(3); - t.test('should return an `{}` when proper args not supplied', t => { - t.plan(3); - t.equal( - Object.keys(createMapUi()).length, - 0 - ); - t.equal( - Object.keys(createMapUi({}, [])).length, - 0 - ); - t.equal( - Object.keys(createMapUi({ superBlock: {} }, [])).length, - 0 - ); - }); - t.test('should return a map tree', t => { - const expected = { - children: [{ - name: 'superBlockA', - children: [{ - name: 'blockA', - children: [{ - name: 'challengeA' - }] - }] - }] - }; - const actual = createMapUi({ - superBlock: { - superBlockA: { - blocks: [ - 'blockA' - ] - } - }, - block: { - blockA: { - challenges: [ - 'challengeA' - ] - } - } - }, - { superBlocks: ['superBlockA'] }, - { challengeA: 'ChallengeA title'} - ); - t.plan(3); - t.equal(actual.children[0].name, expected.children[0].name); - t.equal( - actual.children[0].children[0].name, - expected.children[0].children[0].name - ); - t.equal( - actual.children[0].children[0].children[0].name, - expected.children[0].children[0].children[0].name - ); - }); - t.test('should protect against malformed data', t => { - t.plan(2); - t.equal( - createMapUi({ - superBlock: {}, - block: { - blockA: { - challenges: [ - 'challengeA' - ] - } - } - }, { superBlocks: ['superBlockA'] }).children[0].children.length, - 0 - ); - t.equal( - createMapUi({ - superBlock: { - superBlockA: { - blocks: [ - 'blockA' - ] - } - }, - block: {} - }, - { superBlocks: ['superBlockA'] }).children[0].children[0].children.length, - 0 - ); - }); -}); -test('traverseMapUi', t => { - t.test('should return tree', t => { - t.plan(2); - const expectedTree = {}; - const actaulTree = traverseMapUi(expectedTree, tree => { - t.equal(tree, expectedTree); - return tree; - }); - t.equal(actaulTree, expectedTree); - }); - t.test('should hit every node', t => { - t.plan(4); - const expected = { children: [{ children: [{}] }] }; - const spy = sinon.spy(t => t); - spy.withArgs(expected); - spy.withArgs(expected.children[0]); - spy.withArgs(expected.children[0].children[0]); - traverseMapUi(expected, spy); - t.equal(spy.callCount, 3); - t.ok(spy.withArgs(expected).calledOnce, 'foo'); - t.ok(spy.withArgs(expected.children[0]).calledOnce, 'bar'); - t.ok(spy.withArgs(expected.children[0].children[0]).calledOnce, 'baz'); - }); - t.test('should create new object when children change', t => { - t.plan(9); - const expected = { children: [{ bar: true }, {}] }; - const actual = traverseMapUi(expected, node => ({ ...node, foo: true })); - t.notEqual(actual, expected); - t.notEqual(actual.children, expected.children); - t.notEqual(actual.children[0], expected.children[0]); - t.notEqual(actual.children[1], expected.children[1]); - t.equal(actual.children[0].bar, expected.children[0].bar); - t.notOk(expected.children[0].foo); - t.notOk(expected.children[1].foo); - t.true(actual.children[0].foo); - t.true(actual.children[1].foo); - }); -}); -test('getNode', t => { - t.test('should return node', t => { - t.plan(1); - const expected = { name: 'foo' }; - const tree = { children: [{ name: 'notfoo' }, expected ] }; - const actual = getNode(tree, 'foo'); - t.equal(expected, actual); - }); - t.test('should returned undefined if not found', t => { - t.plan(1); - const tree = { - children: [ { name: 'foo' }, { children: [ { name: 'bar' } ] } ] - }; - const actual = getNode(tree, 'baz'); - t.notOk(actual); - }); -}); -test('updateSingleNode', t => { - t.test('should update single node', t => { - const expected = { name: 'foo' }; - const untouched = { name: 'notFoo' }; - const actual = updateSingleNode( - { children: [ untouched, expected ] }, - 'foo', - node => ({ ...node, tag: true }) - ); - t.plan(4); - t.ok(actual.children[1].tag); - t.equal(actual.children[1].name, expected.name); - t.notEqual(actual.children[1], expected); - t.equal(actual.children[0], untouched); - }); -}); -test('toggleThisPanel', t => { - t.test('should update single node', t => { - const expected = { name: 'foo', isOpen: true }; - const actual = toggleThisPanel( - { children: [ { name: 'foo', isOpen: false }] }, - 'foo' - ); - t.plan(1); - t.deepLooseEqual(actual.children[0], expected); - }); -}); -test('toggleAllPanels', t => { - t.test('should add `isOpen: true` to every node without children', t => { - const expected = { - isOpen: true, - children: [{ - isOpen: true, - children: [{}, {}] - }] - }; - const actual = expandAllPanels({ children: [{ children: [{}, {}] }] }); - t.plan(1); - t.deepLooseEqual(actual, expected); - }); - t.test('should add `isOpen: false` to every node without children', t => { - const leaf = {}; - const expected = { - isOpen: false, - children: [{ - isOpen: false, - children: [{}, leaf] - }] - }; - const actual = collapseAllPanels( - { isOpen: true, children: [{ children: [{}, leaf]}]}, - ); - t.plan(2); - t.deepLooseEqual(actual, expected); - t.equal(actual.children[0].children[1], leaf); - }); -}); -test('updatePath', t => { - t.test('should call update function for each node in the path', t => { - const expected = { - children: [ - { - name: 'superFoo', - children: [ - { - name: 'blockBar', - children: [{name: 'challBar'}] - }, - { - name: 'blockFoo', - children: [{name: 'challFoo'}] - } - ] - }, - { - name: 'superBaz', - isOpen: false, - children: [] - } - ] - }; - - const spy = sinon.spy(t => ({ ...t}) ); - spy.withArgs(expected.children[0]); - spy.withArgs(expected.children[0].children[1]); - spy.withArgs(expected.children[0].children[1].children[0]); - updatePath(expected, 'challFoo', spy); - t.plan(4); - t.equal(spy.callCount, 3); - t.ok(spy.withArgs(expected.children[0]).calledOnce, 'superBlock'); - t.ok(spy.withArgs(expected.children[0].children[1]).calledOnce, 'block'); - t.ok( - spy.withArgs(expected.children[0].children[1].children[0]).calledOnce, - 'chall' - ); - }); -}); -test('openPath', t=> { - t.test('should open all nodes in the path', t => { - const expected = { - children: [ - { - name: 'superFoo', - isOpen: true, - children: [ - { - name: 'blockBar', - isOpen: false, - children: [] - }, - { - name: 'blockFoo', - isOpen: true, - children: [{ - name: 'challFoo' - }] - } - ] - }, - { - name: 'superBar', - isOpen: false, - children: [] - } - ] - }; - - const actual = openPath({ - children: [ - { - name: 'superFoo', - isOpen: false, - children: [ - { - name: 'blockBar', - isOpen: false, - children: [] - }, - { - name: 'blockFoo', - isOpen: false, - children: [{ - name: 'challFoo' - }] - } - ] - }, - { - name: 'superBar', - isOpen: false, - children: [] - } - ] - }, 'challFoo'); - - t.plan(1); - t.deepLooseEqual(actual, expected); - }); -}); diff --git a/api-server/common/app/Nav/LargeNav.jsx b/api-server/common/app/Nav/LargeNav.jsx deleted file mode 100644 index a1127e3bf4..0000000000 --- a/api-server/common/app/Nav/LargeNav.jsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; -import Media from 'react-media'; -import { Col, Navbar, Row } from 'react-bootstrap'; -import FCCSearchBar from 'react-freecodecamp-search'; -import { NavLogo, NavLinks } from './components'; - -import propTypes from './navPropTypes'; - -function LargeNav({ clickOnLogo }) { - return ( - ( - - - - - - - - - - - - - - - ) - } - /> - ); -} - -LargeNav.displayName = 'LargeNav'; -LargeNav.propTypes = propTypes; - -export default LargeNav; diff --git a/api-server/common/app/Nav/MediumNav.jsx b/api-server/common/app/Nav/MediumNav.jsx deleted file mode 100644 index 76c84095b5..0000000000 --- a/api-server/common/app/Nav/MediumNav.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import Media from 'react-media'; -import { Navbar, Row } from 'react-bootstrap'; -import FCCSearchBar from 'react-freecodecamp-search'; -import { NavLogo, NavLinks } from './components'; - -import propTypes from './navPropTypes'; - -function MediumNav({ clickOnLogo }) { - return ( - - { - matches => matches && typeof window !== 'undefined' && ( -
- - -
- - - -
-
- - - - - - - -
- ) - } - - ); -} - -MediumNav.displayName = 'MediumNav'; -MediumNav.propTypes = propTypes; - -export default MediumNav; diff --git a/api-server/common/app/Nav/Nav.jsx b/api-server/common/app/Nav/Nav.jsx deleted file mode 100644 index 23714fcb0f..0000000000 --- a/api-server/common/app/Nav/Nav.jsx +++ /dev/null @@ -1,60 +0,0 @@ -import React from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { Navbar } from 'react-bootstrap'; - -import LargeNav from './LargeNav.jsx'; -import MediumNav from './MediumNav.jsx'; -import SmallNav from './SmallNav.jsx'; -import { - clickOnLogo -} from './redux'; -import propTypes from './navPropTypes'; - -const mapStateToProps = () => ({}); - -function mapDispatchToProps(dispatch) { - return bindActionCreators( - { - clickOnLogo - }, - dispatch - ); -} - -const allNavs = [ - LargeNav, - MediumNav, - SmallNav -]; - -function FCCNav(props) { - const { - clickOnLogo - } = props; - const withNavProps = Component => ( - - ); - return ( - - { - allNavs.map(withNavProps) - } - - ); -} - -FCCNav.displayName = 'FCCNav'; -FCCNav.propTypes = propTypes; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(FCCNav); diff --git a/api-server/common/app/Nav/SmallNav.jsx b/api-server/common/app/Nav/SmallNav.jsx deleted file mode 100644 index dfa251437e..0000000000 --- a/api-server/common/app/Nav/SmallNav.jsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react'; -import Media from 'react-media'; -import { Navbar, Row } from 'react-bootstrap'; -import FCCSearchBar from 'react-freecodecamp-search'; -import { NavLogo, NavLinks } from './components'; - -import propTypes from './navPropTypes'; - -function SmallNav({ clickOnLogo }) { - return ( - - { - matches => matches && typeof window !== 'undefined' && ( -
- - -
- - -
-
- - - - - - - - - -
- ) - } - - ); -} - -SmallNav.displayName = 'SmallNav'; -SmallNav.propTypes = propTypes; - -export default SmallNav; diff --git a/api-server/common/app/Nav/components/Bin-Button.jsx b/api-server/common/app/Nav/components/Bin-Button.jsx deleted file mode 100644 index 3be178145a..0000000000 --- a/api-server/common/app/Nav/components/Bin-Button.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Button } from 'react-bootstrap'; - -const propTypes = { - content: PropTypes.string, - disabled: PropTypes.bool, - handleClick: PropTypes.func.isRequired -}; - -export default function BinButton({ content, handleClick, disabled }) { - return ( - - ); -} -BinButton.displayName = 'BinButton'; -BinButton.propTypes = propTypes; diff --git a/api-server/common/app/Nav/components/BinButtons.jsx b/api-server/common/app/Nav/components/BinButtons.jsx deleted file mode 100644 index abdc702827..0000000000 --- a/api-server/common/app/Nav/components/BinButtons.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { ButtonGroup } from 'react-bootstrap'; -import BinButton from './Bin-Button.jsx'; - -const propTypes = { - panes: PropTypes.arrayOf( - PropTypes.shape({ - actionCreator: PropTypes.func.isRequired, - content: PropTypes.string.isRequired - }) - ) -}; - -function BinButtons({ panes }) { - return ( - - { - panes.map(({ content, actionCreator, isHidden }) => ( - - )) - } - - ); -} - -BinButtons.displayName = 'BinButtons'; -BinButtons.propTypes = propTypes; - -export default BinButtons; diff --git a/api-server/common/app/Nav/components/NavLinks.jsx b/api-server/common/app/Nav/components/NavLinks.jsx deleted file mode 100644 index ac12ba778b..0000000000 --- a/api-server/common/app/Nav/components/NavLinks.jsx +++ /dev/null @@ -1,151 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { capitalize } from 'lodash'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { MenuItem, NavDropdown, NavItem, Nav } from 'react-bootstrap'; - -import navLinks from '../links.json'; -import SignUp from './Sign-Up.jsx'; -import { Link } from '../../Router'; - -import { - openDropdown, - closeDropdown, - dropdownSelector, - createNavLinkActionCreator -} from '../redux'; -import { isSignedInSelector, signInLoadingSelector } from '../../redux'; - -const mapStateToProps = createSelector( - isSignedInSelector, - dropdownSelector, - signInLoadingSelector, - (isSignedIn, isDropdownOpen, showLoading) => ({ - isDropdownOpen, - isSignedIn, - navLinks, - showLoading - }) -); - -function mapDispatchToProps(dispatch) { - return bindActionCreators( - { - ...navLinks.reduce( - (mdtp, { content }) => { - const handler = `handle${capitalize(content)}Click`; - mdtp[handler] = createNavLinkActionCreator(content); - return mdtp; - }, {}), - closeDropdown, - openDropdown - }, - dispatch - ); -} - -const navLinkPropType = PropTypes.shape({ - content: PropTypes.string, - link: PropTypes.string, - isDropdown: PropTypes.bool, - target: PropTypes.string, - links: PropTypes.array -}); - -const propTypes = { - children: PropTypes.any, - closeDropdown: PropTypes.func.isRequired, - isDropdownOpen: PropTypes.bool, - isInNav: PropTypes.bool, - isSignedIn: PropTypes.bool, - navLinks: PropTypes.arrayOf(navLinkPropType), - openDropdown: PropTypes.func.isRequired, - showLoading: PropTypes.bool -}; - -class NavLinks extends PureComponent { - - renderLink(isNavItem, { isReact, isDropdown, content, link, links, target }) { - const Component = isNavItem ? NavItem : MenuItem; - const { - isDropdownOpen, - openDropdown, - closeDropdown - } = this.props; - - if (isDropdown) { - // adding a noop to NavDropdown to disable false warning - // about controlled component - return ( - - { links.map(this.renderLink.bind(this, false)) } - - ); - } - if (isReact) { - return ( - - - { content } - - - ); - } - return ( - - { content } - - ); - } - - render() { - const { - showLoading, - isSignedIn, - navLinks, - isInNav = true, - children - } = this.props; - return ( - - ); - } -} - -NavLinks.displayName = 'NavLinks'; -NavLinks.propTypes = propTypes; - -export default connect(mapStateToProps, mapDispatchToProps)(NavLinks); diff --git a/api-server/common/app/Nav/components/NavLogo.jsx b/api-server/common/app/Nav/components/NavLogo.jsx deleted file mode 100644 index 31a1d811f5..0000000000 --- a/api-server/common/app/Nav/components/NavLogo.jsx +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { NavbarBrand } from 'react-bootstrap'; -import Media from 'react-media'; - -const fCClogo = 'https://s3.amazonaws.com/freecodecamp/freecodecamp_logo.svg'; -const fCCglyph = 'https://s3.amazonaws.com/freecodecamp/FFCFire.png'; - -const propTypes = { - clickOnLogo: PropTypes.func.isRequired -}; - -function NavLogo({ clickOnLogo }) { - return ( - - - - { - matches => matches ? ( - learn to code javascript at freeCodeCamp logo - ) : ( - learn to code javascript at freeCodeCamp logo - ) - } - - - - ); -} - -NavLogo.displayName = 'NavLogo'; -NavLogo.propTypes = propTypes; - -export default NavLogo; diff --git a/api-server/common/app/Nav/components/Sign-Up.jsx b/api-server/common/app/Nav/components/Sign-Up.jsx deleted file mode 100644 index 6fc2bb30e3..0000000000 --- a/api-server/common/app/Nav/components/Sign-Up.jsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { MenuItem, NavItem } from 'react-bootstrap'; - -import { Link } from '../../Router'; -import { onRouteSettings } from '../../routes/Settings/redux'; - -const propTypes = { - isInDropDown: PropTypes.bool, - showLoading: PropTypes.bool, - showSignUp: PropTypes.bool -}; - -function SignUpButton({ isInDropDown, showLoading, showSignUp }) { - if (showLoading) { - return null; - } - if (showSignUp) { - return isInDropDown ? ( - - Sign Up - - ) : ( - - Sign Up - - ); - } - return ( -
  • - - Settings - -
  • - ); -} - -SignUpButton.displayName = 'SignUpButton'; -SignUpButton.propTypes = propTypes; - -export default SignUpButton; diff --git a/api-server/common/app/Nav/components/index.js b/api-server/common/app/Nav/components/index.js deleted file mode 100644 index a406b1ec16..0000000000 --- a/api-server/common/app/Nav/components/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export { default as BinButtons } from './BinButtons.jsx'; -export { default as NavLogo } from './NavLogo.jsx'; -export { default as NavLinks } from './NavLinks.jsx'; diff --git a/api-server/common/app/Nav/index.js b/api-server/common/app/Nav/index.js deleted file mode 100644 index 0d1bcba7c2..0000000000 --- a/api-server/common/app/Nav/index.js +++ /dev/null @@ -1 +0,0 @@ -export default from './Nav.jsx'; diff --git a/api-server/common/app/Nav/links.json b/api-server/common/app/Nav/links.json deleted file mode 100644 index 2e47b55a42..0000000000 --- a/api-server/common/app/Nav/links.json +++ /dev/null @@ -1,15 +0,0 @@ -[ - { - "content": "Curriculum", - "link": "https://learn.freecodecamp.org" - }, - { - "content": "Forum", - "link": "https://forum.freecodecamp.org/", - "target": "_blank" - }, - { - "content": "News", - "link": "https://www.freecodecamp.org/news" - } -] \ No newline at end of file diff --git a/api-server/common/app/Nav/nav.less b/api-server/common/app/Nav/nav.less deleted file mode 100644 index 6ce16737b2..0000000000 --- a/api-server/common/app/Nav/nav.less +++ /dev/null @@ -1,345 +0,0 @@ -.navbar { - background-color: @brand-primary; - font-size: 14px; -} - -.navbar-nav > li > a { - color: @body-bg; - &:hover { - color: @brand-primary; - } -} - -.navbar > .container { - padding-right: 0px; - width: auto; - - @media (max-width: 767px) { - // container default padding size - padding-left: 15px; - padding-right: 15px; - } -} - -.nav-height { - border: none; - height: @navbar-height; - width: 100%; -} - -@navbar-logo-height: 25px; -@navbar-logo-padding: (@navbar-height - @navbar-logo-height) / 2; -.navbar-brand { - padding-top: @navbar-logo-padding; - padding-bottom: @navbar-logo-padding; - display: flex; - align-items: center; - justify-content: center; -} - -.nav-logo { - height: @navbar-logo-height; -} - -.navbar-right { - background-color: @brand-primary; - text-align: center; - - @media (min-width: @screen-md-min) { - margin-right:0; - } - @media (min-width: @screen-md-max) and (max-width: 991px) { - left: 0; - margin-right: 0; - position: absolute; - right: 0; - white-space: nowrap; - } -} - -.navbar { - white-space: nowrap; - border: none; - line-height: 1; - @media (min-width: 767px) { - padding-left: 15px; - padding-right: 30px; - } -} - -// li is used here to get more specific -// and win against navbar.less#273 -li.nav-avatar { - span { - display: inline-block; - } - @media (min-width: @screen-sm-min) { - height: @navbar-height; - margin: 0; - padding: 0; - - > a { - margin: 0; - padding: 7.5px @navbar-padding-horizontal 7.5px @navbar-padding-horizontal; - } - } -} -.navbar-nav a { - color: @body-bg; - margin-top: -5px; - margin-bottom: -5px; -} - -.navbar-toggle { - color: @body-bg; - - &:hover, - &:focus { - color: #4a2b0f; - } -} - -.navbar-collapse { - border-top: 0; -} - -.divider-vertical { - height: 24px; - margin-top: 6px; - margin-bottom: 6px; - border-left: 0.25px solid #ffffff; - border-right: 0.25px solid #ffffff; -} - -@media (max-width: @screen-xs-max) { - .navbar-header { - float: none; - } - - .navbar-toggle { - display: block; - } - - .navbar-collapse.collapse { - display: none !important; - } - - .navbar-nav { - margin-top: 0; - } - - .navbar-nav > li { - float: none; - } - - .navbar-nav > li > a { - padding-top: 10px; - padding-bottom: 10px; - } - - .navbar-text { - float: none; - margin: 15px 0; - } - - /* since 3.1.0 */ - .navbar-collapse.collapse.in { - display: block !important; - } - - .collapsing { - overflow: hidden !important; - position: absolute; - left: 0; - right: 0; - } -} - -.night { - .nav-component-wrapper { - ::-webkit-input-placeholder { - color: @night-text-color; - } - - ::-moz-placeholder { - color: @night-text-color; - } - - ::-ms-placeholder { - color: @night-text-color; - } - - ::placeholder { - color: @night-text-color; - } - - .fcc_input { - background-color: @night-search-color; - color: @night-text-color; - } - } - .navbar-default { - .navbar-nav { - & > li > a { - color: #CCC; - } - .dropdown-menu { - background-color: @gray; - a { - color: @night-text-color !important; - } - } - a:focus, - a:hover, - .open #nav-Community-dropdown { - background-color: #666 !important; - color: @link-hover-color !important; - } - } - } - .navbar-toggle { - &:hover, - &:focus { - background-color: #666; - color: @link-hover-color; - border-color: #666; - } - } -} - -@media (min-width: 768px) and (max-width: 860px) { - .navbar { - padding-left: 0; - padding-right: 0; - } - - .navbar-right { - margin-right: 0; - } - - .navbar-nav > li > a { - padding-left: 10px; - padding-right: 10px; - } -} - -.nav-component { - display: flex; - align-items: center; - - &.header{ - - .navbar-brand { - padding-left: 0px; - } - - } - - &.bins { - justify-content: center; - - .nav { - display: flex; - } - } - - - &.nav-links { - justify-content: flex-end; - } - - .fcc_searchBar { - width: auto; - flex-grow: 1; - ::-webkit-input-placeholder { - color: @input-color-placeholder; - } - - ::-moz-placeholder { - color: @input-color-placeholder; - } - - ::-ms-placeholder { - color: @input-color-placeholder; - } - - ::placeholder { - color: @input-color-placeholder; - } - - .ais-Hits { - background-color: white; - z-index: 5; - } - } - .navbar-header { - display: flex; - align-items: center; - width: 100%; - } -} - -.medium-nav { - display: flex; - justify-content: space-between; - - .bins { - justify-content: flex-end; - - } -} - -.small-nav { - display: flex; - justify-content: space-between; - - .bins { - justify-content: flex-end; - - .btn { - padding: 6px 4px; - } - } -} - -.bins { - - .disabled-button { - color: whitesmoke; - } - .enabled-button { - color: black; - } - - .btn { - border-color: @brand-primary; - background-color: white; - - &:hover { - background-color: @brand-primary; - } - - } - -} - -.collapse-row { - background-color: @brand-primary; - - .dropdown-menu { - - li a { - display: flex; - justify-content: center; - background-color: #eeeeee; - color: @brand-primary !important; - - &:hover { - background-color: @brand-primary !important; - color: #eeeeee !important; - } - } - - } - -} diff --git a/api-server/common/app/Nav/navPropTypes.js b/api-server/common/app/Nav/navPropTypes.js deleted file mode 100644 index 309c69ecac..0000000000 --- a/api-server/common/app/Nav/navPropTypes.js +++ /dev/null @@ -1,5 +0,0 @@ -import PropTypes from 'prop-types'; - -export default { - clickOnLogo: PropTypes.func.isRequired -}; diff --git a/api-server/common/app/Nav/ns.json b/api-server/common/app/Nav/ns.json deleted file mode 100644 index ea84de45ff..0000000000 --- a/api-server/common/app/Nav/ns.json +++ /dev/null @@ -1 +0,0 @@ -"nav" diff --git a/api-server/common/app/Nav/redux/index.js b/api-server/common/app/Nav/redux/index.js deleted file mode 100644 index 6a354b8b31..0000000000 --- a/api-server/common/app/Nav/redux/index.js +++ /dev/null @@ -1,76 +0,0 @@ -import capitalize from 'lodash/capitalize'; -import noop from 'lodash/noop'; -import { - createAction, - createTypes, - handleActions -} from 'berkeleys-redux-utils'; - -import ns from '../ns.json'; -import { createEventMetaCreator } from '../../analytics/index'; - -export const epics = []; - -export const types = createTypes([ - 'clickOnLogo', - 'clickOnMap', - 'navLinkClicked', - - 'closeDropdown', - 'openDropdown' -], ns); - -export const clickOnLogo = createAction( - types.clickOnLogo, - noop, - createEventMetaCreator({ - category: 'Nav', - action: 'clicked', - label: 'fcc logo clicked' - }) -); - -export const clickOnMap = createAction( - types.clickOnMap, - noop, - createEventMetaCreator({ - category: 'Nav', - action: 'clicked', - label: 'map button clicked' - }) -); - -export const closeDropdown = createAction(types.closeDropdown); -export const openDropdown = createAction(types.openDropdown); -export function createNavLinkActionCreator(link) { - return createAction( - types.navLinkClicked, - noop, - createEventMetaCreator({ - category: capitalize(ns), - action: 'click', - label: `${link} link` - }) - ); -} - -const initialState = { - isDropdownOpen: false -}; - -export const dropdownSelector = state => state[ns].isDropdownOpen; - -export default handleActions( - () => ({ - [types.closeDropdown]: state => ({ - ...state, - isDropdownOpen: false - }), - [types.openDropdown]: state => ({ - ...state, - isDropdownOpen: true - }) - }), - initialState, - ns -); diff --git a/api-server/common/app/NotFound/Not-Found.jsx b/api-server/common/app/NotFound/Not-Found.jsx deleted file mode 100644 index a439c75d90..0000000000 --- a/api-server/common/app/NotFound/Not-Found.jsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; -// import PropTypes from 'prop-types'; -import { - Alert, - Button -} from 'react-bootstrap'; - -const propTypes = {}; - -export default function NotFound() { - return ( -
    - -

    - { 'Sorry, we couldn\'t find a page for that address.' } -

    -
    - - - -
    - ); -} - -NotFound.displayName = 'NotFound'; -NotFound.propTypes = propTypes; diff --git a/api-server/common/app/NotFound/index.js b/api-server/common/app/NotFound/index.js deleted file mode 100644 index b174c4f934..0000000000 --- a/api-server/common/app/NotFound/index.js +++ /dev/null @@ -1 +0,0 @@ -export default from './Not-Found.jsx'; diff --git a/api-server/common/app/Router/Link.jsx b/api-server/common/app/Router/Link.jsx deleted file mode 100644 index df29e53cbf..0000000000 --- a/api-server/common/app/Router/Link.jsx +++ /dev/null @@ -1,85 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; - -import toUrl from './to-url.js'; -import createHandler from './handle-press.js'; -import { routesMapSelector } from './redux'; - -const mapStateToProps = createSelector( - routesMapSelector, - routesMap => ({ routesMap }) -); - -const propTypes = { - children: PropTypes.node, - dispatch: PropTypes.func, - onClick: PropTypes.func, - redirect: PropTypes.bool, - replace: PropTypes.bool, - routesMap: PropTypes.object, - shouldDispatch: PropTypes.bool, - style: PropTypes.object, - target: PropTypes.string, - to: PropTypes.oneOfType([ PropTypes.object, PropTypes.string ]).isRequired -}; - -export const Link = ( - { - children, - dispatch, - onClick, - redirect, - replace, - routesMap, - shouldDispatch = true, - style, - target, - to - } -) => { - const url = toUrl(to, routesMap); - const handler = createHandler( - url, - routesMap, - onClick, - shouldDispatch, - target, - dispatch, - to, - replace || redirect - ); - - const localProps = {}; - - if (url) { - localProps.href = url; - } - - if (handler) { - localProps.onMouseDown = handler; - localProps.onTouchStart = handler; - } - - if (target) { - localProps.target = target; - } - - return ( - - {children} - - ); -}; - -Link.contextTypes = { - store: PropTypes.object.isRequired -}; -Link.propTypes = propTypes; - -export default connect(mapStateToProps)(Link); diff --git a/api-server/common/app/Router/handle-press.js b/api-server/common/app/Router/handle-press.js deleted file mode 100644 index 5cda85860d..0000000000 --- a/api-server/common/app/Router/handle-press.js +++ /dev/null @@ -1,50 +0,0 @@ -import { pathToAction, redirect, getOptions } from 'redux-first-router'; - -const isAction = to => typeof to === 'object' && - !Array.isArray(to); - -const isModified = e => !!( - e.metaKey || - e.altKey || - e.ctrlKey || - e.shiftKey -); - -export default ( - url, - routesMap, - onClick, - shouldDispatch, - target, - dispatch, - to, - dispatchRedirect -) => e => { - let shouldGo = true; - - if (onClick) { - // onClick can return false to prevent dispatch - shouldGo = onClick(e); - shouldGo = typeof shouldGo === 'undefined' ? true : shouldGo; - } - - const prevented = e.defaultPrevented; - - if (!target && e && e.preventDefault && !isModified(e)) { - e.preventDefault(); - } - - if ( - shouldGo && - shouldDispatch && - !target && - !prevented && - e.button === 0 && - !isModified(e) - ) { - const { querySerializer: serializer } = getOptions(); - let action = isAction(to) ? to : pathToAction(url, routesMap, serializer); - action = dispatchRedirect ? redirect(action) : action; - dispatch(action); - } -}; diff --git a/api-server/common/app/Router/index.js b/api-server/common/app/Router/index.js deleted file mode 100644 index 79f148eac7..0000000000 --- a/api-server/common/app/Router/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default as Link } from './Link.jsx'; diff --git a/api-server/common/app/Router/redux/index.js b/api-server/common/app/Router/redux/index.js deleted file mode 100644 index 9cae6f2f53..0000000000 --- a/api-server/common/app/Router/redux/index.js +++ /dev/null @@ -1,8 +0,0 @@ -import { selectLocationState } from 'redux-first-router'; - -export const paramsSelector = state => selectLocationState(state).payload || {}; -export const locationTypeSelector = - state => selectLocationState(state).type || ''; -export const routesMapSelector = state => - selectLocationState(state).routesMap || {}; -export const pathnameSelector = state => selectLocationState(state).pathname; diff --git a/api-server/common/app/Router/to-url.js b/api-server/common/app/Router/to-url.js deleted file mode 100644 index 37a5a4d210..0000000000 --- a/api-server/common/app/Router/to-url.js +++ /dev/null @@ -1,41 +0,0 @@ -import { actionToPath, getOptions } from 'redux-first-router'; - -export default (to, routesMap) => { - if (to && typeof to === 'string') { - return to; - } - - if (typeof to === 'object') { - const { payload = {}, ...action } = to; - - try { - const { querySerializer } = getOptions(); - return actionToPath( - { - ...action, - payload: { - ...payload - } - }, - routesMap, - querySerializer - ); - } catch (e) { - console.error(e); - console.warn( - '[Link] could not create path from action:', - action, - 'For reference, here are your current routes:', - routesMap - ); - - return '#'; - } - } - - console.warn( - '[Link] `to` prop must be a string or action object. You provided: ', - to - ); - return '#'; -}; diff --git a/api-server/common/app/Toasts/Toasts.jsx b/api-server/common/app/Toasts/Toasts.jsx deleted file mode 100644 index 322712dd93..0000000000 --- a/api-server/common/app/Toasts/Toasts.jsx +++ /dev/null @@ -1,91 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { NotificationStack } from '@freecodecamp/react-notification'; - -import { removeToast } from './redux'; - -const registeredActions = {}; -const mapStateToProps = state => ({ toasts: state.toasts }); -// we use styles here to overwrite those built into the library -// but there are some styles applied using -// regular css in /client/less/toastr.less -const barStyle = { - fontSize: '2rem', - // null values let our css set the style prop - padding: null -}; -const rightBarStyle = { - ...barStyle, - left: null, - right: '-100%' -}; -const actionStyle = { - fontSize: '2rem' -}; -const addDispatchableActionsToToast = createSelector( - state => state.toasts, - state => state.dispatch, - (toasts, dispatch) => toasts.map(({ position, actionCreator, ...toast }) => { - const activeBarStyle = {}; - let finalBarStyle = barStyle; - if (position !== 'left') { - activeBarStyle.left = null; - activeBarStyle.right = '1rem'; - finalBarStyle = rightBarStyle; - } - const onClick = !registeredActions[actionCreator] ? - () => { - dispatch(removeToast(toast)); - } : - () => { - dispatch(registeredActions[actionCreator]()); - dispatch(removeToast(toast)); - }; - return { - ...toast, - barStyle: finalBarStyle, - activeBarStyle, - actionStyle, - onClick - }; - }) -); -const propTypes = { - dispatch: PropTypes.func, - toasts: PropTypes.arrayOf(PropTypes.object) -}; - -export class Toasts extends React.Component { - constructor(...props) { - super(...props); - this.handleDismiss = this.handleDismiss.bind(this); - } - styleFactory(index, style) { - return { ...style, bottom: `${4 + index * 8}rem` }; - } - - handleDismiss(notification) { - this.props.dispatch(removeToast(notification)); - } - - render() { - const { toasts = [], dispatch } = this.props; - return ( - - ); - } -} - -Toasts.displayName = 'Toasts'; -Toasts.propTypes = propTypes; - -export default connect(mapStateToProps)(Toasts); diff --git a/api-server/common/app/Toasts/index.js b/api-server/common/app/Toasts/index.js deleted file mode 100644 index 2518a55c0f..0000000000 --- a/api-server/common/app/Toasts/index.js +++ /dev/null @@ -1 +0,0 @@ -export default from './Toasts.jsx'; diff --git a/api-server/common/app/Toasts/ns.json b/api-server/common/app/Toasts/ns.json deleted file mode 100644 index 2471c4eb56..0000000000 --- a/api-server/common/app/Toasts/ns.json +++ /dev/null @@ -1 +0,0 @@ -"toasts" diff --git a/api-server/common/app/Toasts/redux/index.js b/api-server/common/app/Toasts/redux/index.js deleted file mode 100644 index f39d37cdb2..0000000000 --- a/api-server/common/app/Toasts/redux/index.js +++ /dev/null @@ -1,47 +0,0 @@ -import { - createAction, - createTypes, - handleActions -} from 'berkeleys-redux-utils'; - -import ns from '../ns.json'; - -export const types = createTypes([ - 'makeToast', - 'removeToast' -], ns); - -let key = 0; -export const makeToast = createAction( - types.makeToast, - ({ timeout, ...rest }) => ({ - ...rest, - // assign current value of key to new toast - // and then increment key value - key: key++, - dismissAfter: timeout || 6000, - position: rest.position === 'left' ? 'left' : 'right' - }) -); - -export const removeToast = createAction( - types.removeToast, - ({ key }) => key -); - - -const initialState = []; - -export default handleActions( - () => ({ - [types.makeToast]: (state, { payload: toast }) => [ - ...state, - toast - ].filter(toast => !!toast.message), - [types.removeToast]: (state, { payload: key }) => state.filter( - toast => toast.key !== key - ) - }), - initialState, - ns -); diff --git a/api-server/common/app/analytics/index.js b/api-server/common/app/analytics/index.js deleted file mode 100644 index 59047cfe8b..0000000000 --- a/api-server/common/app/analytics/index.js +++ /dev/null @@ -1,34 +0,0 @@ -const throwIfUndefined = () => { - throw new TypeError('Argument must not be of type `undefined`'); -}; - -// createEventMetaCreator({ -// category: String, -// action: String, -// label?: String, -// value?: Number -// }) => () => Object -export const createEventMetaCreator = ({ - // categories are features or namespaces of the app (capitalized): - // Map, Nav, Challenges, and so on - category = throwIfUndefined, - // can be a one word the event - // click, play, toggle. - // This is not a hard and fast rule - action = throwIfUndefined, - // any additional information - // when in doubt use redux action type - // or a short sentence describing the - // action - label, - // used to tack some specific value for a GA event - value -} = throwIfUndefined) => () => ({ - analytics: { - type: 'event', - category, - action, - label, - value - } -}); diff --git a/api-server/common/app/app.less b/api-server/common/app/app.less deleted file mode 100644 index b393cdaf27..0000000000 --- a/api-server/common/app/app.less +++ /dev/null @@ -1,26 +0,0 @@ -// should match ./ns.json value and filename -@ns: app; - -.@{ns}-container { - // we invert the nav and content since - // the content needs to render first - // Here we invert the order in which - // they are painted using css so the - // nav is on top again - .grid(@direction: column; @wrap: nowrap); - height: 100%; - width: 100%; -} - -.@{ns}-content { - // makes the initial content height 0px - // then lets it grow to fit the rest of the space - flex: 1 0 0px; - // allow content to adapt to screen size - display: flex; -} - -.@{ns}-centered { - .center(@value: @container-xl, @padding: @grid-gutter-width); - margin-top: @navbar-margin-bottom; -} diff --git a/api-server/common/app/create-app.jsx b/api-server/common/app/create-app.jsx deleted file mode 100644 index 97e5df1669..0000000000 --- a/api-server/common/app/create-app.jsx +++ /dev/null @@ -1,104 +0,0 @@ -import { Observable } from 'rx'; -import createDebugger from 'debug'; -import { compose, createStore, applyMiddleware } from 'redux'; -import { selectLocationState, connectRoutes } from 'redux-first-router'; -import { combineReducers } from 'berkeleys-redux-utils'; - -import { createEpic } from 'redux-epic'; -import appReducer from './reducer.js'; -import routesMap from './routes-map.js'; -import epics from './epics'; - -import servicesCreator from '../utils/services-creator'; - -const debug = createDebugger('fcc:app:createApp'); -// createApp(settings: { -// history?: History, -// defaultState?: Object|Void, -// serviceOptions?: Object, -// middlewares?: [...Function], -// enhancers?: [...Function], -// epics?: [...Function], -// }) => Observable -// -// Either location or history must be defined -export default function createApp({ - history, - defaultState, - serviceOptions = {}, - middlewares: sideMiddlewares = [], - enhancers: sideEnhancers = [], - epics: sideEpics = [], - epicOptions: sideEpicOptions = {} -}) { - const epicOptions = { - ...sideEpicOptions, - services: servicesCreator(serviceOptions) - }; - - const epicMiddleware = createEpic( - epicOptions, - ...epics, - ...sideEpics - ); - - const { - reducer: routesReducer, - middleware: routesMiddleware, - enhancer: routesEnhancer - } = connectRoutes(history, routesMap); - - routesReducer.toString = () => 'location'; - - const enhancer = compose( - routesEnhancer, - applyMiddleware( - routesMiddleware, - epicMiddleware, - ...sideMiddlewares - ), - // enhancers must come after middlewares - // on client side these are things like Redux DevTools - ...sideEnhancers - ); - - const reducer = combineReducers( - appReducer, - routesReducer - ); - - // create composed store enhancer - // use store enhancer function to enhance `createStore` function - // call enhanced createStore function with reducer and defaultState - // to create store - const store = createStore(reducer, defaultState, enhancer); - const location = selectLocationState(store.getState()); - - // note(berks): should get stripped in production client by webpack - // We need to find a way to hoist to top level in production node env - // babel plugin, maybe? After a quick search I couldn't find one - if (process.env.NODE_ENV === 'development') { - if (module.hot) { - module.hot.accept('./reducer.js', () => { - debug('hot reloading reducers'); - store.replaceReducer(combineReducers( - require('./reducer.js').default, - routesReducer - )); - }); - } - } - // ({ - // redirect, - // props, - // reducer, - // store, - // epic: epicMiddleware - // })); - return Observable.of({ - store, - epic: epicMiddleware, - location, - notFound: false - }); -} diff --git a/api-server/common/app/entities/index.js b/api-server/common/app/entities/index.js deleted file mode 100644 index 5d95742299..0000000000 --- a/api-server/common/app/entities/index.js +++ /dev/null @@ -1,331 +0,0 @@ -import { findIndex, property, merge } from 'lodash'; -import uuid from 'uuid/v4'; -import { - composeReducers, - createAction, - createTypes, - handleActions -} from 'berkeleys-redux-utils'; - -import { themes } from '../../utils/themes'; -import { usernameSelector } from '../redux'; -import { types as map } from '../Map/redux'; -import legacyProjects from '../../utils/legacyProjectData'; - -export const ns = 'entities'; -export const getNS = state => state[ns]; -export const entitiesSelector = getNS; -export const types = createTypes([ - 'addPortfolioItem', - 'optoUpdatePortfolio', - 'regresPortfolio', - 'resetFullBlocks', - 'updateLocalProfileUI', - 'updateMultipleUserFlags', - 'updateTheme', - 'updateUserFlag', - 'updateUserEmail', - 'updateUserLang', - 'updateUserCurrentChallenge' -], ns); - -// addPortfolioItem(...PortfolioItem) => Action -export const addPortfolioItem = createAction(types.addPortfolioItem); -// optoUpdatePortfolio(...PortfolioItem) => Action -export const optoUpdatePortfolio = createAction(types.optoUpdatePortfolio); -// regresPortfolio(id: String) => Action -export const regresPortfolio = createAction(types.regresPortfolio); - -// updateMultipleUserFlags({ username: String, flags: { String }) => Action -export const updateMultipleUserFlags = createAction( - types.updateMultipleUserFlags -); - -// updateUserFlag(username: String, flag: String) => Action -export const updateUserFlag = createAction( - types.updateUserFlag, - (username, flag) => ({ username, flag }) -); -// updateUserEmail(username: String, email: String) => Action -export const updateUserEmail = createAction( - types.updateUserEmail, - (username, email) => ({ username, email }) -); -// updateUserLang(username: String, lang: String) => Action -export const updateUserLang = createAction( - types.updateUserLang, - (username, lang) => ({ username, languageTag: lang }) -); - -export const updateLocalProfileUI = createAction(types.updateLocalProfileUI); - -export const resetFullBlocks = createAction(types.resetFullBlocks); - -export const updateUserCurrentChallenge = createAction( - types.updateUserCurrentChallenge -); - -// entity meta creators -const getEntityAction = property('meta.entitiesAction'); - -export const updateThemeMetacreator = (username, theme) => ({ - entitiesAction: { - type: types.updateTheme, - payload: { - username, - theme: !theme || theme === themes.default ? themes.default : themes.night - } - } -}); - -export function emptyPortfolio() { - return { - id: uuid(), - title: '', - description: '', - url: '', - image: '' - }; -} - -const defaultState = { - superBlock: {}, - block: {}, - challenge: {}, - user: {}, - fullBlocks: [] -}; - -export function portfolioSelector(state, props) { - const username = usernameSelector(state); - const { portfolio } = getNS(state).user[username]; - const pIndex = findIndex(portfolio, p => p.id === props.id); - return portfolio[pIndex]; -} - -export function projectsSelector(state) { - const { - block: blocks, - challenge: challengeMap - } = getNS(state); - const idToNameMap = challengeIdToNameMapSelector(state); - const legacyWithDashedNames = legacyProjects - .reduce((list, current) => ([ - ...list, - { - ...current, - challenges: current.challenges.map(id => idToNameMap[id]) - } - ]), - [] - ); - - blocks['full-stack-projects'] = { - dashedName: 'full-stack', - title: 'Full Stack Certification', - time: '1800 hours', - challenges: [], - superBlock: 'full-stack' - }; - return Object.keys(blocks) - .filter(key => - key.includes('projects') && !( - key.includes('coding-interview') || key.includes('take-home') - ) - ) - .map(key => blocks[key]) - .concat(legacyWithDashedNames) - .map(({ title, challenges, superBlock }) => { - const projectChallengeDashNames = challenges - // challengeIdToName is not available on appMount - .filter(Boolean) - // remove any project intros - .filter(chal => !chal.includes('get-set-for')); - const projectChallenges = projectChallengeDashNames - .map(dashedName => { - const { id, title } = challengeMap[dashedName]; - return { id, title, dashedName }; - }); - return { - projectBlockName: title, - superBlock, - challenges: projectChallenges - }; - }); -} - -export function challengeIdToNameMapSelector(state) { - return getNS(state).challengeIdToName || {}; -} - -export const challengeMapSelector = state => getNS(state).challenge || {}; - -export function makeBlockSelector(block) { - return state => { - const blockMap = getNS(state).block || {}; - return blockMap[block] || {}; - }; -} - -export function makeSuperBlockSelector(name) { - return state => { - const superBlock = getNS(state).superBlock || {}; - return superBlock[name] || {}; - }; -} - -export const isChallengeLoaded = (state, { dashedName }) => - !!challengeMapSelector(state)[dashedName]; - -export const fullBlocksSelector = state => getNS(state).fullBlocks; - -export default composeReducers( - ns, - function metaReducer(state = defaultState, action) { - const { meta } = action; - if (meta && meta.entities) { - if (meta.entities.user) { - return { - ...state, - user: { - ...state.user, - ...meta.entities.user - } - }; - } - return merge({}, state, action.meta.entities); - } - return state; - }, - function entitiesReducer(state = defaultState, action) { - if (getEntityAction(action)) { - const { payload: { username, theme } } = getEntityAction(action); - return { - ...state, - user: { - ...state.user, - [username]: { - ...state.user[username], - theme - } - } - }; - } - return state; - }, - handleActions( - () => ({ - [map.fetchMapUi.complete]: (state, { payload: { entities } }) => - merge({}, state, entities), - [types.addPortfolioItem]: (state, { payload: username }) => ({ - ...state, - user: { - ...state.user, - [username]: { - ...state.user[username], - portfolio: [ - ...state.user[username].portfolio, - emptyPortfolio() - ] - } - } - }), - [types.optoUpdatePortfolio]: ( - state, - { payload: { username, portfolio }} - ) => { - const currentPortfolio = state.user[username].portfolio.slice(0); - const pIndex = findIndex(currentPortfolio, p => p.id === portfolio.id); - const updatedPortfolio = currentPortfolio; - updatedPortfolio[pIndex] = portfolio; - return { - ...state, - user: { - ...state.user, - [username]: { - ...state.user[username], - portfolio: updatedPortfolio - } - } - }; - }, - [types.regresPortfolio]: (state, { payload: { username, id } }) => ({ - ...state, - user: { - ...state.user, - [username]: { - ...state.user[username], - portfolio: state.user[username].portfolio.filter(p => p.id !== id) - } - } - }), - [types.updateMultipleUserFlags]: ( - state, - { payload: { username, flags }} - ) => ({ - ...state, - user: { - ...state.user, - [username]: { - ...state.user[username], - ...flags - } - } - }), - [types.updateUserFlag]: (state, { payload: { username, flag } }) => ({ - ...state, - user: { - ...state.user, - [username]: { - ...state.user[username], - [flag]: !state.user[username][flag] - } - } - }), - [types.updateUserEmail]: (state, { payload: { username, email } }) => ({ - ...state, - user: { - ...state.user, - [username]: { - ...state.user[username], - email - } - } - }), - [types.updateUserLang]: - ( - state, - { - payload: { username, languageTag } - } - ) => ({ - ...state, - user: { - ...state.user, - [username]: { - ...state.user[username], - languageTag - } - } - }), - [types.updateLocalProfileUI]: - ( - state, - { payload: { username, profileUI } } - ) => ({ - ...state, - user: { - ...state.user, - [username]: { - ...state.user[username], - profileUI: { - ...state.user[username].profileUI, - ...profileUI - } - } - } - }) - }), - defaultState - ) -); diff --git a/api-server/common/app/epics.js b/api-server/common/app/epics.js deleted file mode 100644 index 6f11f8f087..0000000000 --- a/api-server/common/app/epics.js +++ /dev/null @@ -1,13 +0,0 @@ -import { epics as app } from './redux'; -import { epics as flash } from './Flash/redux'; -import { epics as map } from './Map/redux'; -import { epics as nav } from './Nav/redux'; -import { epics as settings } from './routes/Settings/redux'; - -export default [ - ...app, - ...flash, - ...map, - ...nav, - ...settings -]; diff --git a/api-server/common/app/index.js b/api-server/common/app/index.js deleted file mode 100644 index 1aa4c9e6a6..0000000000 --- a/api-server/common/app/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export { default as createApp } from './create-app.jsx'; -export { default as App } from './App.jsx'; -export { default as provideStore } from './provide-store.js'; diff --git a/api-server/common/app/index.less b/api-server/common/app/index.less deleted file mode 100644 index 67c084f054..0000000000 --- a/api-server/common/app/index.less +++ /dev/null @@ -1,4 +0,0 @@ -&{ @import "./app.less"; } -&{ @import "./Nav/nav.less"; } -&{ @import "./Flash/flash.less"; } -&{ @import "./routes/index.less"; } diff --git a/api-server/common/app/ns.json b/api-server/common/app/ns.json deleted file mode 100644 index 74999efc4e..0000000000 --- a/api-server/common/app/ns.json +++ /dev/null @@ -1 +0,0 @@ -"app" diff --git a/api-server/common/app/provide-store.js b/api-server/common/app/provide-store.js deleted file mode 100644 index 4df78e5f67..0000000000 --- a/api-server/common/app/provide-store.js +++ /dev/null @@ -1,11 +0,0 @@ -/* eslint-disable react/display-name */ -import React from 'react'; -import { Provider } from 'react-redux'; - -export default function provideStore(Component, store) { - return ( - - - - ); -} diff --git a/api-server/common/app/reducer.js b/api-server/common/app/reducer.js deleted file mode 100644 index 9555f1095a..0000000000 --- a/api-server/common/app/reducer.js +++ /dev/null @@ -1,23 +0,0 @@ -import { combineReducers } from 'berkeleys-redux-utils'; - -import app from './redux'; -import entities from './entities'; -import { reducer as form } from 'redux-form'; -import map from './Map/redux'; -import nav from './Nav/redux'; -import routes from './routes/redux'; -import toasts from './Toasts/redux'; -import flash from './Flash/redux'; - -form.toString = () => 'form'; - -export default combineReducers( - app, - entities, - map, - nav, - routes, - toasts, - flash, - form -); diff --git a/api-server/common/app/redux/fetch-user-epic.js b/api-server/common/app/redux/fetch-user-epic.js deleted file mode 100644 index 512aaf907e..0000000000 --- a/api-server/common/app/redux/fetch-user-epic.js +++ /dev/null @@ -1,42 +0,0 @@ -import { Observable } from 'rx'; -import { ofType, combineEpics } from 'redux-epic'; - -import { getJSON$ } from '../../utils/ajax-stream'; -import { - types, - - fetchUserComplete, - fetchOtherUserComplete, - createErrorObservable, - showSignIn -} from './'; -import { userFound } from '../routes/Profile/redux'; - -function getUserEpic(actions, _, { services }) { - return actions::ofType('' + types.fetchUser) - .flatMap(() => { - return services.readService$({ service: 'user' }) - .filter(({ entities, result }) => entities && !!result) - .map(fetchUserComplete) - .defaultIfEmpty(showSignIn()) - .catch(createErrorObservable); - }); -} - -function getOtherUserEpic(actions$) { - return actions$::ofType(types.fetchOtherUser.start) - .distinctUntilChanged() - .flatMap(({ payload: otherUser }) => { - return getJSON$(`/api/users/get-public-profile?username=${otherUser}`) - .flatMap(response => Observable.of( - fetchOtherUserComplete(response), - userFound(!!response.result) - )) - .catch(createErrorObservable); - }); -} - -export default combineEpics( - getUserEpic, - getOtherUserEpic -); diff --git a/api-server/common/app/redux/index.js b/api-server/common/app/redux/index.js deleted file mode 100644 index 96a2521112..0000000000 --- a/api-server/common/app/redux/index.js +++ /dev/null @@ -1,220 +0,0 @@ -import { flow, identity } from 'lodash'; -import { Observable } from 'rx'; -import { - combineActions, - createAction, - createAsyncTypes, - createTypes, - handleActions -} from 'berkeleys-redux-utils'; -import { createSelector } from 'reselect'; - -import fetchUserEpic from './fetch-user-epic.js'; -import nightModeEpic from './night-mode-epic.js'; - -import { - updateThemeMetacreator, - entitiesSelector -} from '../entities'; -import { utils } from '../Flash/redux'; -import { paramsSelector } from '../Router/redux'; -import { types as map } from '../Map/redux'; - -import ns from '../ns.json'; - -import { themes, invertTheme } from '../../utils/themes.js'; - -export const epics = [ - fetchUserEpic, - nightModeEpic -]; - -export const types = createTypes([ - 'onRouteHome', - - 'appMounted', - 'analytics', - 'updateTitle', - - createAsyncTypes('fetchOtherUser'), - createAsyncTypes('fetchUser'), - 'showSignIn', - - 'handleError', - // used to hit the server - 'hardGoTo', - 'delayedRedirect', - - // night mode - 'toggleNightMode', - createAsyncTypes('postTheme') -], ns); - -const throwIfUndefined = () => { - throw new TypeError('Argument must not be of type `undefined`'); -}; - -// createEventMetaCreator({ -// category: String, -// action: String, -// label?: String, -// value?: Number -// }) => () => Object -export function createEventMetaCreator({ - // categories are features or namespaces of the app (capitalized): - // Map, Nav, Challenges, and so on - category = throwIfUndefined, - // can be a one word the event - // click, play, toggle. - // This is not a hard and fast rule - action = throwIfUndefined, - // any additional information - // when in doubt use redux action type - // or a short sentence describing the action - label, - // used to tack some specific value for a GA event - value -} = throwIfUndefined) { - return () => ({ - analytics: { - type: 'event', - category, - action, - label, - value - } - }); -} - -export const onRouteHome = createAction(types.onRouteHome); -export const appMounted = createAction(types.appMounted); - -// updateTitle(title: String) => Action -export const updateTitle = createAction(types.updateTitle); - -// fetchOtherUser() => Action -// used in combination with fetch-user-epic -// to fetch another users profile -export const fetchOtherUser = createAction(types.fetchOtherUser.start); -export const fetchOtherUserComplete = createAction( - types.fetchOtherUser.complete, - ({ result }) => result, - identity -); - -// fetchUser() => Action -// used in combination with fetch-user-epic -export const fetchUser = createAction(types.fetchUser); -export const fetchUserComplete = createAction( - types.fetchUser.complete, - ({ result }) => result, - identity -); - -export const showSignIn = createAction(types.showSignIn); - -// used when server needs client to redirect -export const delayedRedirect = createAction(types.delayedRedirect); - -// hardGoTo(path: String) => Action -export const hardGoTo = createAction(types.hardGoTo); - -export const createErrorObservable = error => Observable.just({ - type: types.handleError, - error -}); -// use sparingly -// doActionOnError( -// actionCreator: (() => Action|Null) -// ) => (error: Error) => Observable[Action] -export const doActionOnError = actionCreator => error => Observable.of( - { - type: types.handleError, - error - }, - actionCreator() -); - -export const toggleNightMode = createAction( - types.toggleNightMode, - null, - (username, theme) => updateThemeMetacreator(username, invertTheme(theme)) -); -export const postThemeComplete = createAction( - types.postTheme.complete, - null, - utils.createFlashMetaAction -); - -export const postThemeError = createAction( - types.postTheme.error, - null, - (username, theme, err) => ({ - ...updateThemeMetacreator(username, invertTheme(theme)), - ...utils.createFlashMetaAction(err) - }) -); - -const defaultState = { - title: 'Learn To Code | freeCodeCamp', - isSignInAttempted: false, - user: '', - csrfToken: '', - superBlocks: [] -}; - -export const getNS = state => state[ns]; -export const csrfSelector = state => getNS(state).csrfToken; -export const titleSelector = state => getNS(state).title; - -export const signInLoadingSelector = state => !getNS(state).isSignInAttempted; - -export const usernameSelector = state => getNS(state).user || ''; -export const userSelector = createSelector( - state => getNS(state).user, - state => entitiesSelector(state).user, - (username, userMap) => userMap[username] || {} -); - -export const userByNameSelector = state => { - const username = paramsSelector(state).username; - const userMap = entitiesSelector(state).user; - return userMap[username] || {}; -}; - -export const themeSelector = flow( - userSelector, - user => user.theme || themes.default -); - -export const isSignedInSelector = state => !!userSelector(state).username; - -export default handleActions( - () => ({ - [types.updateTitle]: (state, { payload = 'Learn To Code' }) => ({ - ...state, - title: payload + ' | freeCodeCamp' - }), - - [types.fetchUser.complete]: (state, { payload: user }) => ({ - ...state, - user - }), - [map.fetchMapUi.complete]: (state, { payload }) => ({ - ...state, - superBlocks: payload.result.superBlocks - }), - [ - combineActions(types.showSignIn, types.fetchUser.complete) - ]: state => ({ - ...state, - isSignInAttempted: true - }), - [types.delayedRedirect]: (state, { payload }) => ({ - ...state, - delayedRedirect: payload - }) - }), - defaultState, - ns -); diff --git a/api-server/common/app/redux/night-mode-epic.js b/api-server/common/app/redux/night-mode-epic.js deleted file mode 100644 index 90e97e75f0..0000000000 --- a/api-server/common/app/redux/night-mode-epic.js +++ /dev/null @@ -1,64 +0,0 @@ -import _ from 'lodash'; -import { Observable } from 'rx'; -import { ofType } from 'redux-epic'; -import store from '@freecodecamp/store'; - -import { themes } from '../../utils/themes.js'; -import { postJSON$ } from '../../utils/ajax-stream.js'; -import { - csrfSelector, - postThemeComplete, - postThemeError, - themeSelector, - types, - usernameSelector -} from './index.js'; - -function persistTheme(theme) { - store.set('fcc-theme', theme); -} - -export default function nightModeEpic( - actions, - { getState }, - { document } -) { - return Observable.of(document) - // if document is undefined we do nothing (ssr trap) - .filter(Boolean) - .flatMap(({ body }) => { - const toggleBodyClass = actions - ::ofType( - types.fetchUser.complete, - types.toggleNightMode, - types.postTheme.complete, - types.postTheme.error - ) - .map(_.flow(getState, themeSelector)) - // catch existing night mode users - .do(persistTheme) - .do(theme => { - if (theme === themes.night) { - body.classList.add(themes.night); - } else { - body.classList.remove(themes.night); - } - }) - .ignoreElements(); - - const postThemeEpic = actions::ofType(types.toggleNightMode) - .debounce(250) - .flatMapLatest(() => { - const _csrf = csrfSelector(getState()); - const theme = themeSelector(getState()); - const username = usernameSelector(getState()); - return postJSON$('/update-my-theme', { _csrf, theme }) - .map(postThemeComplete) - .catch(err => { - return Observable.of(postThemeError(username, theme, err)); - }); - }); - - return Observable.merge(toggleBodyClass, postThemeEpic); - }); -} diff --git a/api-server/common/app/redux/utils.js b/api-server/common/app/redux/utils.js deleted file mode 100644 index 87fdb21b91..0000000000 --- a/api-server/common/app/redux/utils.js +++ /dev/null @@ -1,9 +0,0 @@ -import flowRight from 'lodash/flowRight'; -import { createNameIdMap } from '../../utils/map.js'; - -export const shapeChallenges = flowRight( - entities => ({ - ...entities, - ...createNameIdMap(entities) - }) -); diff --git a/api-server/common/app/routes-map.js b/api-server/common/app/routes-map.js deleted file mode 100644 index be99909c26..0000000000 --- a/api-server/common/app/routes-map.js +++ /dev/null @@ -1,17 +0,0 @@ -import reduce from 'lodash/reduce'; -import { types } from './redux'; -import routes from './routes'; - -export default { - ...reduce(routes, (routes, route, type) => { - let newRoute; - if (typeof route === 'string') { - newRoute = route; - } else { - newRoute = { ...route, path: route.path }; - } - routes[type] = newRoute; - return routes; - }, {}), - [types.routeOnHome]: '/' -}; diff --git a/api-server/common/app/routes/Admin/README.md b/api-server/common/app/routes/Admin/README.md deleted file mode 100644 index 7d48e2a6bd..0000000000 --- a/api-server/common/app/routes/Admin/README.md +++ /dev/null @@ -1 +0,0 @@ -in case we ever want an admin panel diff --git a/api-server/common/app/routes/Profile/Profile.jsx b/api-server/common/app/routes/Profile/Profile.jsx deleted file mode 100644 index eaddf002f7..0000000000 --- a/api-server/common/app/routes/Profile/Profile.jsx +++ /dev/null @@ -1,267 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { - Alert, - Button, - Grid -} from 'react-bootstrap'; - - -import { - updateTitle, - isSignedInSelector, - signInLoadingSelector, - usernameSelector, - userByNameSelector, - fetchOtherUser -} from '../../redux'; -import { userFoundSelector } from './redux'; -import { paramsSelector } from '../../Router/redux'; -import ns from './ns.json'; -import ChildContainer from '../../Child-Container.jsx'; -import { Link } from '../../Router'; -import CamperHOC from './components/CamperHOC.jsx'; -import Portfolio from './components/Portfolio.jsx'; -import Certificates from './components/Certificates.jsx'; -import Timeline from './components/Timeline.jsx'; -import HeatMap from './components/HeatMap.jsx'; -import { FullWidthRow, Loader } from '../../helperComponents'; - -const mapStateToProps = createSelector( - isSignedInSelector, - userByNameSelector, - paramsSelector, - usernameSelector, - signInLoadingSelector, - userFoundSelector, - ( - isSignedIn, - { - username: requestedUsername, - profileUI: { - isLocked, - showAbout, - showCerts, - showHeatMap, - showLocation, - showName, - showPoints, - showPortfolio, - showTimeLine - } = {} - }, - { username: paramsUsername }, - currentUsername, - showLoading, - isUserFound - ) => ({ - isSignedIn, - currentUsername, - isCurrentUserProfile: paramsUsername === currentUsername, - isUserFound, - fetchOtherUserCompleted: typeof isUserFound === 'boolean', - paramsUsername, - requestedUsername, - isLocked, - showLoading, - showAbout, - showCerts, - showHeatMap, - showLocation, - showName, - showPoints, - showPortfolio, - showTimeLine - }) -); - -const mapDispatchToProps = { - fetchOtherUser, - updateTitle -}; - -const propTypes = { - currentUsername: PropTypes.string, - fetchOtherUser: PropTypes.func.isRequired, - fetchOtherUserCompleted: PropTypes.bool, - isCurrentUserProfile: PropTypes.bool, - isLocked: PropTypes.bool, - isSignedIn: PropTypes.bool, - isUserFound: PropTypes.bool, - paramsUsername: PropTypes.string, - requestedUsername: PropTypes.string, - showAbout: PropTypes.bool, - showCerts: PropTypes.bool, - showHeatMap: PropTypes.bool, - showLoading: PropTypes.bool, - showLocation: PropTypes.bool, - showName: PropTypes.bool, - showPoints: PropTypes.bool, - showPortfolio: PropTypes.bool, - showTimeLine: PropTypes.bool, - updateTitle: PropTypes.func.isRequired -}; - -class Profile extends Component { - - componentWillMount() { - this.props.updateTitle('Profile'); - } - componentDidUpdate() { - const { requestedUsername, currentUsername, paramsUsername } = this.props; - if (!requestedUsername && paramsUsername !== currentUsername) { - this.props.fetchOtherUser(paramsUsername); - } - } - - renderRequestedProfile() { - const { - fetchOtherUserCompleted, - isLocked, - isUserFound, - isCurrentUserProfile, - paramsUsername, - showAbout, - showLocation, - showName, - showPoints, - showHeatMap, - showCerts, - showPortfolio, - showTimeLine - } = this.props; - const takeMeToChallenges = ( - - - - ); - if (isLocked) { - return ( -
    -

    - { - `${paramsUsername} has not made their profile public. ` - } -

    - -

    - { - 'In order to view their freeCodeCamp certifications, ' + - 'they need to make their profile public' - } -

    -
    - { takeMeToChallenges } -
    - ); - } - if (!isCurrentUserProfile && (fetchOtherUserCompleted && !isUserFound)) { - return ( -
    - -

    - { `We could not find a user by the name of "${paramsUsername}"` } -

    -
    - { takeMeToChallenges } -
    - ); - } - return ( -
    - - { showHeatMap ? : null } - { showCerts ? : null } - { showPortfolio ? : null } - { showTimeLine ? : null } -
    - ); - } - - renderReportUserButton() { - const { - isSignedIn, - fetchOtherUserCompleted, - isCurrentUserProfile, - isUserFound, - paramsUsername - } = this.props; - - return ( - !isSignedIn || - isCurrentUserProfile || - (fetchOtherUserCompleted && !isUserFound) - ) ? - null : - ( - - - - ); - } - - renderSettingsLink() { - const { isCurrentUserProfile } = this.props; - return isCurrentUserProfile ? - - - - - : - null; - } - - render() { - const { - isCurrentUserProfile, - showLoading, - fetchOtherUserCompleted - } = this.props; - if (isCurrentUserProfile && showLoading) { - return ; - } - if (!isCurrentUserProfile && !fetchOtherUserCompleted) { - return ; - } - return ( - - - { this.renderSettingsLink() } - { this.renderRequestedProfile() } - { this.renderReportUserButton() } - - - ); - } -} - -Profile.displayName = 'Profile'; -Profile.propTypes = propTypes; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(Profile); diff --git a/api-server/common/app/routes/Profile/components/CamperHOC.jsx b/api-server/common/app/routes/Profile/components/CamperHOC.jsx deleted file mode 100644 index beadb648a2..0000000000 --- a/api-server/common/app/routes/Profile/components/CamperHOC.jsx +++ /dev/null @@ -1,77 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { createSelector } from 'reselect'; -import { connect } from 'react-redux'; - -import { userByNameSelector } from '../../../redux'; -import Camper from '../../Settings/components/Camper.jsx'; - -const mapStateToProps = createSelector( - userByNameSelector, - ({ - name, - username, - location, - points, - picture, - about, - yearsTopContributor - }) => ({ - name, - username, - location, - points, - picture, - about, - yearsTopContributor - }) -); - -const propTypes = { - about: PropTypes.string, - location: PropTypes.string, - name: PropTypes.string, - picture: PropTypes.string, - points: PropTypes.number, - showAbout: PropTypes.bool, - showLocation: PropTypes.bool, - showName: PropTypes.bool, - showPoints: PropTypes.bool, - username: PropTypes.string, - yearsTopContributor: PropTypes.array -}; - -function CamperHOC({ - name, - username, - location, - points, - picture, - about, - yearsTopContributor, - showAbout, - showLocation, - showName, - showPoints -}) { - - return ( -
    - -
    -
    - ); -} - -CamperHOC.displayName = 'CamperHOC'; -CamperHOC.propTypes = propTypes; - -export default connect(mapStateToProps)(CamperHOC); diff --git a/api-server/common/app/routes/Profile/components/Certificates.jsx b/api-server/common/app/routes/Profile/components/Certificates.jsx deleted file mode 100644 index 0905163856..0000000000 --- a/api-server/common/app/routes/Profile/components/Certificates.jsx +++ /dev/null @@ -1,183 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import _ from 'lodash'; -import { createSelector } from 'reselect'; -import { connect } from 'react-redux'; -import { - Button, - Row, - Col -} from 'react-bootstrap'; - -import { FullWidthRow } from '../../../helperComponents'; - -import { userByNameSelector } from '../../../redux'; - -const mapStateToProps = createSelector( - userByNameSelector, - ({ - isRespWebDesignCert, - is2018DataVisCert, - isFrontEndLibsCert, - isJsAlgoDataStructCert, - isApisMicroservicesCert, - isInfosecQaCert, - isFrontEndCert, - isBackEndCert, - isDataVisCert, - isFullStackCert, - is2018FullStackCert, - username - }) => ({ - username, - hasModernCert: ( - isRespWebDesignCert || - is2018DataVisCert || - isFrontEndLibsCert || - isJsAlgoDataStructCert || - isApisMicroservicesCert || - isInfosecQaCert - ), - hasLegacyCert: (isFrontEndCert || isBackEndCert || isDataVisCert), - currentCerts: [ - { - show: is2018FullStackCert, - title: 'Full Stack Certification', - showURL: '2018-full-stack' - }, - { - show: isRespWebDesignCert, - title: 'Responsive Web Design Certification', - showURL: 'responsive-web-design' - }, - { - show: isJsAlgoDataStructCert, - title: 'JavaScript Algorithms and Data Structures Certification', - showURL: 'javascript-algorithms-and-data-structures' - }, - { - show: isFrontEndLibsCert, - title: 'Front End Libraries Certification', - showURL: 'front-end-libraries' - }, - { - show: is2018DataVisCert, - title: 'Data Visualization Certification', - showURL: 'data-visualization' - }, - { - show: isApisMicroservicesCert, - title: 'APIs and Microservices Certification', - showURL: 'apis-and-microservices' - }, - { - show: isInfosecQaCert, - title: 'Information Security and Quality Assurance Certification', - showURL: 'information-security-and-quality-assurance' - } - ], - legacyCerts: [ - { - show: isFullStackCert, - title: 'Full Stack Certification', - showURL: 'legacy-full-stack' - }, - { - show: isFrontEndCert, - title: 'Front End Certification', - showURL: 'legacy-front-end' - }, - { - show: isBackEndCert, - title: 'Back End Certification', - showURL: 'legacy-back-end' - }, - { - show: isDataVisCert, - title: 'Data Visualization Certification', - showURL: 'legacy-data-visualization' - } - ] - }) -); - -function mapDispatchToProps() { - return {}; -} - -const certArrayTypes = PropTypes.arrayOf( - PropTypes.shape({ - show: PropTypes.bool, - title: PropTypes.string, - showURL: PropTypes.string - }) -); - -const propTypes = { - currentCerts: certArrayTypes, - hasLegacyCert: PropTypes.bool, - hasModernCert: PropTypes.bool, - legacyCerts: certArrayTypes, - username: PropTypes.string -}; - -function renderCertShow(username, cert) { - return cert.show ? ( - - - - - - ) : - null; -} - -function Certificates({ - currentCerts, - legacyCerts, - hasLegacyCert, - hasModernCert, - username -}) { - const renderCertShowWithUsername = _.curry(renderCertShow)(username); - return ( -
    - -

    freeCodeCamp Certifications

    -
    - { - hasModernCert ? - currentCerts.map(renderCertShowWithUsername) : -

    - No certifications have been earned under the current curriculum -

    - } - { - hasLegacyCert ? -
    -
    -

    Legacy Certifications

    -
    - { - legacyCerts.map(renderCertShowWithUsername) - } -
    : - null - } -
    -
    -
    - ); -} - -Certificates.propTypes = propTypes; -Certificates.displayName = 'Certificates'; - -export default connect(mapStateToProps, mapDispatchToProps)(Certificates); diff --git a/api-server/common/app/routes/Profile/components/HeatMap.jsx b/api-server/common/app/routes/Profile/components/HeatMap.jsx deleted file mode 100644 index 4629748b08..0000000000 --- a/api-server/common/app/routes/Profile/components/HeatMap.jsx +++ /dev/null @@ -1,125 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import d3 from 'react-d3'; -import CalHeatMap from 'cal-heatmap'; -import { createSelector } from 'reselect'; -import { connect } from 'react-redux'; -import { Helmet } from 'react-helmet'; -import differenceInCalendarMonths from 'date-fns/difference_in_calendar_months'; - -import { FullWidthRow } from '../../../helperComponents'; -import { userByNameSelector } from '../../../redux'; - -function ensureD3() { - // CalHeatMap requires d3 to be available on window - if (typeof window !== 'undefined') { - if ('d3' in window) { - return; - } else { - window.d3 = d3; - } - return; - } - return; -} - -const mapStateToProps = createSelector( - userByNameSelector, - ({ calendar, streak }) => ({ calendar, streak }) -); - -const propTypes = { - calendar: PropTypes.object, - streak: PropTypes.shape({ - current: PropTypes.number, - longest: PropTypes.number - }) -}; - -class HeatMap extends Component { - constructor(props) { - super(props); - - this.renderMap = this.renderMap.bind(this); - } - componentDidMount() { - ensureD3(); - this.renderMap(); - } - - renderMap() { - const { calendar = {} } = this.props; - if (Object.keys(calendar).length === 0) { - return null; - } - const today = new Date(); - const cal = new CalHeatMap(); - const rectSelector = '#cal-heatmap > svg > svg.graph-legend > g > rect.r'; - const calLegendTitles = ['0 items', '1 item', '2 items', '3 or more items']; - const firstTS = Object.keys(calendar)[0]; - const sevenMonths = 1000 * 60 * 60 * 24 * 210; - // start should not be earlier than 7 months before now: - let start = (firstTS * 1000 + sevenMonths < Date.now()) - ? new Date(Date.now() - sevenMonths) - : new Date(firstTS * 1000); - const monthsSinceFirstActive = differenceInCalendarMonths( - today, - start - ); - cal.init({ - itemSelector: '#cal-heatmap', - domain: 'month', - subDomain: 'day', - domainDynamicDimension: true, - domainGutter: 5, - data: calendar, - cellSize: 15, - cellRadius: 3, - cellPadding: 2, - tooltip: true, - range: monthsSinceFirstActive < 12 ? monthsSinceFirstActive + 1 : 12, - start, - legendColors: ['#cccccc', '#006400'], - legend: [1, 2, 3], - label: { - position: 'top' - } - }); - calLegendTitles.forEach(function(title, i) { - document - .querySelector(rectSelector + (i + 1).toString() + '> title') - .innerHTML = title; - }); - return null; - } - - render() { - const { streak = {} } = this.props; - return ( -
    - - - - -
    - - -
    - - Longest Streak: { streak.longest || 1 } - - - Current Streak: { streak.current || 1 } - -
    -
    -
    -
    - ); - } -} - -HeatMap.displayName = 'HeatMap'; -HeatMap.propTypes = propTypes; - -export default connect(mapStateToProps)(HeatMap); diff --git a/api-server/common/app/routes/Profile/components/Portfolio.jsx b/api-server/common/app/routes/Profile/components/Portfolio.jsx deleted file mode 100644 index 76a66bd95d..0000000000 --- a/api-server/common/app/routes/Profile/components/Portfolio.jsx +++ /dev/null @@ -1,74 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { createSelector } from 'reselect'; -import { connect } from 'react-redux'; -import { Thumbnail, Media } from 'react-bootstrap'; - -import { FullWidthRow } from '../../../helperComponents'; -import { userByNameSelector } from '../../../redux'; - -const mapStateToProps = createSelector( - userByNameSelector, - ({ portfolio }) => ({ portfolio }) -); - -const propTypes = { - portfolio: PropTypes.arrayOf( - PropTypes.shape({ - description: PropTypes.string, - id: PropTypes.string, - image: PropTypes.string, - title: PropTypes.string, - url: PropTypes.string - }) - ) -}; - -function Portfolio({ portfolio = [] }) { - if (!portfolio.length) { - return null; - } - return ( -
    - -

    Portfolio

    - { - portfolio.map(({ title, url, image, description, id}) => ( - - - { - image && ( - - - - ) - - } - - - - - { title } - - -

    - { description } -

    -
    -
    - )) - } -
    -
    -
    - ); -} - -Portfolio.displayName = 'Portfolio'; -Portfolio.propTypes = propTypes; - -export default connect(mapStateToProps)(Portfolio); diff --git a/api-server/common/app/routes/Profile/components/SocialIcons.jsx b/api-server/common/app/routes/Profile/components/SocialIcons.jsx deleted file mode 100644 index f1010b33f7..0000000000 --- a/api-server/common/app/routes/Profile/components/SocialIcons.jsx +++ /dev/null @@ -1,143 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { createSelector } from 'reselect'; -import { connect } from 'react-redux'; -import { Row, Col } from 'react-bootstrap'; -import FontAwesomeIcon from '@fortawesome/react-fontawesome'; -import { - faLinkedin, - faGithub, - faTwitter -} from '@fortawesome/free-brands-svg-icons'; -import { faLink } from '@fortawesome/free-solid-svg-icons'; - -import { userByNameSelector } from '../../../redux'; - -const propTypes = { - email: PropTypes.string, - githubProfile: PropTypes.string, - isGithub: PropTypes.bool, - isLinkedIn: PropTypes.bool, - isTwitter: PropTypes.bool, - isWebsite: PropTypes.bool, - linkedIn: PropTypes.string, - show: PropTypes.bool, - twitter: PropTypes.string, - website: PropTypes.string -}; - -const mapStateToProps = createSelector( - userByNameSelector, - ({ - githubProfile, - isLinkedIn, - isGithub, - isTwitter, - isWebsite, - linkedIn, - twitter, - website - }) => ({ - githubProfile, - isLinkedIn, - isGithub, - isTwitter, - isWebsite, - linkedIn, - show: (isLinkedIn || isGithub || isTwitter || isWebsite), - twitter, - website - }) -); - -function mapDispatchToProps() { - return {}; -} - -function LinkedInIcon(linkedIn) { - return ( - - - - ); -} - -function GithubIcon(ghURL) { - return ( - - - - ); -} - -function WebsiteIcon(website) { - return ( - - - - ); -} - -function TwitterIcon(handle) { - return ( - - - - ); -} - -function SocialIcons(props) { - const { - githubProfile, - isLinkedIn, - isGithub, - isTwitter, - isWebsite, - linkedIn, - show, - twitter, - website - } = props; - if (!show) { - return null; - } - return ( - - - { - isLinkedIn ? LinkedInIcon(linkedIn) : null - } - { - isGithub ? GithubIcon(githubProfile) : null - } - { - isWebsite ? WebsiteIcon(website) : null - } - { - isTwitter ? TwitterIcon(twitter) : null - } - - - ); -} - -SocialIcons.displayName = 'SocialIcons'; -SocialIcons.propTypes = propTypes; - -export default connect(mapStateToProps, mapDispatchToProps)(SocialIcons); diff --git a/api-server/common/app/routes/Profile/components/Timeline.jsx b/api-server/common/app/routes/Profile/components/Timeline.jsx deleted file mode 100644 index c340ee8acc..0000000000 --- a/api-server/common/app/routes/Profile/components/Timeline.jsx +++ /dev/null @@ -1,173 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { createSelector } from 'reselect'; -import { connect } from 'react-redux'; -import format from 'date-fns/format'; -import { find, reverse, sortBy } from 'lodash'; -import { - Button, - Modal, - Table -} from 'react-bootstrap'; - -import { challengeIdToNameMapSelector } from '../../../entities'; -import { userByNameSelector } from '../../../redux'; -import { homeURL } from '../../../../utils/constantStrings.json'; -import blockNameify from '../../../utils/blockNameify'; -import { Link } from '../../../Router'; -import { FullWidthRow } from '../../../helperComponents'; -import SolutionViewer from '../../Settings/components/SolutionViewer.jsx'; - -const mapStateToProps = createSelector( - challengeIdToNameMapSelector, - userByNameSelector, - ( - idToNameMap, - { completedChallenges: completedMap = [], username } - ) => ({ - completedMap, - idToNameMap, - username - }) -); - -const propTypes = { - completedMap: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.string, - completedDate: PropTypes.number, - challengeType: PropTypes.number, - solution: PropTypes.string, - files: PropTypes.shape({ - ext: PropTypes.string, - contents: PropTypes.string - }) - }) - ), - idToNameMap: PropTypes.objectOf(PropTypes.string), - username: PropTypes.string -}; - -class Timeline extends PureComponent { - constructor(props) { - super(props); - - this.state = { - solutionToView: null, - solutionOpen: false - }; - - this.closeSolution = this.closeSolution.bind(this); - this.renderCompletion = this.renderCompletion.bind(this); - this.viewSolution = this.viewSolution.bind(this); - } - - renderCompletion(completed) { - const { idToNameMap } = this.props; - const { id, completedDate } = completed; - const challengeDashedName = idToNameMap[id]; - return ( - - - - { blockNameify(challengeDashedName) } - - - - - - - - ); - } - viewSolution(id) { - this.setState(state => ({ - ...state, - solutionToView: id, - solutionOpen: true - })); - } - - closeSolution() { - this.setState(state => ({ - ...state, - solutionToView: null, - solutionOpen: false - })); - } - - render() { - const { completedMap, idToNameMap, username } = this.props; - const { solutionToView: id, solutionOpen } = this.state; - if (!Object.keys(idToNameMap).length) { - return null; - } - return ( - -

    Timeline

    - { - completedMap.length === 0 ? -

    - No challenges have been completed yet.  - - Get started here. - -

    : - - - - - - - - - { - reverse( - sortBy( - completedMap, - [ 'completedDate' ] - ).filter(({id}) => id in idToNameMap) - ) - .map(this.renderCompletion) - } - -
    ChallengeCompleted
    - } - { - id && - - - - { `${username}'s Solution to ${blockNameify(idToNameMap[id])}` } - - - - completedId === id) - } - /> - - - - - - } -
    - ); - } -} - - -Timeline.displayName = 'Timeline'; -Timeline.propTypes = propTypes; - -export default connect(mapStateToProps)(Timeline); diff --git a/api-server/common/app/routes/Profile/index.js b/api-server/common/app/routes/Profile/index.js deleted file mode 100644 index 2ae2f541bf..0000000000 --- a/api-server/common/app/routes/Profile/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import { types } from './redux'; - -export { default } from './Profile.jsx'; - -export const routes = { - [types.onRouteProfile]: '/:username' -}; diff --git a/api-server/common/app/routes/Profile/ns.json b/api-server/common/app/routes/Profile/ns.json deleted file mode 100644 index 2c3185e42e..0000000000 --- a/api-server/common/app/routes/Profile/ns.json +++ /dev/null @@ -1 +0,0 @@ -"profile" diff --git a/api-server/common/app/routes/Profile/profile.less b/api-server/common/app/routes/Profile/profile.less deleted file mode 100644 index 7c600634d2..0000000000 --- a/api-server/common/app/routes/Profile/profile.less +++ /dev/null @@ -1,102 +0,0 @@ -// should be the same as the filename and ./ns.json -@ns: profile; - -.avatar-container { - margin: 0 auto; - text-align: center; - .avatar { - height: 180px; - } -} - -.solution-viewer { - background-color: #fff; -} - -.bigger-text { - font-size: 1.7rem; -} - -.bio { - text-align: center; -} - -.name, .location, .bio, .points, .yearsTopContributor { - font-size: 2rem; - margin-bottom: 0; -} - -.@{ns}-container { - - a:hover { - text-decoration: none; - text-decoration-line: none; - } - - .full-size { - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - } - .row { - margin-bottom: 15px; - } - - .social-media-icons { - a { - &:first-child { - margin-left: 0; - } - - margin-left: 32px; - } - } - - h2, h3 { - &.name, &.username, &.points, &.location { - margin-top: 0; - margin-bottom: 0; - } - } - - .portfolio-description { - height: 62px; - } - - .timeline-container { - display: flex; - align-items: center; - } - - #cal-heatmap { - display: flex; - justify-content: center; - } - - .streak-container { - display: flex; - justify-content: space-around; - align-items: center; - font-size: 18px; - } - - .streak { - color: @brand-primary; - - strong { - color: #333; - } - } - -} - -.night { - .@{ns}-container { - .streak { - strong { - color: #ccc; - } - } - } -} \ No newline at end of file diff --git a/api-server/common/app/routes/Profile/redux/index.js b/api-server/common/app/routes/Profile/redux/index.js deleted file mode 100644 index 920fb930b3..0000000000 --- a/api-server/common/app/routes/Profile/redux/index.js +++ /dev/null @@ -1,31 +0,0 @@ -import { - createAction, - createTypes -} from 'berkeleys-redux-utils'; - -import ns from '../ns.json'; -import handleActions from 'berkeleys-redux-utils/lib/handle-actions'; - -export const types = createTypes([ - 'onRouteProfile', - 'userFound' -], 'profile'); - -export const onRouteProfile = createAction(types.onRouteProfile); -export const userFound = createAction(types.userFound); -const initialState = { - isUserFound: null -}; - -export const userFoundSelector = state => state[ns].isUserFound; - -export default handleActions(() => ( - { - [types.userFound]: (state, { payload }) => ({ - ...state, - isUserFound: payload - }) - }), - initialState, - ns -); diff --git a/api-server/common/app/routes/Settings/Settings.jsx b/api-server/common/app/routes/Settings/Settings.jsx deleted file mode 100644 index 7adfd5d25e..0000000000 --- a/api-server/common/app/routes/Settings/Settings.jsx +++ /dev/null @@ -1,122 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { Button } from 'react-bootstrap'; - -import { FullWidthRow, Spacer, Loader } from '../../helperComponents'; -import AboutSettings from './components/About-Settings.jsx'; -import InternetSettings from './components/Internet-Settings.jsx'; -import EmailSettings from './components/Email-Settings.jsx'; -import DangerZone from './components/DangerZone.jsx'; -import CertificationSettings from './components/Cert-Settings.jsx'; -import PortfolioSettings from './components/Portfolio-Settings.jsx'; -import PrivacySettings from './components/Privacy-Settings.jsx'; -import Honesty from './components/Honesty.jsx'; - -import { - toggleNightMode, - updateTitle, - - signInLoadingSelector, - usernameSelector, - themeSelector, - hardGoTo -} from '../../redux'; - -const mapStateToProps = createSelector( - usernameSelector, - themeSelector, - signInLoadingSelector, - ( - username, - theme, - showLoading, - ) => ({ - showLoading, - username - }) -); - -const mapDispatchToProps = { - hardGoTo, - toggleNightMode, - updateTitle -}; - -const propTypes = { - hardGoTo: PropTypes.func.isRequired, - showLoading: PropTypes.bool, - updateTitle: PropTypes.func.isRequired, - username: PropTypes.string -}; - -export class Settings extends React.Component { - componentWillMount() { - this.props.updateTitle('Settings'); - } - - componentWillReceiveProps({ username, showLoading, hardGoTo }) { - if (!username && !showLoading) { - hardGoTo('/signup'); - } - } - - render() { - const { - showLoading, - username - } = this.props; - if (!username && showLoading) { - return ; - } - return ( -
    - - - - -

    { `Account Settings for ${username}` }

    - - - - - - - - - - - - - - - -
    - ); - } -} - -Settings.displayName = 'Settings'; -Settings.propTypes = propTypes; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(Settings); diff --git a/api-server/common/app/routes/Settings/ShowSettings.jsx b/api-server/common/app/routes/Settings/ShowSettings.jsx deleted file mode 100644 index 1bcd62c87b..0000000000 --- a/api-server/common/app/routes/Settings/ShowSettings.jsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { Grid } from 'react-bootstrap'; - -import ns from './ns.json'; -import { showUpdateEmailViewSelector } from './redux'; -import Settings from './Settings.jsx'; -import UpdateEmail from './routes/update-email'; -import ChildContainer from '../../Child-Container.jsx'; - -const mapStateToProps = state => ({ - showUpdateEmailView: showUpdateEmailViewSelector(state) -}); - -const propTypes = { - showUpdateEmailView: PropTypes.bool -}; - -export function ShowSettings({ showUpdateEmailView }) { - let ChildComponent = Settings; - - if (showUpdateEmailView) { - ChildComponent = UpdateEmail; - } - - return ( - - - - - - ); -} - -ShowSettings.displayName = 'ShowSettings'; -ShowSettings.propTypes = propTypes; - -export default connect( - mapStateToProps -)(ShowSettings); diff --git a/api-server/common/app/routes/Settings/Toggle-Button/Toggle-Button.jsx b/api-server/common/app/routes/Settings/Toggle-Button/Toggle-Button.jsx deleted file mode 100644 index 76f58c14ac..0000000000 --- a/api-server/common/app/routes/Settings/Toggle-Button/Toggle-Button.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { ToggleButtonGroup as BSBG, ToggleButton as TB } from 'react-bootstrap'; - -import ns from './ns.json'; - -const propTypes = { - name: PropTypes.string.isRequired, - offLabel: PropTypes.string, - onChange: PropTypes.func.isRequired, - onLabel: PropTypes.string, - value: PropTypes.bool.isRequired -}; - -export default function ToggleButton({ - name, - onChange, - value, - onLabel = 'On', - offLabel = 'Off' -}) { - return ( -
    - - - { onLabel } - - - { offLabel } - - -
    - ); -} - -ToggleButton.displayName = 'ToggleButton'; -ToggleButton.propTypes = propTypes; diff --git a/api-server/common/app/routes/Settings/Toggle-Button/index.js b/api-server/common/app/routes/Settings/Toggle-Button/index.js deleted file mode 100644 index 1428299d5f..0000000000 --- a/api-server/common/app/routes/Settings/Toggle-Button/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './Toggle-Button.jsx'; diff --git a/api-server/common/app/routes/Settings/Toggle-Button/ns.json b/api-server/common/app/routes/Settings/Toggle-Button/ns.json deleted file mode 100644 index d2fa2a0525..0000000000 --- a/api-server/common/app/routes/Settings/Toggle-Button/ns.json +++ /dev/null @@ -1 +0,0 @@ -"toggle" diff --git a/api-server/common/app/routes/Settings/Toggle-Button/toggle.less b/api-server/common/app/routes/Settings/Toggle-Button/toggle.less deleted file mode 100644 index 4ff123406d..0000000000 --- a/api-server/common/app/routes/Settings/Toggle-Button/toggle.less +++ /dev/null @@ -1,21 +0,0 @@ -@ns: toggle; - -.@{ns}-container > .btn-group { - min-width: 180px; - float: right; - .btn { - margin-top: 20px; - } - .btn[disabled] { - opacity: 1; - } - @media (max-width: 768px) { - float: none; - .btn { - margin-top: 10px; - margin-bottom: 20px; - } - } -} - - diff --git a/api-server/common/app/routes/Settings/components/About-Settings.jsx b/api-server/common/app/routes/Settings/components/About-Settings.jsx deleted file mode 100644 index 214722f5e2..0000000000 --- a/api-server/common/app/routes/Settings/components/About-Settings.jsx +++ /dev/null @@ -1,217 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { createSelector } from 'reselect'; -import { bindActionCreators } from 'redux'; -import { reduxForm } from 'redux-form'; -import { - Nav, - NavItem -} from 'react-bootstrap'; - -import { FullWidthRow, Spacer } from '../../../helperComponents'; -import ThemeSettings from './ThemeSettings.jsx'; -import Camper from './Camper.jsx'; -import UsernameSettings from './UsernameSettings.jsx'; -import { userSelector, toggleNightMode } from '../../../redux'; -import { updateUserBackend } from '../redux'; -import { - BlockSaveButton, - BlockSaveWrapper, - FormFields, - maxLength, - validURL -} from '../formHelpers'; - -const max288Char = maxLength(288); - -const mapStateToProps = createSelector( - userSelector, - ( - { - about, - location, - name, - picture, - points, - theme, - username - }, - ) => ({ - about, - currentTheme: theme, - initialValues: { name, location, about, picture }, - location, - name, - picture, - points, - username - }) -); - -const formFields = [ 'name', 'location', 'picture', 'about' ]; - -function validator(values) { - const errors = {}; - const { - about, - picture - } = values; - errors.about = max288Char(about); - errors.picutre = validURL(picture); - -} - -function mapDispatchToProps(dispatch) { - return bindActionCreators({ - toggleNightMode, - updateUserBackend - }, dispatch); -} - -const propTypes = { - about: PropTypes.string, - currentTheme: PropTypes.string, - fields: PropTypes.object, - handleSubmit: PropTypes.func.isRequired, - location: PropTypes.string, - name: PropTypes.string, - picture: PropTypes.string, - points: PropTypes.number, - toggleNightMode: PropTypes.func.isRequired, - updateUserBackend: PropTypes.func.isRequired, - username: PropTypes.string -}; - -class AboutSettings extends PureComponent { - constructor(props) { - super(props); - this.handleSubmit = this.handleSubmit.bind(this); - this.handleTabSelect = this.handleTabSelect.bind(this); - this.renderEdit = this.renderEdit.bind(this); - this.renderPreview = this.renderPreview.bind(this); - - this.state = { - view: 'edit' - }; - this.show = { - edit: this.renderEdit, - preview: this.renderPreview - }; - } - - handleSubmit(values) { - this.props.updateUserBackend(values); - } - - handleTabSelect(key) { - this.setState(state => ({ - ...state, - view: key - })); - } - - renderEdit() { - const { fields } = this.props; - const options = { - types: { - about: 'textarea', - picture: 'url' - } - }; - return ( -
    - -
    - ); - } - - renderPreview() { - const { - fields: { - picture: { value: picture }, - name: { value: name }, - location: { value: location }, - about: { value: about } - }, - points, - username - } = this.props; - return ( - - ); - } - - render() { - const { - currentTheme, - fields: { _meta: { allPristine } }, - handleSubmit, - toggleNightMode, - username - } = this.props; - const { view } = this.state; - - const toggleTheme = () => toggleNightMode(username, currentTheme); - return ( -
    - - - - -
    - -
    - { - this.show[view]() - } - - - -
    -
    - - - - -
    - ); - } -} - -AboutSettings.displayName = 'AboutSettings'; -AboutSettings.propTypes = propTypes; - -export default reduxForm( - { - form: 'account-settings', - fields: formFields, - validate: validator - }, - mapStateToProps, - mapDispatchToProps -)(AboutSettings); diff --git a/api-server/common/app/routes/Settings/components/Camper.jsx b/api-server/common/app/routes/Settings/components/Camper.jsx deleted file mode 100644 index f80f444451..0000000000 --- a/api-server/common/app/routes/Settings/components/Camper.jsx +++ /dev/null @@ -1,92 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Col, Row } from 'react-bootstrap'; -import FontAwesomeIcon from '@fortawesome/react-fontawesome'; -import { faAward } from '@fortawesome/free-solid-svg-icons'; - -import SocialIcons from '../../Profile/components/SocialIcons.jsx'; - -const propTypes = { - about: PropTypes.string, - location: PropTypes.string, - name: PropTypes.string, - picture: PropTypes.string, - points: PropTypes.number, - username: PropTypes.string, - yearsTopContributor: PropTypes.array -}; - -function pluralise(word, condition) { - return condition ? word + 's' : word; -} -function joinArray(array) { - return array.reduce((string, item, index, array) => { - if (string.length > 0) { - if (index === array.length - 1) { - return `${string} and ${item}`; - } else { - return `${string}, ${item}`; - } - } else { - return item; - } - }); -} - -function Camper({ - name, - username, - location, - points, - picture, - about, - yearsTopContributor -}) { - - return ( -
    - - - { - - - -

    @{ username }

    - { name &&

    { name }

    } - { location &&

    { location }

    } - { about &&

    { about }

    } - { - typeof points === 'number' ? ( -

    - { `${points} ${pluralise('point', points !== 1)}` } -

    - ) : null - } - { yearsTopContributor.filter(Boolean).length > 0 && - ( -
    -
    -

    - Top Contributor -

    -

    - { joinArray(yearsTopContributor) } -

    -
    - ) - } -
    -
    - ); -} - -Camper.displayName = 'Camper'; -Camper.propTypes = propTypes; - -export default Camper; diff --git a/api-server/common/app/routes/Settings/components/Cert-Settings.jsx b/api-server/common/app/routes/Settings/components/Cert-Settings.jsx deleted file mode 100644 index a23b1e7069..0000000000 --- a/api-server/common/app/routes/Settings/components/Cert-Settings.jsx +++ /dev/null @@ -1,336 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import _ from 'lodash'; -import { createSelector } from 'reselect'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { Button } from 'react-bootstrap'; - -import { FullWidthRow } from '../../../helperComponents'; -import { Form } from '../formHelpers'; -import JSAlgoAndDSForm from './JSAlgoAndDSForm.jsx'; -import SectionHeader from './SectionHeader.jsx'; -import { projectsSelector } from '../../../entities'; -import { claimCert, updateUserBackend } from '../redux'; -import { - userSelector, - hardGoTo, - createErrorObservable -} from '../../../redux'; -import { - buildUserProjectsMap, - jsProjectSuperBlock -} from '../utils/buildUserProjectsMap'; - -const mapStateToProps = createSelector( - userSelector, - projectsSelector, - ( - { - completedChallenges, - isRespWebDesignCert, - is2018DataVisCert, - isFrontEndLibsCert, - isJsAlgoDataStructCert, - isApisMicroservicesCert, - isInfosecQaCert, - isFrontEndCert, - isBackEndCert, - isDataVisCert, - isFullStackCert, - username - }, - projects - ) => { - let modernProjects = projects.filter(p => !p.superBlock.includes('legacy')); - modernProjects.push(modernProjects.shift()); - - return { - allProjects: projects, - legacyProjects: projects.filter(p => p.superBlock.includes('legacy')), - modernProjects: modernProjects, - userProjects: projects - .map(block => buildUserProjectsMap(block, completedChallenges)) - .reduce((projects, current) => ({ - ...projects, - ...current - }), {}), - blockNameIsCertMap: { - 'Responsive Web Design Projects': isRespWebDesignCert, - /* eslint-disable max-len */ - 'JavaScript Algorithms and Data Structures Projects': isJsAlgoDataStructCert, - /* eslint-enable max-len */ - 'Front End Libraries Projects': isFrontEndLibsCert, - 'Data Visualization Projects': is2018DataVisCert, - 'APIs and Microservices Projects': isApisMicroservicesCert, - 'Information Security and Quality Assurance Projects': isInfosecQaCert, - 'Full Stack Certification': isFullStackCert, - 'Legacy Front End Projects': isFrontEndCert, - 'Legacy Back End Projects': isBackEndCert, - 'Legacy Data Visualization Projects': isDataVisCert - }, - username - }; - } -); - -function mapDispatchToProps(dispatch) { - return bindActionCreators({ - claimCert, - createError: createErrorObservable, - hardGoTo, - updateUserBackend - }, dispatch); -} - -const projectsTypes = PropTypes.arrayOf( - PropTypes.shape({ - projectBlockName: PropTypes.string, - challenges: PropTypes.arrayOf( - PropTypes.shape({ - dashedName: PropTypes.string, - id: PropTypes.string, - title: PropTypes.string - }) - ) - }), -); - -const propTypes = { - allProjects: projectsTypes, - blockNameIsCertMap: PropTypes.objectOf(PropTypes.bool), - claimCert: PropTypes.func.isRequired, - createError: PropTypes.func.isRequired, - hardGoTo: PropTypes.func.isRequired, - legacyProjects: projectsTypes, - modernProjects: projectsTypes, - superBlock: PropTypes.string, - updateUserBackend: PropTypes.func.isRequired, - userProjects: PropTypes.objectOf( - PropTypes.oneOfType( - [ - // this is really messy, it should be addressed - // in completedChallenges migration to unify to one type - PropTypes.string, - PropTypes.arrayOf(PropTypes.object), - PropTypes.object - ] - ) - ), - username: PropTypes.string -}; - -class CertificationSettings extends PureComponent { - constructor(props) { - super(props); - this.buildProjectForms = this.buildProjectForms.bind(this); - this.handleSubmit = this.handleSubmit.bind(this); - } - - buildProjectForms({ - projectBlockName, - challenges, - superBlock - }) { - const { - blockNameIsCertMap, - claimCert, - hardGoTo, - userProjects, - username - } = this.props; - const isCertClaimed = blockNameIsCertMap[projectBlockName]; - const challengeTitles = challenges - .map(challenge => challenge.title || 'Unknown Challenge'); - if (superBlock === jsProjectSuperBlock) { - return ( - - ); - } - const options = challengeTitles - .reduce((options, current) => { - options.types[current] = 'url'; - return options; - }, { types: {} }); - - options.types.id = 'hidden'; - options.placeholder = false; - - const userValues = userProjects[superBlock] || {}; - - if (!userValues.id) { - userValues.id = superBlock; - } - - const initialValues = challengeTitles - .reduce((accu, current) => ({ - ...accu, - [current]: '' - }), {}); - - const completedProjects = _.values(userValues) - .filter(Boolean) - .filter(_.isString) - // minus 1 to account for the id - .length - 1; - - const fullForm = completedProjects === challengeTitles.length; - - let isFullStack = superBlock === 'full-stack'; - let isFullStackClaimable = false; - let description = ''; - if (isFullStack) { - isFullStackClaimable = Object.keys(blockNameIsCertMap).every(function(e) { - if (e.indexOf('Full Stack') !== -1 || e.indexOf('Legacy') !== -1) { - return true; - } - return blockNameIsCertMap[e]; - }); - - description = ( -
    -

    - Once you've earned the following freeCodeCamp certifications, - you'll be able to claim The Full Stack Developer Certification: -

    -
      -
    • Responsive Web Design
    • -
    • Algorithms and Data Structures
    • -
    • Front End Libraries
    • -
    • Data Visualization
    • -
    • APIs and Microservices
    • -
    • Information Security and Quality Assurance
    • -
    -
    - ); - } - - return ( - -

    { projectBlockName }

    - {description} -
    - { - isCertClaimed ? - : - null - } -
    - - ); - } - - handleSubmit(values) { - const { id } = values; - const { allProjects } = this.props; - let project = _.find(allProjects, { superBlock: id }); - if (!project) { - // the submitted projects do not belong to current/legacy certifications - return this.props.createError( - new Error( - 'Submitted projects do not belong to either current or ' + - 'legacy certifications' - ) - ); - } - const valuesSaved = _.values(this.props.userProjects[id]) - .filter(Boolean) - .filter(_.isString); - - // minus 1 due to the form id being in values - const isProjectSectionComplete = - (valuesSaved.length - 1) === project.challenges.length; - - if (isProjectSectionComplete) { - return this.props.claimCert(id); - } - const valuesToIds = project.challenges - .reduce((valuesMap, current) => { - const solution = values[current.title]; - if (solution) { - return { - ...valuesMap, - [current.id]: solution - }; - } - return valuesMap; - }, {}); - return this.props.updateUserBackend({ - projects: { - [id]: valuesToIds - } - }); - } - - render() { - const { - modernProjects, - legacyProjects - } = this.props; - if (!modernProjects.length) { - return null; - } - return ( -
    - - Certification Settings - - -

    - Add links to the live demos of your projects as you finish them. - Then, once you have added all 5 projects required for a certification, - you can claim it. -

    -
    - { - modernProjects.map(this.buildProjectForms) - } - - Legacy Certification Settings - - { - legacyProjects.map(this.buildProjectForms) - } -
    - ); - } -} - -CertificationSettings.displayName = 'CertificationSettings'; -CertificationSettings.propTypes = propTypes; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(CertificationSettings); diff --git a/api-server/common/app/routes/Settings/components/DangerZone.jsx b/api-server/common/app/routes/Settings/components/DangerZone.jsx deleted file mode 100644 index f8f956b0c8..0000000000 --- a/api-server/common/app/routes/Settings/components/DangerZone.jsx +++ /dev/null @@ -1,105 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { Panel, Alert, Button } from 'react-bootstrap'; -import { connect } from 'react-redux'; - -import { ButtonSpacer, FullWidthRow } from '../../../helperComponents'; -import ResetModal from './ResetModal.jsx'; -import DeleteModal from './DeleteModal.jsx'; -import { resetProgress, deleteAccount } from '../redux'; - -const propTypes = { - deleteAccount: PropTypes.func.isRequired, - resetProgress: PropTypes.func.isRequired -}; - -const mapStateToProps = () => ({}); -const mapDispatchToProps = { - deleteAccount, - resetProgress -}; - -class DangerZone extends PureComponent { - constructor(props) { - super(props); - - this.state = { - delete: false, - reset: false - }; - - this.toggleDeleteModal = this.toggleDeleteModal.bind(this); - this.toggleResetModal = this.toggleResetModal.bind(this); - } - - toggleDeleteModal() { - return this.setState(state => ({ - ...state, - delete: !state.delete - })); - } - - toggleResetModal() { - return this.setState(state => ({ - ...state, - reset: !state.reset - })); - } - - render() { - const { resetProgress, deleteAccount } = this.props; - return ( -
    - - Danger Zone} - > - -

    - Please be careful! Changes in this section are permanent. -

    -
    - - - - - -
    - - -
    -
    - ); - } -} - -DangerZone.displayName = 'DangerZone'; -DangerZone.propTypes = propTypes; - -export default connect(mapStateToProps, mapDispatchToProps)(DangerZone); diff --git a/api-server/common/app/routes/Settings/components/DeleteModal.jsx b/api-server/common/app/routes/Settings/components/DeleteModal.jsx deleted file mode 100644 index 9b77378a75..0000000000 --- a/api-server/common/app/routes/Settings/components/DeleteModal.jsx +++ /dev/null @@ -1,71 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Modal, Button } from 'react-bootstrap'; - -const propTypes = { - delete: PropTypes.func.isRequired, - onHide: PropTypes.func.isRequired, - show: PropTypes.bool -}; - -function DeleteModal(props) { - const { show, onHide } = props; - return ( - - - Delete My Account - - -

    - This will really delete all your data, including all your progress - and account information. -

    -

    - We won't be able to recover any of it for you later, - even if you change your mind. -

    -

    - If there's something we could do better, send us an email instead and - we'll do our best:   - - team@freecodecamp.org - -

    -
    - -
    - - - - - - - ); -} - -DeleteModal.displayName = 'DeleteModal'; -DeleteModal.propTypes = propTypes; - -export default DeleteModal; diff --git a/api-server/common/app/routes/Settings/components/Email-Settings.jsx b/api-server/common/app/routes/Settings/components/Email-Settings.jsx deleted file mode 100644 index f1489cc1fc..0000000000 --- a/api-server/common/app/routes/Settings/components/Email-Settings.jsx +++ /dev/null @@ -1,170 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { createSelector } from 'reselect'; -import { bindActionCreators } from 'redux'; -import { reduxForm } from 'redux-form'; -import { - Alert, - Button, - Col, - ControlLabel, - HelpBlock, - Row -} from 'react-bootstrap'; - -import TB from '../Toggle-Button'; -import EmailForm from './EmailForm.jsx'; -import { Link } from '../../../Router'; -import { FullWidthRow, Spacer } from '../../../helperComponents'; -import SectionHeader from './SectionHeader.jsx'; -import { userSelector } from '../../../redux'; -import { onRouteUpdateEmail, updateMyEmail, updateUserBackend } from '../redux'; - -const mapStateToProps = createSelector( - userSelector, - ({ - email, - isEmailVerified, - sendQuincyEmail - }) => ({ - email, - initialValues: { email }, - isEmailVerified, - sendQuincyEmail - }) -); - -function mapDispatchToProps(dispatch) { - return bindActionCreators({ - updateMyEmail, - updateUserBackend - }, dispatch); -} - -const propTypes = { - email: PropTypes.string, - isEmailVerified: PropTypes.bool, - options: PropTypes.arrayOf( - PropTypes.shape({ - flag: PropTypes.string, - label: PropTypes.string, - bool: PropTypes.bool - }) - ), - sendQuincyEmail: PropTypes.bool, - updateMyEmail: PropTypes.func.isRequired, - updateUserBackend: PropTypes.func.isRequired -}; - -export function UpdateEmailButton() { - return ( - - - - ); -} - -class EmailSettings extends PureComponent { - constructor(props) { - super(props); - - this.handleSubmit = this.handleSubmit.bind(this); - } - - handleSubmit({ email }) { - - this.props.updateMyEmail(email); - } - - render() { - const { - email, - isEmailVerified, - sendQuincyEmail, - updateUserBackend - } = this.props; - if (!email) { - return ( -
    - -

    - You do not have an email associated with this account. -

    -
    - - - -
    - ); - } - return ( -
    - - Email Settings - - { - isEmailVerified ? null : - - - - Your email has not been verified. - To use your email, you must - verify it here first. - - - - } - - - - - - - - - Send me Quincy's weekly email - - - - updateUserBackend({ - sendQuincyEmail: !sendQuincyEmail - }) - } - value={ sendQuincyEmail } - /> - - - -
    - ); - } -} - -EmailSettings.displayName = 'EmailSettings'; -EmailSettings.propTypes = propTypes; - -export default reduxForm( - { - form: 'email-settings', - fields: [ 'email' ] - }, - mapStateToProps, - mapDispatchToProps -)(EmailSettings); diff --git a/api-server/common/app/routes/Settings/components/EmailForm.jsx b/api-server/common/app/routes/Settings/components/EmailForm.jsx deleted file mode 100644 index 4751589859..0000000000 --- a/api-server/common/app/routes/Settings/components/EmailForm.jsx +++ /dev/null @@ -1,170 +0,0 @@ -import React, { PureComponent} from 'react'; -import PropTypes from 'prop-types'; -import { reduxForm } from 'redux-form'; -import { - Row, - Col, - ControlLabel, - FormControl, - HelpBlock, - Alert -} from 'react-bootstrap'; -import { updateUserBackend } from '../redux'; -import { FullWidthRow, Spacer } from '../../../helperComponents'; -import { BlockSaveButton, BlockSaveWrapper, validEmail } from '../formHelpers'; - -const propTypes = { - email: PropTypes.string, - errors: PropTypes.object, - fields: PropTypes.objectOf( - PropTypes.shape({ - name: PropTypes.string.isRequired, - onChange: PropTypes.func.isRequired, - value: PropTypes.string.isRequired - }) - ), - handleSubmit: PropTypes.func.isRequired, - updateUserBackend: PropTypes.func.isRequired -}; - -function mapStateToProps() { - return {}; -} - -const mapDispatchtoProps = { updateUserBackend }; - -function validator(values) { - const errors = {}; - const { email = '', confirmEmail = '' } = values; - - errors.email = validEmail(email); - if (errors.email || errors.confirmEmail) { - return errors; - } - errors.confirmEmail = email.toLowerCase() === confirmEmail.toLowerCase() ? - null : - 'Emails should be the same'; - - return errors; -} - -class EmailForm extends PureComponent { - constructor(props) { - super(props); - - this.options = { - required: [ 'confirmEmail', 'email' ], - types: { confirmemail: 'email', email: 'email' } - }; - this.handleSubmit = this.handleSubmit.bind(this); - } - - handleSubmit(values) { - const { updateUserBackend } = this.props; - const update = { - email: values.email - }; - updateUserBackend(update); - } - - render() { - - const { - fields: { email, confirmEmail }, - handleSubmit - } = this.props; - - const disableForm = (email.pristine && confirmEmail.pristine) || - (!!email.error || !!confirmEmail.error); - - return ( - - - - Current Email - - -

    - { this.props.email } -

    - -
    - - - - New Email - - - - - - - - { - !email.pristine && email.error ? - - - { email.error } - - : - null - } - - - - - Confirm New Email - - - - - - - - { - !confirmEmail.pristine && confirmEmail.error ? - - - { confirmEmail.error } - - : - null - } - - - - - - - ); - } -} - -EmailForm.displayName = 'EmailForm'; -EmailForm.propTypes = propTypes; - -export default reduxForm( - { - form: 'email-form', - fields: [ 'confirmEmail', 'email' ], - validate: validator - }, - mapStateToProps, - mapDispatchtoProps -)(EmailForm); diff --git a/api-server/common/app/routes/Settings/components/Honesty.jsx b/api-server/common/app/routes/Settings/components/Honesty.jsx deleted file mode 100644 index b846aec01a..0000000000 --- a/api-server/common/app/routes/Settings/components/Honesty.jsx +++ /dev/null @@ -1,95 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { Button, Panel } from 'react-bootstrap'; -import { createSelector } from 'reselect'; -import { connect } from 'react-redux'; - -import { FullWidthRow } from '../../../helperComponents'; -import SectionHeader from './SectionHeader.jsx'; -import { userSelector } from '../../../redux'; -import academicPolicy from '../../../../resource/academicPolicy'; -import { updateUserBackend } from '../redux'; - -const propTypes = { - isHonest: PropTypes.bool, - policy: PropTypes.arrayOf(PropTypes.string), - updateUserBackend: PropTypes.func.isRequired -}; - -const mapStateToProps = createSelector( - userSelector, - ({ isHonest }) => ({ - policy: academicPolicy, - isHonest - }) -); - -const mapDispatchToProps = { updateUserBackend }; - -class Honesty extends PureComponent { - constructor(props) { - super(props); - this.state = { - showHonesty: false - }; - - this.handleAgreeClick = this.handleAgreeClick.bind(this); - } - - handleAgreeClick() { - this.props.updateUserBackend({ isHonest: true }); - } - - render() { - const { policy, isHonest } = this.props; - const isHonestAgreed = ( - -

    - You have accepted our Academic Honesty Policy. -

    -
    - ); - const agreeButton = ( - - ); - return ( -
    - - Academic Honesty Policy - - - - { - policy.map( - (line, i) => ( -

    - ) - ) - } -
    - { - isHonest ? - isHonestAgreed : - agreeButton - } - - -

    - ); - } -} - - -Honesty.displayName = 'Honesty'; -Honesty.propTypes = propTypes; - -export default connect(mapStateToProps, mapDispatchToProps)(Honesty); diff --git a/api-server/common/app/routes/Settings/components/Internet-Settings.jsx b/api-server/common/app/routes/Settings/components/Internet-Settings.jsx deleted file mode 100644 index 8c090c7e55..0000000000 --- a/api-server/common/app/routes/Settings/components/Internet-Settings.jsx +++ /dev/null @@ -1,113 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { createSelector } from 'reselect'; -import { bindActionCreators } from 'redux'; -import { reduxForm } from 'redux-form'; - -import { FullWidthRow, Spacer } from '../../../helperComponents'; -import { BlockSaveButton, BlockSaveWrapper, FormFields } from '../formHelpers'; -import SectionHeader from './SectionHeader.jsx'; -import { userSelector } from '../../../redux'; -import { updateUserBackend } from '../redux'; - -const mapStateToProps = createSelector( - userSelector, - ({ - githubProfile = '', - linkedin = '', - twitter = '', - website = '' - }) => ({ - initialValues: { - githubProfile, - linkedin, - twitter, - personalWebsite: website - } - }) -); - -const formFields = [ - 'githubProfile', - 'linkedin', - 'twitter', - 'personalWebsite' -]; - -function mapDispatchToProps(dispatch) { - return bindActionCreators({ - updateUserBackend - }, dispatch); -} - -const propTypes = { - fields: PropTypes.object, - githubProfile: PropTypes.string, - handleSubmit: PropTypes.func.isRequired, - linkedin: PropTypes.string, - personalWebsite: PropTypes.string, - twitter: PropTypes.string, - updateUserBackend: PropTypes.func.isRequired, - username: PropTypes.string -}; - -class InternetSettings extends PureComponent { - constructor(props) { - super(props); - - this.handleSubmit = this.handleSubmit.bind(this); - } - - handleSubmit({personalWebsite, ...others}) { - this.props.updateUserBackend({ ...others, website: personalWebsite }); - } - - render() { - const { - fields, - fields: { _meta: { allPristine } }, - handleSubmit - } = this.props; - const options = { - types: formFields.reduce( - (all, current) => ({ ...all, [current]: 'url' }), - {} - ), - placeholder: false - }; - return ( -
    - - Your Internet Presence - - -
    - - - - - - -
    -
    - ); - } -} - -InternetSettings.displayName = 'InternetSettings'; -InternetSettings.propTypes = propTypes; - -export default reduxForm( - { - form: 'internet-settings', - fields: formFields - }, - mapStateToProps, - mapDispatchToProps -)(InternetSettings); diff --git a/api-server/common/app/routes/Settings/components/JSAlgoAndDSForm.jsx b/api-server/common/app/routes/Settings/components/JSAlgoAndDSForm.jsx deleted file mode 100644 index 3a5c14d907..0000000000 --- a/api-server/common/app/routes/Settings/components/JSAlgoAndDSForm.jsx +++ /dev/null @@ -1,134 +0,0 @@ -import React, { PureComponent } from 'react'; -import { kebabCase, defaultTo } from 'lodash'; -import PropTypes from 'prop-types'; -import { Button } from 'react-bootstrap'; - -import { FullWidthRow } from '../../../helperComponents'; -import { BlockSaveButton } from '../formHelpers'; -import { Link } from '../../../Router'; -import SolutionViewer from './SolutionViewer.jsx'; - -const jsFormPropTypes = { - challenges: PropTypes.arrayOf(PropTypes.string), - claimCert: PropTypes.func.isRequired, - hardGoTo: PropTypes.func.isRequired, - isCertClaimed: PropTypes.bool, - jsProjects: PropTypes.objectOf( - PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.object), - PropTypes.string - ]) - ), - projectBlockName: PropTypes.string, - superBlock: PropTypes.string, - username: PropTypes.string -}; - -const jsProjectPath = '/challenges/javascript-algorithms-and-data-structures-' + - 'projects/'; - -class JSAlgoAndDSForm extends PureComponent { - constructor(props) { - super(props); - - this.state = {}; - this.handleSolutionToggle = this.handleSolutionToggle.bind(this); - this.handleSubmit = this.handleSubmit.bind(this); - } - - handleSolutionToggle(e) { - e.persist(); - return this.setState(state => ({ - ...state, - [e.target.id]: !state[e.target.id] - })); - } - - handleSubmit(e) { - e.preventDefault(); - const { username, superBlock, isCertClaimed } = this.props; - if (isCertClaimed) { - return this.props.hardGoTo(`/certification/${username}/${superBlock}`); - } - return this.props.claimCert(superBlock); - } - - render() { - const { - projectBlockName, - challenges = [], - jsProjects = {}, - isCertClaimed - } = this.props; - const completeCount = Object.values(jsProjects) - .map(val => defaultTo(val, {})) - .filter(challengeInfo => Object.keys(challengeInfo).length !== 0) - .length; - - return ( - -

    { projectBlockName }

    -

    - To complete this certification, you must first complete the - JavaScript Algorithms and Data Structures project challenges -

    -
      - { - challenges.map(challenge => ( -
      -
    • -

      { challenge }

      - { - Object.keys(jsProjects[challenge] || {}).length ? -
      - -
      : - - - - } -
    • - { - this.state[challenge] ? - : - null - } -
      - )) - } -
    - { - Object.keys(jsProjects).length === completeCount ? -
    - - { isCertClaimed ? 'Show' : 'Claim'} Certification - -
    : - null - } -
    -
    - ); - } -} - -JSAlgoAndDSForm.displayName = 'JSAlgoAndDSForm'; -JSAlgoAndDSForm.propTypes = jsFormPropTypes; - -export default JSAlgoAndDSForm; diff --git a/api-server/common/app/routes/Settings/components/Portfolio-Settings.jsx b/api-server/common/app/routes/Settings/components/Portfolio-Settings.jsx deleted file mode 100644 index 257ec0b321..0000000000 --- a/api-server/common/app/routes/Settings/components/Portfolio-Settings.jsx +++ /dev/null @@ -1,178 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { createSelector } from 'reselect'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { Button } from 'react-bootstrap'; - -import { FullWidthRow, ButtonSpacer } from '../../../helperComponents'; -import SectionHeader from './SectionHeader.jsx'; -import { userSelector } from '../../../redux'; -import { addPortfolioItem } from '../../../entities'; -import { updateMyPortfolio, deletePortfolio } from '../redux'; -import { - Form, - maxLength, - minLength, - validURL -} from '../formHelpers'; - -const minTwoChar = minLength(2); -const max288Char = maxLength(288); - -const propTypes = { - addPortfolioItem: PropTypes.func.isRequired, - deletePortfolio: PropTypes.func.isRequired, - picture: PropTypes.string, - portfolio: PropTypes.arrayOf( - PropTypes.shape({ - description: PropTypes.string, - image: PropTypes.string, - title: PropTypes.string, - url: PropTypes.string - }) - ), - updateMyPortfolio: PropTypes.func.isRequired, - username: PropTypes.string -}; - -const mapStateToProps = createSelector( - userSelector, - ({ portfolio, username }) => ({ - portfolio, - username - }) -); - -function mapDispatchToProps(dispatch) { - return bindActionCreators({ - addPortfolioItem, - deletePortfolio, - updateMyPortfolio - }, dispatch); -} - -const formFields = [ 'title', 'url', 'image', 'description', 'id' ]; -const options = { - types: { - id: 'hidden', - url: 'url', - image: 'url', - description: 'textarea' - }, - required: [ 'url', 'title', 'id' ] -}; - -function validator(values) { - const errors = {}; - const { - title = '', - url = '', - description = '', - image = '' - } = values; - errors.title = minTwoChar(title); - errors.description = max288Char(description); - errors.url = url && validURL(url); - errors.image = image && validURL(image); - return errors; -} - -class PortfolioSettings extends PureComponent { - constructor(props) { - super(props); - - this.handleAdd = this.handleAdd.bind(this); - this.handleDelete = this.handleDelete.bind(this); - this.handleSave = this.handleSave.bind(this); - this.renderPortfolio = this.renderPortfolio.bind(this); - } - - handleAdd() { - this.props.addPortfolioItem(this.props.username); - } - - handleDelete(id) { - const { deletePortfolio } = this.props; - deletePortfolio({ portfolio: { id } }); - } - - handleSave(portfolio) { - const { updateMyPortfolio } = this.props; - updateMyPortfolio(portfolio); - } - - renderPortfolio(portfolio, index, arr) { - const { - id - } = portfolio; - return ( -
    - -
    - - - { - index + 1 !== arr.length &&
    - } - -
    - ); - } - - render() { - const { portfolio = [] } = this.props; - return ( -
    - - Portfolio Settings - - -
    -

    - Share your non-FreeCodeCamp projects, articles or accepted - pull requests. -

    -
    -
    - { - portfolio.length ? portfolio.map(this.renderPortfolio) : null - } - - - - -
    - ); - } -} - -PortfolioSettings.displayName = 'PortfolioSettings'; -PortfolioSettings.propTypes = propTypes; - -export default connect(mapStateToProps, mapDispatchToProps)(PortfolioSettings); diff --git a/api-server/common/app/routes/Settings/components/Privacy-Settings.jsx b/api-server/common/app/routes/Settings/components/Privacy-Settings.jsx deleted file mode 100644 index 5ef1196470..0000000000 --- a/api-server/common/app/routes/Settings/components/Privacy-Settings.jsx +++ /dev/null @@ -1,177 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { Button } from 'react-bootstrap'; - -import { updateMyProfileUI } from '../redux'; -import { userSelector } from '../../../redux'; - -import { FullWidthRow, Spacer } from '../../../helperComponents'; -import SectionHeader from './SectionHeader.jsx'; -import ToggleSetting from './ToggleSetting.jsx'; - -const mapStateToProps = createSelector( - userSelector, - user => ({ - ...user.profileUI, - user - }) -); - -const mapDispatchToProps = dispatch => - bindActionCreators({ updateMyProfileUI }, dispatch); - -const propTypes = { - isLocked: PropTypes.bool, - showAbout: PropTypes.bool, - showCerts: PropTypes.bool, - showDonation: PropTypes.bool, - showHeatMap: PropTypes.bool, - showLocation: PropTypes.bool, - showName: PropTypes.bool, - showPoints: PropTypes.bool, - showPortfolio: PropTypes.bool, - showTimeLine: PropTypes.bool, - updateMyProfileUI: PropTypes.func.isRequired, - user: PropTypes.object -}; - -function PrivacySettings(props) { - const { - isLocked = true, - showAbout = false, - showCerts = false, - showDonation = false, - showHeatMap = false, - showLocation = false, - showName = false, - showPoints = false, - showPortfolio = false, - showTimeLine = false, - updateMyProfileUI, - user - } = props; - const toggleFlag = flag => - () => updateMyProfileUI({ profileUI: { [flag]: !props[flag] } }); - return ( -
    - Privacy Settings - -

    - The settings in this section enable you to control what is shown on{' '} - your freeCodeCamp public portfolio. -

    -

    There is also a button to see what data we hold on your account

    - - - - - - - - - - -
    - - - - -
    - ); -} - -PrivacySettings.displayName = 'PrivacySettings'; -PrivacySettings.propTypes = propTypes; - -export default connect(mapStateToProps, mapDispatchToProps)(PrivacySettings); diff --git a/api-server/common/app/routes/Settings/components/ResetModal.jsx b/api-server/common/app/routes/Settings/components/ResetModal.jsx deleted file mode 100644 index a4c0ffbd29..0000000000 --- a/api-server/common/app/routes/Settings/components/ResetModal.jsx +++ /dev/null @@ -1,65 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Modal, Button } from 'react-bootstrap'; - -const propTypes = { - onHide: PropTypes.func.isRequired, - reset: PropTypes.func.isRequired, - show: PropTypes.bool -}; - -function ResetModal(props) { - const { show, onHide } = props; - return ( - - - Reset My Progress - - -

    - This will really delete all of your progress, points, completed - challenges, our records of your projects, any certifications you have, - everything. -

    -

    - We won't be able to recover any of it for you later, even if you - change your mind. -

    -
    - -
    - - - - - - - ); -} - -ResetModal.displayName = 'ResetModal'; -ResetModal.propTypes = propTypes; - -export default ResetModal; diff --git a/api-server/common/app/routes/Settings/components/SectionHeader.jsx b/api-server/common/app/routes/Settings/components/SectionHeader.jsx deleted file mode 100644 index ed92678f33..0000000000 --- a/api-server/common/app/routes/Settings/components/SectionHeader.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { FullWidthRow } from '../../../helperComponents'; - -const propTypes = { - children: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.element, - PropTypes.node - ]) -}; - -function SectionHeader({ children }) { - return ( - -

    { children }

    -
    -
    - ); -} - -SectionHeader.displayName = 'SectionHeader'; -SectionHeader.propTypes = propTypes; - -export default SectionHeader; diff --git a/api-server/common/app/routes/Settings/components/SolutionViewer.jsx b/api-server/common/app/routes/Settings/components/SolutionViewer.jsx deleted file mode 100644 index 0091ad84e6..0000000000 --- a/api-server/common/app/routes/Settings/components/SolutionViewer.jsx +++ /dev/null @@ -1,80 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Panel } from 'react-bootstrap'; -import Prism from 'prismjs'; -import Helmet from 'react-helmet'; - -const prismLang = { - css: 'css', - js: 'javascript', - jsx: 'javascript', - html: 'markup' -}; - -function getContentString(file) { - return file.trim() || '// We do not have the solution to this challenge'; -} - -function SolutionViewer({ files }) { - const solutions = files && Array.isArray(files) ? - files.map(file => ( - -
    -          
    -        
    -
    - )) : ( - -
    -          
    -        
    -
    - ) - ; - return ( -
    - - - - { - solutions - } -
    - ); -} - -SolutionViewer.displayName = 'SolutionViewer'; -SolutionViewer.propTypes = { - files: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.objectOf(PropTypes.string)), - PropTypes.string - ]) -}; - -export default SolutionViewer; diff --git a/api-server/common/app/routes/Settings/components/ThemeSettings.jsx b/api-server/common/app/routes/Settings/components/ThemeSettings.jsx deleted file mode 100644 index 314350562b..0000000000 --- a/api-server/common/app/routes/Settings/components/ThemeSettings.jsx +++ /dev/null @@ -1,38 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import { - Row, - Col, - ControlLabel -} from 'react-bootstrap'; - -import TB from '../Toggle-Button'; - -const propTypes = { - currentTheme: PropTypes.string.isRequired, - toggleNightMode: PropTypes.func.isRequired -}; - -export default function ThemeSettings({ currentTheme, toggleNightMode }) { - return ( - - - -

    - Night Mode -

    -
    - - - toggleNightMode(currentTheme) } - value={ currentTheme === 'night' } - /> - -
    - ); -} - -ThemeSettings.displayName = 'ThemeSettings'; -ThemeSettings.propTypes = propTypes; diff --git a/api-server/common/app/routes/Settings/components/ToggleSetting.jsx b/api-server/common/app/routes/Settings/components/ToggleSetting.jsx deleted file mode 100644 index 24fe1a5b2d..0000000000 --- a/api-server/common/app/routes/Settings/components/ToggleSetting.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import { - Row, - Col, - ControlLabel -} from 'react-bootstrap'; - -import TB from '../Toggle-Button'; - -const propTypes = { - action: PropTypes.string.isRequired, - explain: PropTypes.string, - flag: PropTypes.bool.isRequired, - flagName: PropTypes.string.isRequired, - toggleFlag: PropTypes.func.isRequired -}; - -export default function ToggleSetting({ - action, - explain, - flag, - flagName, - toggleFlag, - ...restProps -}) { - return ( - - - -

    - - { action } - -
    - { - explain ? {explain} : null - } -

    -
    - - - - -
    - ); -} - -ToggleSetting.displayName = 'ToggleSetting'; -ToggleSetting.propTypes = propTypes; diff --git a/api-server/common/app/routes/Settings/components/UsernameSettings.jsx b/api-server/common/app/routes/Settings/components/UsernameSettings.jsx deleted file mode 100644 index b244d7e77a..0000000000 --- a/api-server/common/app/routes/Settings/components/UsernameSettings.jsx +++ /dev/null @@ -1,187 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { - Col, - ControlLabel, - FormControl, - Alert -} from 'react-bootstrap'; -import { reduxForm } from 'redux-form'; -import { createSelector } from 'reselect'; - -import { - settingsSelector, - updateUserBackend, - validateUsername -} from '../redux'; -import { userSelector } from '../../../redux'; -import { BlockSaveButton, minLength } from '../formHelpers'; -import { FullWidthRow } from '../../../helperComponents'; - -const minTwoChar = minLength(2); - -const propTypes = { - fields: PropTypes.objectOf( - PropTypes.shape({ - error: PropTypes.string, - name: PropTypes.string.isRequired, - onChange: PropTypes.func.isRequired, - pristine: PropTypes.bool.isRequired, - value: PropTypes.string.isRequired - }) - ), - handleSubmit: PropTypes.func.isRequired, - isValidUsername: PropTypes.bool, - submitAction: PropTypes.func.isRequired, - username: PropTypes.string, - validateUsername: PropTypes.func.isRequired, - validating: PropTypes.bool -}; - -const mapStateToProps = createSelector( - userSelector, - settingsSelector, - ({ username }, { isValidUsername, validating }) => ({ - initialValues: { username }, - isValidUsername, - validate: validator, - validating - }) -); - -const mapDispatchToProps = { - validateUsername, - submitAction: updateUserBackend -}; -function normalise(str = '') { - return str.toLowerCase().trim(); -} - -function makeHandleChange(changeFn, validationAction, valid) { - return function handleChange(e) { - const { value } = e.target; - e.target.value = normalise(value); - if (e.target.value && valid) { - validationAction(value); - } - return changeFn(e); - }; -} - -function validator(values) { - const errors = {}; - const { username } = values; - const minWarn = minTwoChar(username); - if (minWarn) { - errors.username = minWarn; - return errors; - } - if (username.length === 0) { - errors.username = 'Username cannot be empty'; - } - return errors; -} - -function renderAlerts(validating, error, isValidUsername) { - if (!validating && error) { - return ( - - - { error } - - - ); - } - if (!validating && !isValidUsername) { - return ( - - - Username not available - - - ); - } - if (validating) { - return ( - - - Validating username - - - ); - } - if (!validating && isValidUsername) { - return ( - - - Username is available - - - ); - } - return null; -} - -function UsernameSettings(props) { - const { - fields: { - username: { - value, - onChange, - error, - pristine, - valid - } - }, - handleSubmit, - isValidUsername, - submitAction, - validateUsername, - validating - } = props; - return ( -
    - { - !pristine && renderAlerts(validating, error, isValidUsername) - } - - - - - Username - - - - - - - - - - -
    - ); -} - -UsernameSettings.displayName = 'UsernameSettings'; -UsernameSettings.propTypes = propTypes; - -export default reduxForm( - { - form: 'usernameSettings', - fields: [ 'username' ] - }, - mapStateToProps, - mapDispatchToProps -)(UsernameSettings); diff --git a/api-server/common/app/routes/Settings/formHelpers/BlockSaveButton.jsx b/api-server/common/app/routes/Settings/formHelpers/BlockSaveButton.jsx deleted file mode 100644 index c551c87de5..0000000000 --- a/api-server/common/app/routes/Settings/formHelpers/BlockSaveButton.jsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Button } from 'react-bootstrap'; - -function BlockSaveButton(props) { - return ( - - ); -} - -BlockSaveButton.displayName = 'BlockSaveButton'; -BlockSaveButton.propTypes = { - children: PropTypes.any -}; - -export default BlockSaveButton; diff --git a/api-server/common/app/routes/Settings/formHelpers/BlockSaveWrapper.jsx b/api-server/common/app/routes/Settings/formHelpers/BlockSaveWrapper.jsx deleted file mode 100644 index d560793e64..0000000000 --- a/api-server/common/app/routes/Settings/formHelpers/BlockSaveWrapper.jsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -const propTypes = { - children: PropTypes.node -}; - -const style = { - padding: '0 15px' -}; - -function BlockSaveWrapper({ children }) { - return ( -
    - { children } -
    - ); -} - -BlockSaveWrapper.displayName = 'BlockSaveWrapper'; -BlockSaveWrapper.propTypes = propTypes; - -export default BlockSaveWrapper; diff --git a/api-server/common/app/routes/Settings/formHelpers/Form.jsx b/api-server/common/app/routes/Settings/formHelpers/Form.jsx deleted file mode 100644 index c41b47325a..0000000000 --- a/api-server/common/app/routes/Settings/formHelpers/Form.jsx +++ /dev/null @@ -1,87 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { reduxForm } from 'redux-form'; - -import { FormFields, BlockSaveButton } from './'; - -const propTypes = { - buttonText: PropTypes.string, - enableSubmit: PropTypes.bool, - errors: PropTypes.object, - fields: PropTypes.objectOf( - PropTypes.shape({ - name: PropTypes.string.isRequired, - onChange: PropTypes.func.isRequired, - value: PropTypes.string.isRequired - }) - ), - formFields: PropTypes.arrayOf(PropTypes.string).isRequired, - handleSubmit: PropTypes.func, - hideButton: PropTypes.bool, - id: PropTypes.string.isRequired, - initialValues: PropTypes.object, - options: PropTypes.shape({ - ignored: PropTypes.arrayOf(PropTypes.string), - required: PropTypes.arrayOf(PropTypes.string), - types: PropTypes.objectOf(PropTypes.string) - }), - submit: PropTypes.func.isRequired -}; - -function DynamicForm({ - // redux-form - errors, - fields, - handleSubmit, - fields: { _meta: { allPristine }}, - - // HOC - buttonText, - enableSubmit, - hideButton, - id, - options, - submit -}) { - return ( -
    - - { - hideButton ? - null : - errors[key]).length) - } - > - { - buttonText ? buttonText : null - } - - } - - ); -} - -DynamicForm.displayName = 'DynamicForm'; -DynamicForm.propTypes = propTypes; - -const DynamicFormWithRedux = reduxForm()(DynamicForm); - -export default function Form(props) { - return ( - - ); -} - -Form.propTypes = propTypes; diff --git a/api-server/common/app/routes/Settings/formHelpers/FormFields.jsx b/api-server/common/app/routes/Settings/formHelpers/FormFields.jsx deleted file mode 100644 index f66c5f2945..0000000000 --- a/api-server/common/app/routes/Settings/formHelpers/FormFields.jsx +++ /dev/null @@ -1,100 +0,0 @@ -import React from 'react'; -import _ from 'lodash'; -import PropTypes from 'prop-types'; -import { - Alert, - Col, - ControlLabel, - FormControl, - HelpBlock, - Row -} from 'react-bootstrap'; - -const propTypes = { - errors: PropTypes.objectOf(PropTypes.string), - fields: PropTypes.objectOf( - PropTypes.shape({ - name: PropTypes.string.isRequired, - onChange: PropTypes.func.isRequired, - value: PropTypes.string.isRequired - }) - ).isRequired, - formId: PropTypes.string, - options: PropTypes.shape({ - errors: PropTypes.objectOf( - PropTypes.oneOfType([ - PropTypes.string, - PropTypes.instanceOf(null) - ]) - ), - ignored: PropTypes.arrayOf(PropTypes.string), - placeholder: PropTypes.bool, - required: PropTypes.arrayOf(PropTypes.string), - types: PropTypes.objectOf(PropTypes.string) - }) -}; - -function FormFields(props) { - const { errors = {}, fields, formId, options = {} } = props; - const { - ignored = [], - placeholder = true, - required = [], - types = {} - } = options; - return ( -
    - { - Object.keys(fields) - .filter(field => !ignored.includes(field)) - .map(key => fields[key]) - .map(({ name, onChange, value, pristine }) => { - const key = formId ? - `${formId}_${_.kebabCase(name)}` : - _.kebabCase(name); - const type = name in types ? types[name] : 'text'; - return ( - - - { type === 'hidden' ? - null : - - { _.startCase(name) } - - } - - - - { - name in errors && !pristine ? - - - { errors[name] } - - : - null - } - - - ); - }) - } -
    - ); -} - -FormFields.displayName = 'FormFields'; -FormFields.propTypes = propTypes; - -export default FormFields; diff --git a/api-server/common/app/routes/Settings/formHelpers/index.js b/api-server/common/app/routes/Settings/formHelpers/index.js deleted file mode 100644 index 29360ee1c6..0000000000 --- a/api-server/common/app/routes/Settings/formHelpers/index.js +++ /dev/null @@ -1,35 +0,0 @@ -import { isEmail, isURL } from 'validator'; - -/** Components **/ - -export { default as BlockSaveButton } from './BlockSaveButton.jsx'; -export { default as BlockSaveWrapper } from './BlockSaveWrapper.jsx'; -export { default as Form } from './Form.jsx'; -export { default as FormFields } from './FormFields.jsx'; - -/** Normalise **/ - -export function lowerAndTrim(str = '') { - return str.toLowerCase().trim(); -} - -/** Validation **/ - -export function maxLength(max) { - return value => value && value.length > max ? - `Must be ${max} characters or less` : - null; -} - -export function minLength(min) { - return value => value && value.length < min ? - `Must be ${min} characters or more` : - null; -} -export function validEmail(email) { - return isEmail(email) ? null : 'Must be a valid email'; -} - -export function validURL(str) { - return isURL(str) ? null : 'Must be a valid URL'; -} diff --git a/api-server/common/app/routes/Settings/index.js b/api-server/common/app/routes/Settings/index.js deleted file mode 100644 index 9ced24862f..0000000000 --- a/api-server/common/app/routes/Settings/index.js +++ /dev/null @@ -1,8 +0,0 @@ -import { types } from './redux'; - -export { default } from './ShowSettings.jsx'; - -export const routes = { - [types.onRouteSettings]: '/settings', - [types.onRouteUpdateEmail]: '/settings/update-email' -}; diff --git a/api-server/common/app/routes/Settings/index.less b/api-server/common/app/routes/Settings/index.less deleted file mode 100644 index 80c7de4d1f..0000000000 --- a/api-server/common/app/routes/Settings/index.less +++ /dev/null @@ -1,2 +0,0 @@ -&{ @import "./settings.less"; } -&{ @import "./Toggle-Button/toggle.less"; } diff --git a/api-server/common/app/routes/Settings/ns.json b/api-server/common/app/routes/Settings/ns.json deleted file mode 100644 index 1beea17991..0000000000 --- a/api-server/common/app/routes/Settings/ns.json +++ /dev/null @@ -1 +0,0 @@ -"settings" diff --git a/api-server/common/app/routes/Settings/redux/certificate-epic.js b/api-server/common/app/routes/Settings/redux/certificate-epic.js deleted file mode 100644 index afa1725a3c..0000000000 --- a/api-server/common/app/routes/Settings/redux/certificate-epic.js +++ /dev/null @@ -1,42 +0,0 @@ -import { Observable } from 'rx'; -import { ofType } from 'redux-epic'; - -import { doActionOnError, fetchUser } from '../../../redux'; -import { makeToast } from '../../../Toasts/redux'; -import { postJSON$ } from '../../../../utils/ajax-stream'; -import { - types, - claimCertComplete, - claimCertError -} from '../redux'; - -function certificateEpic(actions$, { getState }) { - const start = actions$::ofType(types.claimCert.start) - .flatMap(({ payload: superBlock }) => { - const { - app: { csrfToken: _csrf } - } = getState(); - return postJSON$('/certificate/verify', { _csrf, superBlock }); - }) - .map(claimCertComplete) - .catch(doActionOnError(error => claimCertError(error))); - - const complete = actions$::ofType(types.claimCert.complete) - .flatMap(({ meta: { message, success }}) => Observable.if( - () => success, - Observable.of(fetchUser(), makeToast({ message })), - Observable.of(makeToast({ message })) - )); - - const error = actions$::ofType(types.claimCert.error) - .flatMap(error => { - return Observable.of( - makeToast({ message: 'Something went wrong updating your account' }), - { type: 'error', error} - ); - }); - - return Observable.merge(start, complete, error); -} - -export default certificateEpic; diff --git a/api-server/common/app/routes/Settings/redux/danger-zone-epic.js b/api-server/common/app/routes/Settings/redux/danger-zone-epic.js deleted file mode 100644 index 0d5a0c8138..0000000000 --- a/api-server/common/app/routes/Settings/redux/danger-zone-epic.js +++ /dev/null @@ -1,53 +0,0 @@ -import { Observable } from 'rx'; -import { ofType } from 'redux-epic'; - -import { - types, - resetProgressError, - deleteAccountError, - deleteAccountComplete -} from './'; -import { postJSON$ } from '../../../../utils/ajax-stream'; -import { - doActionOnError, - hardGoTo, - createErrorObservable -} from '../../../redux'; - -function dangerZoneEpic(actions$, { getState }) { - /** Reset Progress **/ - const resetStart = actions$::ofType(types.resetProgress.start) - .flatMap(() => { - const { csrfToken: _csrf } = getState().app; - return postJSON$('/account/reset-progress', { _csrf }) - .map(() => hardGoTo('/')) - .catch(doActionOnError(error => resetProgressError(error))); - }); - const resetError = actions$::ofType(types.resetProgress.error) - .flatMap(createErrorObservable); - - /** Delete Account **/ - const deleteStart = actions$::ofType(types.deleteAccount.start) - .flatMap(() => { - const { csrfToken: _csrf } = getState().app; - return postJSON$('/account/delete', { _csrf }) - .map(deleteAccountComplete) - .catch(doActionOnError(error => deleteAccountError(error))); - }); - - const deleteComplete = actions$::ofType(types.deleteAccount.complete) - .map(() => hardGoTo('/')); - - const deleteError = actions$::ofType(types.deleteAccount.error) - .flatMap(createErrorObservable); - - return Observable.merge( - resetStart, - resetError, - deleteStart, - deleteComplete, - deleteError - ).filter(Boolean); -} - -export default dangerZoneEpic; diff --git a/api-server/common/app/routes/Settings/redux/index.js b/api-server/common/app/routes/Settings/redux/index.js deleted file mode 100644 index b9b13f7b99..0000000000 --- a/api-server/common/app/routes/Settings/redux/index.js +++ /dev/null @@ -1,175 +0,0 @@ -import { isLocationAction } from 'redux-first-router'; -import { - composeReducers, - createAction, - createAsyncTypes, - createTypes, - handleActions -} from 'berkeleys-redux-utils'; -import { identity } from 'lodash'; - -import certificateEpic from './certificate-epic'; -import dangerZoneEpic from './danger-zone-epic'; -import userUpdateEpic from './update-user-epic'; -import newUsernameEpic from './new-username-epic'; -import ns from '../ns.json'; -import { utils } from '../../../Flash/redux'; - -export const epics = [ - certificateEpic, - dangerZoneEpic, - newUsernameEpic, - userUpdateEpic -]; - -const createActionWithFlash = type => createAction( - type, - null, - utils.createFlashMetaAction -); - -export const types = createTypes([ - createAsyncTypes('claimCert'), - createAsyncTypes('updateMyEmail'), - createAsyncTypes('updateUserBackend'), - createAsyncTypes('deletePortfolio'), - createAsyncTypes('updateMyPortfolio'), - createAsyncTypes('updateMyProfileUI'), - 'updateNewUsernameValidity', - createAsyncTypes('validateUsername'), - createAsyncTypes('refetchCompletedChallenges'), - createAsyncTypes('deleteAccount'), - createAsyncTypes('resetProgress'), - - 'onRouteSettings', - 'onRouteUpdateEmail' -], 'settings'); - - -export const onRouteSettings = createAction(types.onRouteSettings); -export const onRouteUpdateEmail = createAction(types.onRouteUpdateEmail); - -export const claimCert = createAction(types.claimCert.start); -export const claimCertComplete = createAction( - types.claimCert.complete, - ({ result }) => result, - identity -); -export const claimCertError = createAction( - types.claimCert.error, - identity -); - -export const updateUserBackend = createAction(types.updateUserBackend.start); -export const updateUserBackendComplete = createActionWithFlash( - types.updateUserBackend.complete -); -export const updateUserBackendError = createActionWithFlash( - types.updateUserBackend.error -); - -export const updateMyEmail = createAction(types.updateMyEmail.start); -export const updateMyEmailComplete = createActionWithFlash( - types.updateMyEmail.complete -); -export const updateMyEmailError = createActionWithFlash( - types.updateMyEmail.error -); - -export const updateMyPortfolio = createAction(types.updateMyPortfolio.start); -export const updateMyPortfolioComplete = createAction( - types.updateMyPortfolio.complete -); -export const updateMyPortfolioError = createAction( - types.updateMyPortfolio.error -); -export const deletePortfolio = createAction(types.deletePortfolio.start); -export const deletePortfolioError = createAction(types.deletePortfolio.error); - -export const updateMyProfileUI = createAction(types.updateMyProfileUI.start); -export const updateMyProfileUIComplete = createAction( - types.updateMyProfileUI.complete -); -export const updateMyProfileUIError = createAction( - types.updateMyProfileUI.error -); - -export const resetProgress = createAction(types.resetProgress.start); -export const resetProgressComplete = createAction(types.resetProgress.complete); -export const resetProgressError = createAction( - types.resetProgress.error, - identity -); - -export const deleteAccount = createAction(types.deleteAccount.start); -export const deleteAccountComplete = createAction(types.deleteAccount.complete); -export const deleteAccountError = createAction( - types.deleteAccount.error, - identity -); - -export const updateNewUsernameValidity = createAction( - types.updateNewUsernameValidity -); - -export const refetchCompletedChallenges = createAction( - types.refetchCompletedChallenges.start -); - -export const validateUsername = createAction(types.validateUsername.start); -export const validateUsernameError = createAction( - types.validateUsername.error, - identity -); - -const defaultState = { - showUpdateEmailView: false, - isValidUsername: false, - validating: false -}; - -const getNS = state => state[ns]; - -export function settingsSelector(state) { - return getNS(state); -} - -export const showUpdateEmailViewSelector = - state => getNS(state).showUpdateEmailView; - -export default composeReducers( - ns, - function settingsRouteReducer(state = defaultState, action) { - if (isLocationAction(action)) { - const { type } = action; - if (type === types.onRouteUpdateEmail) { - return { - ...state, - showUpdateEmailView: true - }; - } - if (state.showUpdateEmailView) { - return { - ...state, - showUpdateEmailView: false - }; - } - } - return state; - }, - handleActions(() => ({ - [types.updateNewUsernameValidity]: (state, { payload }) => ({ - ...state, - isValidUsername: payload, - validating: false - }), - [types.validateUsername.start]: state => ({ - ...state, - isValidUsername: false, - validating: true - }), - [types.validateUsername.error]: state => ({ ...state, validating: false }) - }), - defaultState -) -); diff --git a/api-server/common/app/routes/Settings/redux/new-username-epic.js b/api-server/common/app/routes/Settings/redux/new-username-epic.js deleted file mode 100644 index b6a7055711..0000000000 --- a/api-server/common/app/routes/Settings/redux/new-username-epic.js +++ /dev/null @@ -1,30 +0,0 @@ -import { Observable } from 'rx'; -import { ofType } from 'redux-epic'; - -import { - types, - updateNewUsernameValidity, - validateUsernameError -} from './'; -import { getJSON$ } from '../../../../utils/ajax-stream'; -import { - doActionOnError, - createErrorObservable -} from '../../../redux'; - -function validateUsernameEpic(actions$) { - const start = actions$::ofType(types.validateUsername.start) - .debounce(500) - .flatMap(({ payload }) => - getJSON$(`/api/users/exists?username=${payload}`) - .map(({ exists }) => updateNewUsernameValidity(!exists)) - .catch(error => doActionOnError(() => validateUsernameError(error))) - ); - - const error = actions$::ofType(types.validateUsername.error) - .flatMap(createErrorObservable); - - return Observable.merge(start, error); -} - -export default validateUsernameEpic; diff --git a/api-server/common/app/routes/Settings/redux/update-user-epic.js b/api-server/common/app/routes/Settings/redux/update-user-epic.js deleted file mode 100644 index a148871f15..0000000000 --- a/api-server/common/app/routes/Settings/redux/update-user-epic.js +++ /dev/null @@ -1,230 +0,0 @@ -import { Observable } from 'rx'; -import { combineEpics, ofType } from 'redux-epic'; -import { pick } from 'lodash'; -import { - types, - refetchCompletedChallenges, - updateUserBackendComplete, - updateMyPortfolioComplete, - updateMyProfileUIComplete -} from './'; -import { makeToast } from '../../../Toasts/redux'; -import { - doActionOnError, - usernameSelector, - userSelector, - createErrorObservable -} from '../../../redux'; -import { - updateUserEmail, - updateMultipleUserFlags, - regresPortfolio, - optoUpdatePortfolio, - updateLocalProfileUI -} from '../../../entities'; - -import { postJSON$ } from '../../../../utils/ajax-stream'; - -const endpoints = { - email: '/update-my-email', - projects: '/update-my-projects', - username: '/update-my-username' -}; - -function backendUserUpdateEpic(actions$, { getState }) { - const start = actions$::ofType(types.updateUserBackend.start); - const server = start - .flatMap(({ payload }) => { - const userMap = userSelector(getState()); - const { username } = userMap; - const flagsToCheck = Object.keys(payload); - const valuesToCheck = pick(userMap, flagsToCheck); - const oldValues = { - ...flagsToCheck.reduce((accu, current) => ({ ...accu, [current]: '' })), - ...valuesToCheck - }; - const valuesToUpdate = flagsToCheck.reduce((accu, current) => { - if (payload[current] !== valuesToCheck[current]) { - return { ...accu, [current]: payload[current] }; - } - return accu; - }, {}); - if (!Object.keys(valuesToUpdate).length) { - return Observable.of( - makeToast({ message: 'No changes in settings detected' }) - ); - } - const { - app: { csrfToken: _csrf } - } = getState(); - let body = { _csrf }; - let endpoint = '/update-flags'; - const updateKeys = Object.keys(valuesToUpdate); - if (updateKeys.length === 1 && updateKeys[0] in endpoints) { - // there is a specific route for this update - const flag = updateKeys[0]; - endpoint = endpoints[flag]; - body = { - ...body, - [flag]: valuesToUpdate[flag] - }; - } else { - body = { - ...body, - values: valuesToUpdate - }; - } - return postJSON$(endpoint, body) - .map(updateUserBackendComplete) - .catch( - doActionOnError( - () => updateMultipleUserFlags({ username, flags: oldValues }) - ) - ); - }); - const optimistic = start - .flatMap(({ payload }) => { - const username = usernameSelector(getState()); - return Observable.of( - updateMultipleUserFlags({ username, flags: payload }) - ); - }); - const complete = actions$::ofType(types.updateUserBackend.complete) - .flatMap(({ payload: { message } }) => Observable.if( - () => message.includes('project'), - Observable.of(refetchCompletedChallenges(), makeToast({ message })), - Observable.of(makeToast({ message })) - ) - ); - - return Observable.merge(server, optimistic, complete); -} - -function refetchCompletedChallengesEpic(actions$, { getState }) { - return actions$::ofType(types.refetchCompletedChallenges.start) - .flatMap(() => { - const { - app: { csrfToken: _csrf } - } = getState(); - const username = usernameSelector(getState()); - return postJSON$('/refetch-user-completed-challenges', { _csrf }) - .map(({ completedChallenges }) => - updateMultipleUserFlags({ username, flags: { completedChallenges } }) - ) - .catch(createErrorObservable); - }); -} - -function updateMyPortfolioEpic(actions$, { getState }) { - const edit = actions$::ofType(types.updateMyPortfolio.start); - const remove = actions$::ofType(types.deletePortfolio.start); - const serverEdit = edit - .flatMap(({ payload }) => { - const { id } = payload; - const { - app: { csrfToken: _csrf, username } - } = getState(); - return postJSON$('/update-my-portfolio', { _csrf, portfolio: payload }) - .map(updateMyPortfolioComplete) - .catch(doActionOnError(() => regresPortfolio({ username, id }))); - }); - const optimisticEdit = edit - .map(({ payload }) => { - const username = usernameSelector(getState()); - return optoUpdatePortfolio({ username, portfolio: payload }); - }); - const complete = actions$::ofType(types.updateMyPortfolio.complete) - .flatMap(({ payload: { message } }) => - Observable.of(makeToast({ message })) - ); - - const serverRemove = remove - .flatMap(({ payload: { portfolio } }) => { - const { - app: { csrfToken: _csrf } - } = getState(); - return postJSON$('/update-my-portfolio', { _csrf, portfolio }) - .map(updateMyPortfolioComplete) - .catch( - doActionOnError( - () => makeToast({ - message: 'Something went wrong removing a portfolio item.' - }) - ) - ); - }); - const optimisticRemove = remove - .flatMap(({ payload: { portfolio: { id } } }) => { - const username = usernameSelector(getState()); - return Observable.of(regresPortfolio({ username, id })); - }); - - return Observable.merge( - serverEdit, - optimisticEdit, - complete, - serverRemove, - optimisticRemove - ); -} - -function updateUserEmailEpic(actions, { getState }) { - return actions::ofType(types.updateMyEmail) - .flatMap(({ payload: email }) => { - const { - app: { user: username, csrfToken: _csrf }, - entities: { user: userMap } - } = getState(); - const { email: oldEmail } = userMap[username] || {}; - const body = { _csrf, email }; - const optimisticUpdate = Observable.just( - updateUserEmail(username, email) - ); - const ajaxUpdate = postJSON$('/update-my-email', body) - .catch(doActionOnError(() => oldEmail ? - updateUserEmail(username, oldEmail) : - null - )) - .filter(Boolean); - return Observable.merge(optimisticUpdate, ajaxUpdate); - }); -} - -function updateMyProfileUIEpic(action$, { getState }) { - const toggle = action$::ofType(types.updateMyProfileUI.start); - - const server = toggle.flatMap(({payload: { profileUI }}) => { - const state = getState(); - const { csrfToken: _csrf } = state.app; - const username = usernameSelector(state); - const oldUI = { ...userSelector(state).profileUI }; - return postJSON$('/update-my-profile-ui', { _csrf, profileUI }) - .map(updateMyProfileUIComplete) - .catch( - doActionOnError( - () => Observable.of( - makeToast({ - message: - 'Something went wrong saving your privacy settings, ' + - 'please try again.' - }), - updateLocalProfileUI({username, profileUI: oldUI }) - ) - ) - ); - }); - const optimistic = toggle.flatMap(({payload: { profileUI }}) => { - const username = usernameSelector(getState()); - return Observable.of(updateLocalProfileUI({username, profileUI})); - }); - - return Observable.merge(server, optimistic); -} - -export default combineEpics( - backendUserUpdateEpic, - refetchCompletedChallengesEpic, - updateMyPortfolioEpic, - updateUserEmailEpic, - updateMyProfileUIEpic -); diff --git a/api-server/common/app/routes/Settings/routes/update-email/Update-Email.jsx b/api-server/common/app/routes/Settings/routes/update-email/Update-Email.jsx deleted file mode 100644 index d241244722..0000000000 --- a/api-server/common/app/routes/Settings/routes/update-email/Update-Email.jsx +++ /dev/null @@ -1,172 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import { - Button, - Col, - FormControl, - FormGroup, - HelpBlock, - Row -} from 'react-bootstrap'; -import { reduxForm } from 'redux-form'; -import { isEmail } from 'validator'; - -import { Link } from '../../../../Router'; -import { getValidationState, DOMOnlyProps } from '../../../../utils/form'; -import { - onRouteSettings, - updateMyEmail -} from '../../redux'; - -const actions = { - updateMyEmail -}; -const fields = [ - 'email', - 'duplicate' -]; -const validateFields = ({ email, duplicate }) => { - const errors = {}; - - if (!isEmail('' + email)) { - errors.email = 'This email is invalid.'; - } - - if (duplicate && email !== duplicate) { - errors.duplicate = 'This email does not match the one above.'; - } - - return errors; -}; - -const mapStateToProps = state => { - const { - app: { user: username }, - entities: { user: userMap } - } = state; - const { email, emailVerified } = userMap[username] || {}; - - return { - initialValues: { email }, - isEmailThere: !!email, - isVerified: !!emailVerified - }; -}; - -const propTypes = { - fields: PropTypes.object.isRequired, - handleSubmit: PropTypes.func.isRequired, - isEmailThere: PropTypes.bool, - isVerified: PropTypes.bool, - submitting: PropTypes.bool, - updateMyEmail: PropTypes.func.isRequired -}; - -export class UpdateEmail extends React.Component { - constructor(...props) { - super(...props); - this.handleSubmit = this.handleSubmit.bind(this); - } - - handleSubmit(e) { - e.preventDefault(); - this.props.handleSubmit(({ email }) => this.props.updateMyEmail(email))(e); - } - - render() { - const { - isEmailThere, - isVerified, - fields: { email, duplicate }, - submitting - } = this.props; - const buttonCopy = !isEmailThere || isVerified ? - 'Update my Email' : - 'Verify Email'; - return ( - - -

    Update your email address here:

    -
    - - - { - !email.error ? - null : - { email.error } - } - - - - { - !duplicate.error ? - null : - { duplicate.error } - } - - - -
    - - - - - - - - ); - } -} - -UpdateEmail.displayName = 'UpdateEmail'; -UpdateEmail.propTypes = propTypes; - -export default reduxForm( - { - form: 'update-email', - fields, - validate: validateFields - }, - mapStateToProps, - actions -)(UpdateEmail); diff --git a/api-server/common/app/routes/Settings/routes/update-email/index.js b/api-server/common/app/routes/Settings/routes/update-email/index.js deleted file mode 100644 index 7d32d33ebe..0000000000 --- a/api-server/common/app/routes/Settings/routes/update-email/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './Update-Email.jsx'; diff --git a/api-server/common/app/routes/Settings/settings.less b/api-server/common/app/routes/Settings/settings.less deleted file mode 100644 index a7f946bd89..0000000000 --- a/api-server/common/app/routes/Settings/settings.less +++ /dev/null @@ -1,144 +0,0 @@ -// should be the same as the filename and ./ns.json -@ns: settings; - -@skeleton-gray: #b0bdb7; - -.night .@{ns}-container { - - .btn-group { - - label:disabled, label[disabled] { - border: 1px solid #999; - background-color: #999; - color: #333; - } - } - .panel { - color:#333333; - background-color: #cccccc; - a { - color: @brand-primary; - &:hover { - color: #001800; - } - } - } - .nav-tabs > li > a:hover { - color: #333333; - } -} - -.@{ns}-container { - .center(@value: @container-xl, @padding: @grid-gutter-width); - - button:disabled, button[disabled] { - border: 1px solid #999; - background-color: #999; - color: #333; - } - - a:hover { - text-decoration: none; - text-decoration-line: none; - } - - .panel { - background-color: #fff; - } - - .solution-list { - padding-left: 0px; - } - - .solution-list-item { - list-style: none; - display: flex; - justify-content: space-between; - align-items: center; - - p { - font-weight: bold; - margin-bottom: 0px; - } - } -} - -.privacy-settings { - - .toggle-label { - margin: 0; - - p { - display: flex; - flex-direction: column; - margin: 0; - } - } - - .inline-form { - display: flex; - align-items: center; - .btn-group > label { - margin: 10px 0; - } - @media (max-width: 450px){ - flex-wrap: wrap; - border-bottom: 1px solid #e0e0e0; - } - } - - label { - - em { - font-weight: 400; - } - } -} - -.@{ns}-email-container { - .below(sm, { - text-align: center; - }) -} - -.inline-form-field { - display: flex; - align-items: center; - margin: 5px 0; - - input, textarea { - background-color: #fff; - margin: 5px 0; - } - @media (max-width: 450px) { - flex-wrap: wrap; - } -} -.edit-preview-tabs { - - li { - padding-bottom: 0px; - } -} - -.avatar-container { - display: flex; - justify-content: center; -} - -.portfolio-settings-intro { - display: flex; - justify-content: space-around; - - .p-intro { - font-size: 18px; - } -} - -.danger-zone-panel { - background-color: #fff; - .panel-heading { - background-color: #880000; - color: #fff; - } -} diff --git a/api-server/common/app/routes/Settings/utils/buildUserProjectsMap.js b/api-server/common/app/routes/Settings/utils/buildUserProjectsMap.js deleted file mode 100644 index a0226b3909..0000000000 --- a/api-server/common/app/routes/Settings/utils/buildUserProjectsMap.js +++ /dev/null @@ -1,32 +0,0 @@ -import { find } from 'lodash'; - -export const jsProjectSuperBlock = 'javascript-algorithms-and-data-structures'; - -export function buildUserProjectsMap(projectBlock, completedChallenges) { - const { - challenges, - superBlock - } = projectBlock; - return { - [superBlock]: challenges.reduce((solutions, current) => { - const { id } = current; - const completed = find( - completedChallenges, - ({ id: completedId }) => completedId === id - ); - let solution = ''; - if (superBlock === jsProjectSuperBlock) { - solution = {}; - } - if (completed) { - solution = 'solution' in completed ? - completed.solution : - completed.files || ''; - } - return { - ...solutions, - [current.title]: solution - }; - }, {}) - }; -} diff --git a/api-server/common/app/routes/index.js b/api-server/common/app/routes/index.js deleted file mode 100644 index 36f862f9e9..0000000000 --- a/api-server/common/app/routes/index.js +++ /dev/null @@ -1,8 +0,0 @@ -import { routes as settingsRoutes } from './Settings'; -import { routes as profileRoutes } from './Profile'; - -export default { - ...settingsRoutes, - // ensure profile routes are last else they hijack other routes - ...profileRoutes -}; diff --git a/api-server/common/app/routes/index.less b/api-server/common/app/routes/index.less deleted file mode 100644 index d852290ad1..0000000000 --- a/api-server/common/app/routes/index.less +++ /dev/null @@ -1,2 +0,0 @@ -&{ @import "./Profile/profile.less"; } -&{ @import "./Settings/index.less"; } diff --git a/api-server/common/app/routes/redux.js b/api-server/common/app/routes/redux.js deleted file mode 100644 index d25ff41ad8..0000000000 --- a/api-server/common/app/routes/redux.js +++ /dev/null @@ -1,33 +0,0 @@ -import { isLocationAction } from 'redux-first-router'; -import { combineReducers } from 'berkeleys-redux-utils'; - -import profileReducer from './Profile/redux'; -import settingsReducer from './Settings/redux'; -import { routes as profileRoutes } from './Profile'; -import { routes as settingsRoutes } from './Settings'; - -const ns = 'mainRouter'; - -export const mainRouteSelector = state => state[ns]; - -export function mainRouter(state = 'NotFound', action) { - if (!isLocationAction(action)) { - return state; - } - const { type } = action; - if (settingsRoutes[type]) { - return 'settings'; - } - if (profileRoutes[type]) { - return 'profile'; - } - return ''; -} - -mainRouter.toString = () => ns; - -export default combineReducers( - profileReducer, - settingsReducer, - mainRouter -); diff --git a/api-server/common/app/utils/No-Props-Passthrough.jsx b/api-server/common/app/utils/No-Props-Passthrough.jsx deleted file mode 100644 index 66281c5e87..0000000000 --- a/api-server/common/app/utils/No-Props-Passthrough.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import PropTypes from 'prop-types'; - -// use when passing a react primitive element as a child to a -// react-boostrap component that will inject props -// using cloneElement -export default function NoPropsPassthrough({ children }) { - return children; -} - -NoPropsPassthrough.propTypes = { - children: PropTypes.element -}; diff --git a/api-server/common/app/utils/blockNameify.js b/api-server/common/app/utils/blockNameify.js deleted file mode 100644 index 72dc1ad157..0000000000 --- a/api-server/common/app/utils/blockNameify.js +++ /dev/null @@ -1,41 +0,0 @@ -const preFormattedBlockNames = { - 'api-projects': 'API Projects', - 'basic-css': 'Basic CSS', - 'basic-html-and-html5': 'Basic HTML and HTML5', - 'css-flexbox': 'CSS Flexbox', - 'css-grid': 'CSS Grid', - devops: 'DevOps', - es6: 'ES6', - 'information-security-with-helmetjs': 'Information Security with HelmetJS', - jquery: 'jQuery', - 'json-apis-and-ajax': 'JSON APIs and Ajax', - 'mongodb-and-mongoose': 'MongoDB and Mongoose', - 'the-dom': 'The DOM' -}; - -const noFormatting = [ - 'and', - 'for', - 'of', - 'the', - 'up', - 'with' -]; - -export default function blockNameify(phrase) { - const preFormatted = preFormattedBlockNames[phrase] || ''; - if (preFormatted) { - return preFormatted; - } - return phrase.split('-') - .map((word) => { - if (noFormatting.indexOf(word) !== -1) { - return word; - } - if (word === 'javascript') { - return 'JavaScript'; - } - return word.charAt(0).toUpperCase() + word.slice(1); - }) - .join(' '); -} diff --git a/api-server/common/app/utils/challengeTypes.js b/api-server/common/app/utils/challengeTypes.js deleted file mode 100644 index 92ffcedc24..0000000000 --- a/api-server/common/app/utils/challengeTypes.js +++ /dev/null @@ -1,12 +0,0 @@ -export const html = 0; -export const js = 1; -export const backend = 2; -export const zipline = 3; -export const frontEndProject = 3; -export const basejump = 4; -export const backEndProject = 4; -export const bonfire = 5; -export const modern = 6; -export const step = 7; -export const quiz = 8; -export const invalid = 9; diff --git a/api-server/common/app/utils/create-types.js b/api-server/common/app/utils/create-types.js deleted file mode 100644 index 31a93dba8a..0000000000 --- a/api-server/common/app/utils/create-types.js +++ /dev/null @@ -1,10 +0,0 @@ -// createTypes(types: String[], prefix: String) => Object -export default function createTypes(types = [], prefix = '') { - if (!Array.isArray(types) || typeof prefix !== 'string') { - return {}; - } - return types.reduce((types, type) => { - types[type] = prefix + '.' + type; - return types; - }, {}); -} diff --git a/api-server/common/app/utils/form.js b/api-server/common/app/utils/form.js deleted file mode 100644 index c96f4477f9..0000000000 --- a/api-server/common/app/utils/form.js +++ /dev/null @@ -1,94 +0,0 @@ -import omit from 'lodash/omit'; -import normalizeUrl from 'normalize-url'; -import { isURL } from 'validator'; - -const normalizeOptions = { - stripWWW: false -}; - -// callIfDefined(fn: (Any) => Any) => (value: Any) => Any -export function callIfDefined(fn) { - return value => value ? fn(value) : value; -} - -// formatUrl(url: String) => String -export function formatUrl(url) { - if ( - typeof url === 'string' && - url.length > 4 && - url.indexOf('.') !== -1 - ) { - // prevent trailing / from being stripped during typing - let lastChar = ''; - if (url.substring(url.length - 1) === '/') { - lastChar = '/'; - } - // prevent normalize-url from stripping last dot during typing - if (url.substring(url.length - 1) === '.') { - lastChar = '.'; - } - return normalizeUrl(url, normalizeOptions) + lastChar; - } - return url; -} - -export function isValidURL(data) { - /* eslint-disable quote-props */ - return isURL(data, { 'require_protocol': true }); - /* eslint-enable quote-props */ -} - -export function makeOptional(validator) { - return val => val ? validator(val) : true; -} - -export function makeRequired(validator) { - return (val) => val ? validator(val) : false; -} - -export function createFormValidator(fieldValidators) { - const fieldKeys = Object.keys(fieldValidators); - return values => fieldKeys - .map(field => { - if (fieldValidators[field](values[field])) { - return null; - } - return { [field]: !fieldValidators[field](values[field]) }; - }) - .filter(Boolean) - .reduce((errors, error) => ({ ...errors, ...error }), {}); -} - - -export function getValidationState(field) { - if (field.pristine) { - return null; - } - - if ((/https?:\/\/glitch\.com\/edit\/#!\/.*/g).test(field.value)) { - return 'glitch-warning'; - } - - return field.error ? - 'error' : - 'success'; -} - -// this should filter out none-dom props to silence React warnings -export function DOMOnlyProps(field) { - return omit(field, [ - 'initialValue', - 'autofill', - 'autocompleted', - 'onUpdate', - 'valid', - 'invalid', - 'dirty', - 'pristine', - 'active', - 'touched', - 'visited', - 'autofilled', - 'error' - ]); -} diff --git a/api-server/common/app/utils/shallow-equals.js b/api-server/common/app/utils/shallow-equals.js deleted file mode 100644 index 3f8ba3912f..0000000000 --- a/api-server/common/app/utils/shallow-equals.js +++ /dev/null @@ -1,37 +0,0 @@ -// original sourc -// https://github.com/rackt/react-redux/blob/master/src/utils/shallowEqual.js -// MIT license -export default function shallowEqual(objA, objB) { - if (objA === objB) { - return true; - } - - if ( - typeof objA !== 'object' || - objA === null || - typeof objB !== 'object' || - objB === null - ) { - return false; - } - - var keysA = Object.keys(objA); - var keysB = Object.keys(objB); - - if (keysA.length !== keysB.length) { - return false; - } - - // Test for A's keys different from B. - var bHasOwnProperty = Object.prototype.hasOwnProperty.bind(objB); - for (var i = 0; i < keysA.length; i++) { - if ( - !bHasOwnProperty(keysA[i]) || - objA[keysA[i]] !== objB[keysA[i]] - ) { - return false; - } - } - - return true; -} diff --git a/api-server/common/app/utils/empty-protector.js b/api-server/common/utils/empty-protector.js similarity index 100% rename from api-server/common/app/utils/empty-protector.js rename to api-server/common/utils/empty-protector.js diff --git a/api-server/common/utils/map.js b/api-server/common/utils/map.js index 60544ae491..9ce2e1b8ff 100644 --- a/api-server/common/utils/map.js +++ b/api-server/common/utils/map.js @@ -1,4 +1,4 @@ -import emptyProtector from '../app/utils/empty-protector'; +import emptyProtector from './empty-protector'; export function checkMapData( { diff --git a/api-server/common/utils/polyvinyl.js b/api-server/common/utils/polyvinyl.js index 18e5b15ea7..ed3faf53aa 100644 --- a/api-server/common/utils/polyvinyl.js +++ b/api-server/common/utils/polyvinyl.js @@ -1,7 +1,7 @@ // originally based off of https://github.com/gulpjs/vinyl import invariant from 'invariant'; import { Observable } from 'rx'; -import castToObservable from '../app/utils/cast-to-observable.js'; +import castToObservable from '../../server/utils/cast-to-observable'; // createFileStream( diff --git a/api-server/development-entry.js b/api-server/development-entry.js new file mode 100644 index 0000000000..a71da1f4b5 --- /dev/null +++ b/api-server/development-entry.js @@ -0,0 +1,13 @@ +const nodemon = require('nodemon'); + +nodemon({ + ext: 'js json', + // --silent squashes an ELIFECYCLE error when the server exits + exec: 'DEBUG=fcc* npm run --silent babel-dev-server', + watch: './server', + spawn: true +}); + +nodemon.on('restart', function nodemonRestart(files) { + console.log('App restarted due to: ', files); +}); diff --git a/api-server/package-lock.json b/api-server/package-lock.json index 36593409ba..cd8367ddb3 100644 --- a/api-server/package-lock.json +++ b/api-server/package-lock.json @@ -4,6 +4,24 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/cli": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.0.0.tgz", + "integrity": "sha512-SH/x7W1dz4FSSBeJZXIiYSbHIOU3ZxNgwQPLTG+I8KXyTS81pzmLouPa2st6hji7VbVrEF/D8EQzQbXAYj1TsA==", + "dev": true, + "requires": { + "chokidar": "2.0.4", + "commander": "2.17.1", + "convert-source-map": "1.5.1", + "fs-readdir-recursive": "1.1.0", + "glob": "7.1.3", + "lodash": "4.17.10", + "mkdirp": "0.5.1", + "output-file-sync": "2.0.1", + "slash": "2.0.0", + "source-map": "0.5.7" + } + }, "@babel/code-frame": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", @@ -313,6 +331,20 @@ "@babel/plugin-syntax-async-generators": "7.0.0" } }, + "@babel/plugin-proposal-class-properties": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.0.0.tgz", + "integrity": "sha512-mVgsbdySh6kuzv4omXvw0Kuh+3hrUrQ883qTCf75MqfC6zctx2LXrP3Wt+bbJmB5fE5nfhf/Et2pQyrRy4j0Pg==", + "dev": true, + "requires": { + "@babel/helper-function-name": "7.0.0", + "@babel/helper-member-expression-to-functions": "7.0.0", + "@babel/helper-optimise-call-expression": "7.0.0", + "@babel/helper-plugin-utils": "7.0.0", + "@babel/helper-replace-supers": "7.0.0", + "@babel/plugin-syntax-class-properties": "7.0.0" + } + }, "@babel/plugin-proposal-json-strings": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.0.0.tgz", @@ -363,6 +395,15 @@ "@babel/helper-plugin-utils": "7.0.0" } }, + "@babel/plugin-syntax-class-properties": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.0.0.tgz", + "integrity": "sha512-cR12g0Qzn4sgkjrbrzWy2GE7m9vMl/sFkqZ3gIpAQdrvPDnLM8180i+ANDFIXfjHo9aqp0ccJlQ0QNZcFUbf9w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "7.0.0" + } + }, "@babel/plugin-syntax-json-strings": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.0.0.tgz", @@ -3029,9 +3070,9 @@ } }, "dotenv": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz", - "integrity": "sha1-hk7xN5rO1Vzm+V3r7NzhefegzR0=" + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.0.0.tgz", + "integrity": "sha512-FlWbnhgjtwD+uNLUGHbMykMOYQaTivdHEmYwAKFjn6GKe/CqY0fNae93ZHTd20snh9ZLr8mTzIL9m0APQ1pjQg==" }, "duplex": { "version": "1.0.0", @@ -3541,6 +3582,21 @@ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz", "integrity": "sha1-YZegldX7a1folC9v1+qtY6CclFI=" }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" + } + }, "exit-hook": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", @@ -9726,6 +9782,12 @@ } } }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, "slice-ansi": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", @@ -11032,23 +11094,6 @@ "dev": true, "requires": { "execa": "0.7.0" - }, - "dependencies": { - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" - } - } } }, "text-encoding": { diff --git a/api-server/package.json b/api-server/package.json index 03c2d92259..90a38b5003 100644 --- a/api-server/package.json +++ b/api-server/package.json @@ -2,7 +2,10 @@ "name": "@freecodecamp/api-server", "version": "0.1.0", "repository": "freecodecamp/freecodecamp", + "private": true, + "main": "development-entry.js", "scripts": { + "babel-dev-server": "babel-node ./server/server.js", "commit": "git-cz", "commitmsg": "validate-commit-msg", "ensure-env": "node ./config/ensure-env.js", @@ -39,7 +42,7 @@ "date-fns": "^1.29.0", "debug": "^2.2.0", "dedent": "~0.7.0", - "dotenv": "^4.0.0", + "dotenv": "^6.0.0", "express-flash": "~0.0.2", "express-session": "^1.12.1", "express-state": "^1.2.0", @@ -79,8 +82,11 @@ "validator": "^9.4.1" }, "devDependencies": { + "@babel/cli": "^7.0.0", "@babel/core": "^7.0.0", "@babel/node": "^7.0.0", + "@babel/plugin-proposal-class-properties": "^7.0.0", + "@babel/plugin-proposal-object-rest-spread": "^7.0.0", "@babel/preset-env": "^7.0.0", "@babel/register": "^7.0.0", "adler32": "~0.1.7", @@ -95,7 +101,7 @@ "joi": "^13.1.2", "joi-objectid": "^2.0.0", "loopback-component-explorer": "^2.1.1", - "nodemon": "^1.18.3", + "nodemon": "^1.18.4", "pm2": "^3.0.3", "prettier": "^1.14.2", "sinon": "^2.0.0", diff --git a/api-server/server/boot/a-services.js b/api-server/server/boot/a-services.js index a14422a2af..f29a5a1cdd 100644 --- a/api-server/server/boot/a-services.js +++ b/api-server/server/boot/a-services.js @@ -1,20 +1,13 @@ import Fetchr from 'fetchr'; import getUserServices from '../services/user'; -import getMapUiServices from '../services/mapUi'; -import getChallengesForBlockService from '../services/challenge'; export default function bootServices(app) { - const challenge = getChallengesForBlockService(app); - const mapUi = getMapUiServices(app); const user = getUserServices(app); - Fetchr.registerFetcher(challenge); - Fetchr.registerFetcher(mapUi); Fetchr.registerFetcher(user); const middleware = Fetchr.middleware(); - app.use('/services', middleware); app.use('/external/services', middleware); app.use('/internal/services', middleware); } diff --git a/api-server/server/server.js b/api-server/server/server.js index 95ed7f029c..0540c5fce7 100755 --- a/api-server/server/server.js +++ b/api-server/server/server.js @@ -1,11 +1,13 @@ -require('dotenv').load(); +const path = require('path'); +require('dotenv').config({ path: path.resolve(__dirname, '../.env')}); + +console.log(path.resolve(__dirname, '../.env')) const _ = require('lodash'); const Rx = require('rx'); const loopback = require('loopback'); const boot = require('loopback-boot'); const expressState = require('express-state'); -const path = require('path'); const createDebugger = require('debug'); const { setupPassport } = require('./component-passport'); diff --git a/api-server/common/app/utils/cast-to-observable.js b/api-server/server/utils/cast-to-observable.js similarity index 100% rename from api-server/common/app/utils/cast-to-observable.js rename to api-server/server/utils/cast-to-observable.js diff --git a/client/src/components/Header/components/SignedIn.js b/client/src/components/Header/components/SignedIn.js index a0e398bb64..b61ab15da4 100644 --- a/client/src/components/Header/components/SignedIn.js +++ b/client/src/components/Header/components/SignedIn.js @@ -12,7 +12,7 @@ const mapStateToProps = createSelector(userSelector, ({ picture }) => ({ function SignedIn({ picture }) { return ( - + ); } diff --git a/client/src/components/Header/index.js b/client/src/components/Header/index.js index db34116619..9772a2f31d 100644 --- a/client/src/components/Header/index.js +++ b/client/src/components/Header/index.js @@ -21,7 +21,11 @@ function Header({ disableSettings }) { Curriculum
  • - + Forum
  • diff --git a/client/src/components/Layout.js b/client/src/components/Layout.js index 14147e70b3..0ffe239960 100644 --- a/client/src/components/Layout.js +++ b/client/src/components/Layout.js @@ -42,10 +42,6 @@ const mapDispatchToProps = dispatch => bindActionCreators({ fetchUser, removeFlashMessage }, dispatch); class Layout extends Component { - constructor(props) { - super(props); - } - componentDidMount() { if (!this.props.isSignedIn) { this.props.fetchUser(); diff --git a/client/src/pages/accept-privacy-terms.js b/client/src/pages/accept-privacy-terms.js index c733df5214..9776865760 100644 --- a/client/src/pages/accept-privacy-terms.js +++ b/client/src/pages/accept-privacy-terms.js @@ -104,7 +104,11 @@ class AcceptPrivacyTerms extends Component { onChange={this.createHandleChange('termsOfService')} > I accept the{' '} - + terms of service {' '} (required) @@ -122,7 +126,11 @@ class AcceptPrivacyTerms extends Component { onChange={this.createHandleChange('privacyPolicy')} > I accept the{' '} - + privacy policy {' '} (required) diff --git a/client/src/pages/update-email.js b/client/src/pages/update-email.js index 12b5b0c077..5074d91a05 100644 --- a/client/src/pages/update-email.js +++ b/client/src/pages/update-email.js @@ -37,7 +37,7 @@ const mapStateToProps = createSelector( const mapDispatchToProps = dispatch => bindActionCreators({ updateMyEmail }, dispatch); -const maybeEmailRE = /[\w\.\+]*?@\w*?\.\w+?/; +const maybeEmailRE = /[\w.+]*?@\w*?\.\w+?/; class UpdateEmail extends Component { constructor(props) { diff --git a/client/src/utils/ajax.js b/client/src/utils/ajax.js index 587888de60..55376bd514 100644 --- a/client/src/utils/ajax.js +++ b/client/src/utils/ajax.js @@ -6,9 +6,9 @@ function get(path) { return axios.get(`${base}${path}`); } -function post(path, body) { - return axios.post(`${base}${path}`, body); -} +// function post(path, body) { +// return axios.post(`${base}${path}`, body); +// } function put(path, body) { return axios.put(`${base}${path}`, body); diff --git a/develop-client-server.js b/develop-client-server.js new file mode 100644 index 0000000000..b234e978ba --- /dev/null +++ b/develop-client-server.js @@ -0,0 +1,34 @@ +const { spawn } = require('child_process'); +const kill = require('tree-kill'); + +const spawnOpts = { + stdio: 'inherit', + shell: true +}; + +const loopback = spawn( + 'cd', + ['./api-server', '&&', 'node development-entry.js'], + spawnOpts +); +const gatsby = spawn('cd', ['./client', '&&', 'npm run develop'], spawnOpts); + +function cleanUp() { + console.log(` + Killing processes... + `); + const promises = [ + kill(loopback.pid, 'SIGINT', () => Promise.resolve()), + kill(gatsby.pid, 'SIGINT', () => Promise.resolve()) + ]; + return Promise.all(promises).then(() => { + console.log(` + All processes have exited + `); + }); +} + +process.on('exit', cleanUp); +process.on('SIGINT', cleanUp); +process.on('SIGUSR1', cleanUp); +process.on('SIGUSR2', cleanUp); diff --git a/development-server.js b/development-server.js deleted file mode 100644 index b59f0cb5e0..0000000000 --- a/development-server.js +++ /dev/null @@ -1,27 +0,0 @@ -require('@babel/register'); - -const { spawn } = require('child_process'); -const nodemon = require('nodemon'); - -const spawnOpts = { - stdio: 'inherit', - shell: true -}; - -// spawns loopback - -nodemon({ - script: './server/server.js', - ext: 'js json', - // --silent squashes an ELIFECYCLE error when the server exits - exec: 'npm run --silent babel-dev-server', - watch: './server', - spawn: true -}); - -nodemon - .on('restart', function nodemonRestart(files) { - console.log('App restarted due to: ', files); - }); -// spawns gatsby in development mode -spawn('npm', ['run', 'develop'], spawnOpts); diff --git a/old-client/client/README.md b/old-client/client/README.md deleted file mode 100644 index dd8e39e772..0000000000 --- a/old-client/client/README.md +++ /dev/null @@ -1,4 +0,0 @@ -This is the entry point for the client code. -Code that should run only on the client should be put here. - -NOTE(berks): For react specific stuff this should be the entry point diff --git a/old-client/client/epics/README.md b/old-client/client/epics/README.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/old-client/client/epics/analytics-epic.js b/old-client/client/epics/analytics-epic.js deleted file mode 100644 index da9207d167..0000000000 --- a/old-client/client/epics/analytics-epic.js +++ /dev/null @@ -1,44 +0,0 @@ -import { Observable } from 'rx'; -import { createErrorObservable } from '../../common/app/redux'; -import capitalize from 'lodash/capitalize'; - -// analytics types -// interface social { -// network: String, // facebook, twitter, etc -// action: String, // like, favorite, etc -// target: String // url like fcc.com or any other string -// } -// interface event { -// category: String, -// action: String, -// label?: String, -// value?: String -// } -// -const types = [ 'event', 'social' ]; -function formatFields({ type, ...fields }) { - // make sure type is supported - if (!types.some(_type => _type === type)) { - return null; - } - return Object.keys(fields).reduce((_fields, field) => { - _fields[ type + capitalize(field) ] = fields[ field ]; - return _fields; - }, { type }); -} - -export default function analyticsSaga(actions, _, { window }) { - const { ga } = window; - if (typeof ga !== 'function') { - console.log('GA not found'); - return Observable.empty(); - } - return actions - .filter(({ meta }) => !!(meta && meta.analytics && meta.analytics.type)) - .map(({ meta: { analytics } }) => formatFields(analytics)) - .filter(Boolean) - // ga always returns undefined - .map(({ type, ...fields }) => ga('send', type, fields)) - .ignoreElements() - .catch(createErrorObservable); -} diff --git a/old-client/client/epics/err-epic.js b/old-client/client/epics/err-epic.js deleted file mode 100644 index 12c0689843..0000000000 --- a/old-client/client/epics/err-epic.js +++ /dev/null @@ -1,10 +0,0 @@ -import { makeToast } from '../../common/app/Toasts/redux'; - -export default function errorSaga(actions) { - return actions.filter(({ error }) => !!error) - .map(({ error }) => error) - .doOnNext(error => console.error(error)) - .map(() => makeToast({ - message: 'Something went wrong, please try again later' - })); -} diff --git a/old-client/client/epics/hard-go-to-epic.js b/old-client/client/epics/hard-go-to-epic.js deleted file mode 100644 index 895ee49b70..0000000000 --- a/old-client/client/epics/hard-go-to-epic.js +++ /dev/null @@ -1,14 +0,0 @@ -import _ from 'lodash'; -import { ofType } from 'redux-epic'; - -import { types } from '../../common/app/redux'; - -export default function hardGoToSaga(actions, store, { location }) { - return actions::ofType(types.hardGoTo) - .pluck('payload') - .filter(_.isString) - .do((payload = '/') => { - location.pathname = payload; - }) - .ignoreElements(); -} diff --git a/old-client/client/epics/index.js b/old-client/client/epics/index.js deleted file mode 100644 index c21ccd687b..0000000000 --- a/old-client/client/epics/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import analyticsEpic from './analytics-epic.js'; -import errEpic from './err-epic.js'; -import hardGoToEpic from './hard-go-to-epic.js'; -import mouseTrapEpic from './mouse-trap-epic.js'; -import titleEpic from './title-epic.js'; - -export default [ - analyticsEpic, - errEpic, - hardGoToEpic, - mouseTrapEpic, - titleEpic -]; diff --git a/old-client/client/epics/mouse-trap-epic.js b/old-client/client/epics/mouse-trap-epic.js deleted file mode 100644 index b5a6298d66..0000000000 --- a/old-client/client/epics/mouse-trap-epic.js +++ /dev/null @@ -1,52 +0,0 @@ -import { Observable } from 'rx'; -import MouseTrap from 'mousetrap'; -import { push } from 'redux-first-router'; -import { - toggleNightMode, - hardGoTo -} from '../../common/app/redux'; -import { - aboutUrl, - donateUrl, - forumUrl, - githubUrl -} from '../../common/utils/constantStrings.json'; - -function bindKey(key, actionCreator) { - return Observable.fromEventPattern( - h => MouseTrap.bind(key, h), - h => MouseTrap.unbind(key, h) - ) - .map(actionCreator); -} - -const softRedirects = { - 'g n n': '/challenges/next-challenge', - 'g n m': '/map', - 'g n o': '/settings' -}; - -export default function mouseTrapSaga(actions) { - const traps = [ - ...Object.keys(softRedirects) - .map(key => bindKey(key, () => push(softRedirects[key]))), - bindKey( - 'g n a', - () => hardGoTo(aboutUrl) - ), - bindKey( - 'g n r', - () => hardGoTo(githubUrl) - ), - bindKey( - 'g n d', - () => hardGoTo(donateUrl) - ), - bindKey( - 'g n w', - () => hardGoTo(forumUrl) - ), - bindKey('g t n', toggleNightMode) - ]; - return Observable.merge(traps).takeUntil(actions.last()); -} diff --git a/old-client/client/epics/title-epic.js b/old-client/client/epics/title-epic.js deleted file mode 100644 index 4a9656c4f8..0000000000 --- a/old-client/client/epics/title-epic.js +++ /dev/null @@ -1,10 +0,0 @@ -import { ofType } from 'redux-epic'; -import { types, titleSelector } from '../../common/app/redux'; - -export default function titleSage(actions, { getState }, { document }) { - return actions::ofType(types.updateTitle) - .do(() => { - document.title = titleSelector(getState()); - }) - .ignoreElements(); -} diff --git a/old-client/client/index.js b/old-client/client/index.js deleted file mode 100644 index 617cf141f8..0000000000 --- a/old-client/client/index.js +++ /dev/null @@ -1,74 +0,0 @@ -import Rx from 'rx'; -import debug from 'debug'; -import { render } from 'redux-epic'; -import createHistory from 'history/createBrowserHistory'; -import sendPageAnalytics from './utils/send-page-analytics'; - -import { App, createApp, provideStore } from '../common/app'; - -// client specific epics -import epics from './epics'; - -const isDev = Rx.config.longStackSupport = debug.enabled('fcc:*'); -const log = debug('fcc:client'); -const hotReloadTimeout = 2000; -const { - devToolsExtension, - location, - history: _history, - document, - ga, - __fcc__: { - data: defaultState = {}, - csrf: { - token: csrfToken - } = {} - } -} = window; -const epicOptions = { - isDev, - window, - document, - location, - history: _history -}; - -const DOMContainer = document.getElementById('fcc'); - -defaultState.app.csrfToken = csrfToken; - -const serviceOptions = { - context: { _csrf: csrfToken }, - xhrPath: '/services', - xhrTimeout: 15000 -}; - -const history = createHistory(); -sendPageAnalytics(history, ga); - -createApp({ - history, - serviceOptions, - defaultState, - epics, - epicOptions, - enhancers: isDev && devToolsExtension && [ devToolsExtension() ] - }) - .doOnNext(() => { - if (module.hot && typeof module.hot.accept === 'function') { - module.hot.accept(() => { - // note(berks): not sure this ever runs anymore after adding - // RHR? - log('saving state and refreshing.'); - log('ignore react ssr warning.'); - setTimeout(() => location.reload(), hotReloadTimeout); - }); - } - }) - .do(() => log('rendering')) - .flatMap(({ store }) => render(provideStore(App, store), DOMContainer)) - .subscribe( - () => debug('react rendered'), - err => { throw err; }, - () => debug('react closed subscription') - ); diff --git a/old-client/client/less/flexgrid.less b/old-client/client/less/flexgrid.less deleted file mode 100644 index 00aad1ecad..0000000000 --- a/old-client/client/less/flexgrid.less +++ /dev/null @@ -1,152 +0,0 @@ -.justify-mixin(start) { justify-content: flex-start } -.justify-mixin(end) { justify-content: flex-end } -.justify-mixin(center) { justify-content: center } -.justify-mixin(around) { justify-content: space-around } -.justify-mixin(between) { justify-content: between } -.justify-mixin(@_) {} - -.items-mixin(top) { align-items: flex-start; } -.items-mixin(bottom) { align-items: flex-end; } -.items-mixin(center) { align-items: center; } -.items-mixin(stretch) { align-items: stretch; } -.items-mixin(baseline) { align-items: baseline; } -.items-mixin(@_) {} - -.item-mixin(top) { align-self: flex-start; } -.item-mixin(bottom) { align-self: flex-end; } -.item-mixin(center) { align-self: center; } -.item-mixin(stretch) { align-self: stretch; } -.item-mixin(baseline) { align-self: baseline; } -.item-mixin(@_) {} - -.content-mixin(top) { align-content: flex-start; } -.content-mixin(bottom) { align-content: flex-end; } -.content-mixin(center) { align-content: center; } -.content-mixin(stretch) { align-content: stretch; } -.content-mixin(baseline) { align-content: baseline; } -.content-mixin(@_) {} - -.grid(@direction: row; @items: none; @justify: none; @content: none; @wrap: wrap) { - display: flex; - flex-wrap: @wrap; - flex-direction: @direction; - - .justify-mixin(@justify); - .items-mixin(@item); - .content-mixin(@content); -} - -.row(@items: none; @justify: none; @content: none) { - .grid(@direction: row, @items, @justify, @content); -} - -.column(@items: none; @justify: none; @content: none) { - .grid(@direction: column; @items; @justify; @content); -} - -.margin-mixin(@g) when not (@g = 0) { - margin: @g / 2; -} - -.cell(@i: 1; @item; @g: @grid-gutter-width; @cols: @grid-columns) { - flex-basis: %('calc(100% * %s - %s)', @i / @cols, @g); - min-width: 0; // FF adjustment for responsive images - .item-mixin(@item); - .margin-mixin(@g); -} - -.cell-offset(@i: 1; @g: @grid-gutter-width; @cols: @grid-columns) { - margin-left: e%('calc(100% * %s + %s)', @i / @cols, @g / 2) !important; -} - -.padding-mixin(@pad) when (@pad) { - padding-left: @pad; - padding-right: @pad; -} - -// center an element -.center(@value: 50%; @padding: 0) { - margin-left: auto; - margin-right: auto; - max-width: @value; - width: 100%; - - .padding-mixin(@padding); -} - -// usage: -// .some-class { -// .between(sm, xl, { -// height: 9001px; -// }); -// } -// outputs: -// .some-class { -// @media only screen and (min-width: @screen-sm) and (max-width: @screen-xl) { -// height: 9001px; -// } -// } -// -// where screen-md, screen-xl are bootstrap vars -// min/max can be any of: sm, md, lg, xl -.between(@min; @max; @rules) { - // BS logic to do string building with conditions - .condition-wrapper(@new) { - .redefine-condition() { - @condition: @new; - } - } - .init-condition() { - .condition-wrapper('only screen'); - } - .init-condition(); - .add-min-media(@min) when (iskeyword(@min)) { - .redefine-condition(); - .condition-wrapper(%('%s and (min-width: @{screen-%s})', @condition, @min)); - } - - .add-max-media(@max) when (iskeyword(@max)) { - .redefine-condition(); - .condition-wrapper(%('%s and (max-width: @{screen-%s})', @condition, @max)); - } - .add-min-media(@min); - .add-max-media(@max); - - .add-query() { - .redefine-condition(); - @query: e(@condition); - @media @query { - @rules(); - } - } - - .add-query(); -} - -// usage -// .below(md, { -// height: 9001px; -// }); -// outputs: -// .some-class { -// @media only screen and (max-width: @screen-xl) { -// height: 9001px; -// } -// } -.below(@max, @rules) { - .between(@empty; @max; @rules); -} - -// usage -// .above(sm, { -// height: 9001px; -// }); -// outputs: -// .some-class { -// @media only screen and (min-width: @screen-sm) { -// height: 9001px; -// } -// } -.above(@min, @rules) { - .between(@min; @empty; @rules); -} diff --git a/old-client/client/less/lib/animate.less b/old-client/client/less/lib/animate.less deleted file mode 100644 index c9ed56812f..0000000000 --- a/old-client/client/less/lib/animate.less +++ /dev/null @@ -1,3338 +0,0 @@ -/*! - * animate.css -http://daneden.me/animate - * Version - 3.5.1 - * Licensed under the MIT license - http://opensource.org/licenses/MIT - * - * Copyright (c) 2016 Daniel Eden - */ - -.animated { - -webkit-animation-duration: 1s; - animation-duration: 1s; - -webkit-animation-fill-mode: both; - animation-fill-mode: both; -} - -.animated.infinite { - -webkit-animation-iteration-count: infinite; - animation-iteration-count: infinite; -} - -.animated.hinge { - -webkit-animation-duration: 2s; - animation-duration: 2s; -} - -.animated.flipOutX, -.animated.flipOutY, -.animated.bounceIn, -.animated.bounceOut { - -webkit-animation-duration: .75s; - animation-duration: .75s; -} - -@-webkit-keyframes bounce { - from, 20%, 53%, 80%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - -webkit-transform: translate3d(0,0,0); - transform: translate3d(0,0,0); - } - - 40%, 43% { - -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - -webkit-transform: translate3d(0, -30px, 0); - transform: translate3d(0, -30px, 0); - } - - 70% { - -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - -webkit-transform: translate3d(0, -15px, 0); - transform: translate3d(0, -15px, 0); - } - - 90% { - -webkit-transform: translate3d(0,-4px,0); - transform: translate3d(0,-4px,0); - } -} - -@keyframes bounce { - from, 20%, 53%, 80%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - -webkit-transform: translate3d(0,0,0); - transform: translate3d(0,0,0); - } - - 40%, 43% { - -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - -webkit-transform: translate3d(0, -30px, 0); - transform: translate3d(0, -30px, 0); - } - - 70% { - -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - -webkit-transform: translate3d(0, -15px, 0); - transform: translate3d(0, -15px, 0); - } - - 90% { - -webkit-transform: translate3d(0,-4px,0); - transform: translate3d(0,-4px,0); - } -} - -.bounce { - -webkit-animation-name: bounce; - animation-name: bounce; - -webkit-transform-origin: center bottom; - transform-origin: center bottom; -} - -@-webkit-keyframes flash { - from, 50%, to { - opacity: 1; - } - - 25%, 75% { - opacity: 0; - } -} - -@keyframes flash { - from, 50%, to { - opacity: 1; - } - - 25%, 75% { - opacity: 0; - } -} - -.flash { - -webkit-animation-name: flash; - animation-name: flash; -} - -/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ - -@-webkit-keyframes pulse { - from { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } - - 50% { - -webkit-transform: scale3d(1.05, 1.05, 1.05); - transform: scale3d(1.05, 1.05, 1.05); - } - - to { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } -} - -@keyframes pulse { - from { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } - - 50% { - -webkit-transform: scale3d(1.05, 1.05, 1.05); - transform: scale3d(1.05, 1.05, 1.05); - } - - to { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } -} - -.pulse { - -webkit-animation-name: pulse; - animation-name: pulse; -} - -@-webkit-keyframes rubberBand { - from { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } - - 30% { - -webkit-transform: scale3d(1.25, 0.75, 1); - transform: scale3d(1.25, 0.75, 1); - } - - 40% { - -webkit-transform: scale3d(0.75, 1.25, 1); - transform: scale3d(0.75, 1.25, 1); - } - - 50% { - -webkit-transform: scale3d(1.15, 0.85, 1); - transform: scale3d(1.15, 0.85, 1); - } - - 65% { - -webkit-transform: scale3d(.95, 1.05, 1); - transform: scale3d(.95, 1.05, 1); - } - - 75% { - -webkit-transform: scale3d(1.05, .95, 1); - transform: scale3d(1.05, .95, 1); - } - - to { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } -} - -@keyframes rubberBand { - from { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } - - 30% { - -webkit-transform: scale3d(1.25, 0.75, 1); - transform: scale3d(1.25, 0.75, 1); - } - - 40% { - -webkit-transform: scale3d(0.75, 1.25, 1); - transform: scale3d(0.75, 1.25, 1); - } - - 50% { - -webkit-transform: scale3d(1.15, 0.85, 1); - transform: scale3d(1.15, 0.85, 1); - } - - 65% { - -webkit-transform: scale3d(.95, 1.05, 1); - transform: scale3d(.95, 1.05, 1); - } - - 75% { - -webkit-transform: scale3d(1.05, .95, 1); - transform: scale3d(1.05, .95, 1); - } - - to { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } -} - -.rubberBand { - -webkit-animation-name: rubberBand; - animation-name: rubberBand; -} - -@-webkit-keyframes shake { - from, to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - 10%, 30%, 50%, 70%, 90% { - -webkit-transform: translate3d(-10px, 0, 0); - transform: translate3d(-10px, 0, 0); - } - - 20%, 40%, 60%, 80% { - -webkit-transform: translate3d(10px, 0, 0); - transform: translate3d(10px, 0, 0); - } -} - -@keyframes shake { - from, to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - 10%, 30%, 50%, 70%, 90% { - -webkit-transform: translate3d(-10px, 0, 0); - transform: translate3d(-10px, 0, 0); - } - - 20%, 40%, 60%, 80% { - -webkit-transform: translate3d(10px, 0, 0); - transform: translate3d(10px, 0, 0); - } -} - -.shake { - -webkit-animation-name: shake; - animation-name: shake; -} - -@-webkit-keyframes headShake { - 0% { - -webkit-transform: translateX(0); - transform: translateX(0); - } - - 6.5% { - -webkit-transform: translateX(-6px) rotateY(-9deg); - transform: translateX(-6px) rotateY(-9deg); - } - - 18.5% { - -webkit-transform: translateX(5px) rotateY(7deg); - transform: translateX(5px) rotateY(7deg); - } - - 31.5% { - -webkit-transform: translateX(-3px) rotateY(-5deg); - transform: translateX(-3px) rotateY(-5deg); - } - - 43.5% { - -webkit-transform: translateX(2px) rotateY(3deg); - transform: translateX(2px) rotateY(3deg); - } - - 50% { - -webkit-transform: translateX(0); - transform: translateX(0); - } -} - -@keyframes headShake { - 0% { - -webkit-transform: translateX(0); - transform: translateX(0); - } - - 6.5% { - -webkit-transform: translateX(-6px) rotateY(-9deg); - transform: translateX(-6px) rotateY(-9deg); - } - - 18.5% { - -webkit-transform: translateX(5px) rotateY(7deg); - transform: translateX(5px) rotateY(7deg); - } - - 31.5% { - -webkit-transform: translateX(-3px) rotateY(-5deg); - transform: translateX(-3px) rotateY(-5deg); - } - - 43.5% { - -webkit-transform: translateX(2px) rotateY(3deg); - transform: translateX(2px) rotateY(3deg); - } - - 50% { - -webkit-transform: translateX(0); - transform: translateX(0); - } -} - -.headShake { - -webkit-animation-timing-function: ease-in-out; - animation-timing-function: ease-in-out; - -webkit-animation-name: headShake; - animation-name: headShake; -} - -@-webkit-keyframes swing { - 20% { - -webkit-transform: rotate3d(0, 0, 1, 15deg); - transform: rotate3d(0, 0, 1, 15deg); - } - - 40% { - -webkit-transform: rotate3d(0, 0, 1, -10deg); - transform: rotate3d(0, 0, 1, -10deg); - } - - 60% { - -webkit-transform: rotate3d(0, 0, 1, 5deg); - transform: rotate3d(0, 0, 1, 5deg); - } - - 80% { - -webkit-transform: rotate3d(0, 0, 1, -5deg); - transform: rotate3d(0, 0, 1, -5deg); - } - - to { - -webkit-transform: rotate3d(0, 0, 1, 0deg); - transform: rotate3d(0, 0, 1, 0deg); - } -} - -@keyframes swing { - 20% { - -webkit-transform: rotate3d(0, 0, 1, 15deg); - transform: rotate3d(0, 0, 1, 15deg); - } - - 40% { - -webkit-transform: rotate3d(0, 0, 1, -10deg); - transform: rotate3d(0, 0, 1, -10deg); - } - - 60% { - -webkit-transform: rotate3d(0, 0, 1, 5deg); - transform: rotate3d(0, 0, 1, 5deg); - } - - 80% { - -webkit-transform: rotate3d(0, 0, 1, -5deg); - transform: rotate3d(0, 0, 1, -5deg); - } - - to { - -webkit-transform: rotate3d(0, 0, 1, 0deg); - transform: rotate3d(0, 0, 1, 0deg); - } -} - -.swing { - -webkit-transform-origin: top center; - transform-origin: top center; - -webkit-animation-name: swing; - animation-name: swing; -} - -@-webkit-keyframes tada { - from { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } - - 10%, 20% { - -webkit-transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); - transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); - } - - 30%, 50%, 70%, 90% { - -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); - transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); - } - - 40%, 60%, 80% { - -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); - transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); - } - - to { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } -} - -@keyframes tada { - from { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } - - 10%, 20% { - -webkit-transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); - transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); - } - - 30%, 50%, 70%, 90% { - -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); - transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); - } - - 40%, 60%, 80% { - -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); - transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); - } - - to { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } -} - -.tada { - -webkit-animation-name: tada; - animation-name: tada; -} - -/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ - -@-webkit-keyframes wobble { - from { - -webkit-transform: none; - transform: none; - } - - 15% { - -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); - transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); - } - - 30% { - -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); - transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); - } - - 45% { - -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); - transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); - } - - 60% { - -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); - transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); - } - - 75% { - -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); - transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); - } - - to { - -webkit-transform: none; - transform: none; - } -} - -@keyframes wobble { - from { - -webkit-transform: none; - transform: none; - } - - 15% { - -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); - transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); - } - - 30% { - -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); - transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); - } - - 45% { - -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); - transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); - } - - 60% { - -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); - transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); - } - - 75% { - -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); - transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); - } - - to { - -webkit-transform: none; - transform: none; - } -} - -.wobble { - -webkit-animation-name: wobble; - animation-name: wobble; -} - -@-webkit-keyframes jello { - from, 11.1%, to { - -webkit-transform: none; - transform: none; - } - - 22.2% { - -webkit-transform: skewX(-12.5deg) skewY(-12.5deg); - transform: skewX(-12.5deg) skewY(-12.5deg); - } - - 33.3% { - -webkit-transform: skewX(6.25deg) skewY(6.25deg); - transform: skewX(6.25deg) skewY(6.25deg); - } - - 44.4% { - -webkit-transform: skewX(-3.125deg) skewY(-3.125deg); - transform: skewX(-3.125deg) skewY(-3.125deg); - } - - 55.5% { - -webkit-transform: skewX(1.5625deg) skewY(1.5625deg); - transform: skewX(1.5625deg) skewY(1.5625deg); - } - - 66.6% { - -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg); - transform: skewX(-0.78125deg) skewY(-0.78125deg); - } - - 77.7% { - -webkit-transform: skewX(0.390625deg) skewY(0.390625deg); - transform: skewX(0.390625deg) skewY(0.390625deg); - } - - 88.8% { - -webkit-transform: skewX(-0.1953125deg) skewY(-0.1953125deg); - transform: skewX(-0.1953125deg) skewY(-0.1953125deg); - } -} - -@keyframes jello { - from, 11.1%, to { - -webkit-transform: none; - transform: none; - } - - 22.2% { - -webkit-transform: skewX(-12.5deg) skewY(-12.5deg); - transform: skewX(-12.5deg) skewY(-12.5deg); - } - - 33.3% { - -webkit-transform: skewX(6.25deg) skewY(6.25deg); - transform: skewX(6.25deg) skewY(6.25deg); - } - - 44.4% { - -webkit-transform: skewX(-3.125deg) skewY(-3.125deg); - transform: skewX(-3.125deg) skewY(-3.125deg); - } - - 55.5% { - -webkit-transform: skewX(1.5625deg) skewY(1.5625deg); - transform: skewX(1.5625deg) skewY(1.5625deg); - } - - 66.6% { - -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg); - transform: skewX(-0.78125deg) skewY(-0.78125deg); - } - - 77.7% { - -webkit-transform: skewX(0.390625deg) skewY(0.390625deg); - transform: skewX(0.390625deg) skewY(0.390625deg); - } - - 88.8% { - -webkit-transform: skewX(-0.1953125deg) skewY(-0.1953125deg); - transform: skewX(-0.1953125deg) skewY(-0.1953125deg); - } -} - -.jello { - -webkit-animation-name: jello; - animation-name: jello; - -webkit-transform-origin: center; - transform-origin: center; -} - -@-webkit-keyframes bounceIn { - from, 20%, 40%, 60%, 80%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - 0% { - opacity: 0; - -webkit-transform: scale3d(.3, .3, .3); - transform: scale3d(.3, .3, .3); - } - - 20% { - -webkit-transform: scale3d(1.1, 1.1, 1.1); - transform: scale3d(1.1, 1.1, 1.1); - } - - 40% { - -webkit-transform: scale3d(.9, .9, .9); - transform: scale3d(.9, .9, .9); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(1.03, 1.03, 1.03); - transform: scale3d(1.03, 1.03, 1.03); - } - - 80% { - -webkit-transform: scale3d(.97, .97, .97); - transform: scale3d(.97, .97, .97); - } - - to { - opacity: 1; - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } -} - -@keyframes bounceIn { - from, 20%, 40%, 60%, 80%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - 0% { - opacity: 0; - -webkit-transform: scale3d(.3, .3, .3); - transform: scale3d(.3, .3, .3); - } - - 20% { - -webkit-transform: scale3d(1.1, 1.1, 1.1); - transform: scale3d(1.1, 1.1, 1.1); - } - - 40% { - -webkit-transform: scale3d(.9, .9, .9); - transform: scale3d(.9, .9, .9); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(1.03, 1.03, 1.03); - transform: scale3d(1.03, 1.03, 1.03); - } - - 80% { - -webkit-transform: scale3d(.97, .97, .97); - transform: scale3d(.97, .97, .97); - } - - to { - opacity: 1; - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } -} - -.bounceIn { - -webkit-animation-name: bounceIn; - animation-name: bounceIn; -} - -@-webkit-keyframes bounceInDown { - from, 60%, 75%, 90%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - 0% { - opacity: 0; - -webkit-transform: translate3d(0, -3000px, 0); - transform: translate3d(0, -3000px, 0); - } - - 60% { - opacity: 1; - -webkit-transform: translate3d(0, 25px, 0); - transform: translate3d(0, 25px, 0); - } - - 75% { - -webkit-transform: translate3d(0, -10px, 0); - transform: translate3d(0, -10px, 0); - } - - 90% { - -webkit-transform: translate3d(0, 5px, 0); - transform: translate3d(0, 5px, 0); - } - - to { - -webkit-transform: none; - transform: none; - } -} - -@keyframes bounceInDown { - from, 60%, 75%, 90%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - 0% { - opacity: 0; - -webkit-transform: translate3d(0, -3000px, 0); - transform: translate3d(0, -3000px, 0); - } - - 60% { - opacity: 1; - -webkit-transform: translate3d(0, 25px, 0); - transform: translate3d(0, 25px, 0); - } - - 75% { - -webkit-transform: translate3d(0, -10px, 0); - transform: translate3d(0, -10px, 0); - } - - 90% { - -webkit-transform: translate3d(0, 5px, 0); - transform: translate3d(0, 5px, 0); - } - - to { - -webkit-transform: none; - transform: none; - } -} - -.bounceInDown { - -webkit-animation-name: bounceInDown; - animation-name: bounceInDown; -} - -@-webkit-keyframes bounceInLeft { - from, 60%, 75%, 90%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - 0% { - opacity: 0; - -webkit-transform: translate3d(-3000px, 0, 0); - transform: translate3d(-3000px, 0, 0); - } - - 60% { - opacity: 1; - -webkit-transform: translate3d(25px, 0, 0); - transform: translate3d(25px, 0, 0); - } - - 75% { - -webkit-transform: translate3d(-10px, 0, 0); - transform: translate3d(-10px, 0, 0); - } - - 90% { - -webkit-transform: translate3d(5px, 0, 0); - transform: translate3d(5px, 0, 0); - } - - to { - -webkit-transform: none; - transform: none; - } -} - -@keyframes bounceInLeft { - from, 60%, 75%, 90%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - 0% { - opacity: 0; - -webkit-transform: translate3d(-3000px, 0, 0); - transform: translate3d(-3000px, 0, 0); - } - - 60% { - opacity: 1; - -webkit-transform: translate3d(25px, 0, 0); - transform: translate3d(25px, 0, 0); - } - - 75% { - -webkit-transform: translate3d(-10px, 0, 0); - transform: translate3d(-10px, 0, 0); - } - - 90% { - -webkit-transform: translate3d(5px, 0, 0); - transform: translate3d(5px, 0, 0); - } - - to { - -webkit-transform: none; - transform: none; - } -} - -.bounceInLeft { - -webkit-animation-name: bounceInLeft; - animation-name: bounceInLeft; -} - -@-webkit-keyframes bounceInRight { - from, 60%, 75%, 90%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - from { - opacity: 0; - -webkit-transform: translate3d(3000px, 0, 0); - transform: translate3d(3000px, 0, 0); - } - - 60% { - opacity: 1; - -webkit-transform: translate3d(-25px, 0, 0); - transform: translate3d(-25px, 0, 0); - } - - 75% { - -webkit-transform: translate3d(10px, 0, 0); - transform: translate3d(10px, 0, 0); - } - - 90% { - -webkit-transform: translate3d(-5px, 0, 0); - transform: translate3d(-5px, 0, 0); - } - - to { - -webkit-transform: none; - transform: none; - } -} - -@keyframes bounceInRight { - from, 60%, 75%, 90%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - from { - opacity: 0; - -webkit-transform: translate3d(3000px, 0, 0); - transform: translate3d(3000px, 0, 0); - } - - 60% { - opacity: 1; - -webkit-transform: translate3d(-25px, 0, 0); - transform: translate3d(-25px, 0, 0); - } - - 75% { - -webkit-transform: translate3d(10px, 0, 0); - transform: translate3d(10px, 0, 0); - } - - 90% { - -webkit-transform: translate3d(-5px, 0, 0); - transform: translate3d(-5px, 0, 0); - } - - to { - -webkit-transform: none; - transform: none; - } -} - -.bounceInRight { - -webkit-animation-name: bounceInRight; - animation-name: bounceInRight; -} - -@-webkit-keyframes bounceInUp { - from, 60%, 75%, 90%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - from { - opacity: 0; - -webkit-transform: translate3d(0, 3000px, 0); - transform: translate3d(0, 3000px, 0); - } - - 60% { - opacity: 1; - -webkit-transform: translate3d(0, -20px, 0); - transform: translate3d(0, -20px, 0); - } - - 75% { - -webkit-transform: translate3d(0, 10px, 0); - transform: translate3d(0, 10px, 0); - } - - 90% { - -webkit-transform: translate3d(0, -5px, 0); - transform: translate3d(0, -5px, 0); - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -@keyframes bounceInUp { - from, 60%, 75%, 90%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - from { - opacity: 0; - -webkit-transform: translate3d(0, 3000px, 0); - transform: translate3d(0, 3000px, 0); - } - - 60% { - opacity: 1; - -webkit-transform: translate3d(0, -20px, 0); - transform: translate3d(0, -20px, 0); - } - - 75% { - -webkit-transform: translate3d(0, 10px, 0); - transform: translate3d(0, 10px, 0); - } - - 90% { - -webkit-transform: translate3d(0, -5px, 0); - transform: translate3d(0, -5px, 0); - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -.bounceInUp { - -webkit-animation-name: bounceInUp; - animation-name: bounceInUp; -} - -@-webkit-keyframes bounceOut { - 20% { - -webkit-transform: scale3d(.9, .9, .9); - transform: scale3d(.9, .9, .9); - } - - 50%, 55% { - opacity: 1; - -webkit-transform: scale3d(1.1, 1.1, 1.1); - transform: scale3d(1.1, 1.1, 1.1); - } - - to { - opacity: 0; - -webkit-transform: scale3d(.3, .3, .3); - transform: scale3d(.3, .3, .3); - } -} - -@keyframes bounceOut { - 20% { - -webkit-transform: scale3d(.9, .9, .9); - transform: scale3d(.9, .9, .9); - } - - 50%, 55% { - opacity: 1; - -webkit-transform: scale3d(1.1, 1.1, 1.1); - transform: scale3d(1.1, 1.1, 1.1); - } - - to { - opacity: 0; - -webkit-transform: scale3d(.3, .3, .3); - transform: scale3d(.3, .3, .3); - } -} - -.bounceOut { - -webkit-animation-name: bounceOut; - animation-name: bounceOut; -} - -@-webkit-keyframes bounceOutDown { - 20% { - -webkit-transform: translate3d(0, 10px, 0); - transform: translate3d(0, 10px, 0); - } - - 40%, 45% { - opacity: 1; - -webkit-transform: translate3d(0, -20px, 0); - transform: translate3d(0, -20px, 0); - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, 2000px, 0); - transform: translate3d(0, 2000px, 0); - } -} - -@keyframes bounceOutDown { - 20% { - -webkit-transform: translate3d(0, 10px, 0); - transform: translate3d(0, 10px, 0); - } - - 40%, 45% { - opacity: 1; - -webkit-transform: translate3d(0, -20px, 0); - transform: translate3d(0, -20px, 0); - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, 2000px, 0); - transform: translate3d(0, 2000px, 0); - } -} - -.bounceOutDown { - -webkit-animation-name: bounceOutDown; - animation-name: bounceOutDown; -} - -@-webkit-keyframes bounceOutLeft { - 20% { - opacity: 1; - -webkit-transform: translate3d(20px, 0, 0); - transform: translate3d(20px, 0, 0); - } - - to { - opacity: 0; - -webkit-transform: translate3d(-2000px, 0, 0); - transform: translate3d(-2000px, 0, 0); - } -} - -@keyframes bounceOutLeft { - 20% { - opacity: 1; - -webkit-transform: translate3d(20px, 0, 0); - transform: translate3d(20px, 0, 0); - } - - to { - opacity: 0; - -webkit-transform: translate3d(-2000px, 0, 0); - transform: translate3d(-2000px, 0, 0); - } -} - -.bounceOutLeft { - -webkit-animation-name: bounceOutLeft; - animation-name: bounceOutLeft; -} - -@-webkit-keyframes bounceOutRight { - 20% { - opacity: 1; - -webkit-transform: translate3d(-20px, 0, 0); - transform: translate3d(-20px, 0, 0); - } - - to { - opacity: 0; - -webkit-transform: translate3d(2000px, 0, 0); - transform: translate3d(2000px, 0, 0); - } -} - -@keyframes bounceOutRight { - 20% { - opacity: 1; - -webkit-transform: translate3d(-20px, 0, 0); - transform: translate3d(-20px, 0, 0); - } - - to { - opacity: 0; - -webkit-transform: translate3d(2000px, 0, 0); - transform: translate3d(2000px, 0, 0); - } -} - -.bounceOutRight { - -webkit-animation-name: bounceOutRight; - animation-name: bounceOutRight; -} - -@-webkit-keyframes bounceOutUp { - 20% { - -webkit-transform: translate3d(0, -10px, 0); - transform: translate3d(0, -10px, 0); - } - - 40%, 45% { - opacity: 1; - -webkit-transform: translate3d(0, 20px, 0); - transform: translate3d(0, 20px, 0); - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, -2000px, 0); - transform: translate3d(0, -2000px, 0); - } -} - -@keyframes bounceOutUp { - 20% { - -webkit-transform: translate3d(0, -10px, 0); - transform: translate3d(0, -10px, 0); - } - - 40%, 45% { - opacity: 1; - -webkit-transform: translate3d(0, 20px, 0); - transform: translate3d(0, 20px, 0); - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, -2000px, 0); - transform: translate3d(0, -2000px, 0); - } -} - -.bounceOutUp { - -webkit-animation-name: bounceOutUp; - animation-name: bounceOutUp; -} - -@-webkit-keyframes fadeIn { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} - -@keyframes fadeIn { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} - -.fadeIn { - -webkit-animation-name: fadeIn; - animation-name: fadeIn; -} - -@-webkit-keyframes fadeInDown { - from { - opacity: 0; - -webkit-transform: translate3d(0, -100%, 0); - transform: translate3d(0, -100%, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -@keyframes fadeInDown { - from { - opacity: 0; - -webkit-transform: translate3d(0, -100%, 0); - transform: translate3d(0, -100%, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -.fadeInDown { - -webkit-animation-name: fadeInDown; - animation-name: fadeInDown; -} - -@-webkit-keyframes fadeInDownBig { - from { - opacity: 0; - -webkit-transform: translate3d(0, -2000px, 0); - transform: translate3d(0, -2000px, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -@keyframes fadeInDownBig { - from { - opacity: 0; - -webkit-transform: translate3d(0, -2000px, 0); - transform: translate3d(0, -2000px, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -.fadeInDownBig { - -webkit-animation-name: fadeInDownBig; - animation-name: fadeInDownBig; -} - -@-webkit-keyframes fadeInLeft { - from { - opacity: 0; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -@keyframes fadeInLeft { - from { - opacity: 0; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -.fadeInLeft { - -webkit-animation-name: fadeInLeft; - animation-name: fadeInLeft; -} - -@-webkit-keyframes fadeInLeftBig { - from { - opacity: 0; - -webkit-transform: translate3d(-2000px, 0, 0); - transform: translate3d(-2000px, 0, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -@keyframes fadeInLeftBig { - from { - opacity: 0; - -webkit-transform: translate3d(-2000px, 0, 0); - transform: translate3d(-2000px, 0, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -.fadeInLeftBig { - -webkit-animation-name: fadeInLeftBig; - animation-name: fadeInLeftBig; -} - -@-webkit-keyframes fadeInRight { - from { - opacity: 0; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -@keyframes fadeInRight { - from { - opacity: 0; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -.fadeInRight { - -webkit-animation-name: fadeInRight; - animation-name: fadeInRight; -} - -@-webkit-keyframes fadeInRightBig { - from { - opacity: 0; - -webkit-transform: translate3d(2000px, 0, 0); - transform: translate3d(2000px, 0, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -@keyframes fadeInRightBig { - from { - opacity: 0; - -webkit-transform: translate3d(2000px, 0, 0); - transform: translate3d(2000px, 0, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -.fadeInRightBig { - -webkit-animation-name: fadeInRightBig; - animation-name: fadeInRightBig; -} - -@-webkit-keyframes fadeInUp { - from { - opacity: 0; - -webkit-transform: translate3d(0, 100%, 0); - transform: translate3d(0, 100%, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -@keyframes fadeInUp { - from { - opacity: 0; - -webkit-transform: translate3d(0, 100%, 0); - transform: translate3d(0, 100%, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -.fadeInUp { - -webkit-animation-name: fadeInUp; - animation-name: fadeInUp; -} - -@-webkit-keyframes fadeInUpBig { - from { - opacity: 0; - -webkit-transform: translate3d(0, 2000px, 0); - transform: translate3d(0, 2000px, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -@keyframes fadeInUpBig { - from { - opacity: 0; - -webkit-transform: translate3d(0, 2000px, 0); - transform: translate3d(0, 2000px, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -.fadeInUpBig { - -webkit-animation-name: fadeInUpBig; - animation-name: fadeInUpBig; -} - -@-webkit-keyframes fadeOut { - from { - opacity: 1; - } - - to { - opacity: 0; - } -} - -@keyframes fadeOut { - from { - opacity: 1; - } - - to { - opacity: 0; - } -} - -.fadeOut { - -webkit-animation-name: fadeOut; - animation-name: fadeOut; -} - -@-webkit-keyframes fadeOutDown { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, 100%, 0); - transform: translate3d(0, 100%, 0); - } -} - -@keyframes fadeOutDown { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, 100%, 0); - transform: translate3d(0, 100%, 0); - } -} - -.fadeOutDown { - -webkit-animation-name: fadeOutDown; - animation-name: fadeOutDown; -} - -@-webkit-keyframes fadeOutDownBig { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, 2000px, 0); - transform: translate3d(0, 2000px, 0); - } -} - -@keyframes fadeOutDownBig { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, 2000px, 0); - transform: translate3d(0, 2000px, 0); - } -} - -.fadeOutDownBig { - -webkit-animation-name: fadeOutDownBig; - animation-name: fadeOutDownBig; -} - -@-webkit-keyframes fadeOutLeft { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } -} - -@keyframes fadeOutLeft { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } -} - -.fadeOutLeft { - -webkit-animation-name: fadeOutLeft; - animation-name: fadeOutLeft; -} - -@-webkit-keyframes fadeOutLeftBig { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(-2000px, 0, 0); - transform: translate3d(-2000px, 0, 0); - } -} - -@keyframes fadeOutLeftBig { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(-2000px, 0, 0); - transform: translate3d(-2000px, 0, 0); - } -} - -.fadeOutLeftBig { - -webkit-animation-name: fadeOutLeftBig; - animation-name: fadeOutLeftBig; -} - -@-webkit-keyframes fadeOutRight { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } -} - -@keyframes fadeOutRight { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } -} - -.fadeOutRight { - -webkit-animation-name: fadeOutRight; - animation-name: fadeOutRight; -} - -@-webkit-keyframes fadeOutRightBig { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(2000px, 0, 0); - transform: translate3d(2000px, 0, 0); - } -} - -@keyframes fadeOutRightBig { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(2000px, 0, 0); - transform: translate3d(2000px, 0, 0); - } -} - -.fadeOutRightBig { - -webkit-animation-name: fadeOutRightBig; - animation-name: fadeOutRightBig; -} - -@-webkit-keyframes fadeOutUp { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, -100%, 0); - transform: translate3d(0, -100%, 0); - } -} - -@keyframes fadeOutUp { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, -100%, 0); - transform: translate3d(0, -100%, 0); - } -} - -.fadeOutUp { - -webkit-animation-name: fadeOutUp; - animation-name: fadeOutUp; -} - -@-webkit-keyframes fadeOutUpBig { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, -2000px, 0); - transform: translate3d(0, -2000px, 0); - } -} - -@keyframes fadeOutUpBig { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, -2000px, 0); - transform: translate3d(0, -2000px, 0); - } -} - -.fadeOutUpBig { - -webkit-animation-name: fadeOutUpBig; - animation-name: fadeOutUpBig; -} - -@-webkit-keyframes flip { - from { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -360deg); - transform: perspective(400px) rotate3d(0, 1, 0, -360deg); - -webkit-animation-timing-function: ease-out; - animation-timing-function: ease-out; - } - - 40% { - -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); - transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); - -webkit-animation-timing-function: ease-out; - animation-timing-function: ease-out; - } - - 50% { - -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); - transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } - - 80% { - -webkit-transform: perspective(400px) scale3d(.95, .95, .95); - transform: perspective(400px) scale3d(.95, .95, .95); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } - - to { - -webkit-transform: perspective(400px); - transform: perspective(400px); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } -} - -@keyframes flip { - from { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -360deg); - transform: perspective(400px) rotate3d(0, 1, 0, -360deg); - -webkit-animation-timing-function: ease-out; - animation-timing-function: ease-out; - } - - 40% { - -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); - transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); - -webkit-animation-timing-function: ease-out; - animation-timing-function: ease-out; - } - - 50% { - -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); - transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } - - 80% { - -webkit-transform: perspective(400px) scale3d(.95, .95, .95); - transform: perspective(400px) scale3d(.95, .95, .95); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } - - to { - -webkit-transform: perspective(400px); - transform: perspective(400px); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } -} - -.animated.flip { - -webkit-backface-visibility: visible; - backface-visibility: visible; - -webkit-animation-name: flip; - animation-name: flip; -} - -@-webkit-keyframes flipInX { - from { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); - transform: perspective(400px) rotate3d(1, 0, 0, 90deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - opacity: 0; - } - - 40% { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); - transform: perspective(400px) rotate3d(1, 0, 0, -20deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } - - 60% { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg); - transform: perspective(400px) rotate3d(1, 0, 0, 10deg); - opacity: 1; - } - - 80% { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg); - transform: perspective(400px) rotate3d(1, 0, 0, -5deg); - } - - to { - -webkit-transform: perspective(400px); - transform: perspective(400px); - } -} - -@keyframes flipInX { - from { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); - transform: perspective(400px) rotate3d(1, 0, 0, 90deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - opacity: 0; - } - - 40% { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); - transform: perspective(400px) rotate3d(1, 0, 0, -20deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } - - 60% { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg); - transform: perspective(400px) rotate3d(1, 0, 0, 10deg); - opacity: 1; - } - - 80% { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg); - transform: perspective(400px) rotate3d(1, 0, 0, -5deg); - } - - to { - -webkit-transform: perspective(400px); - transform: perspective(400px); - } -} - -.flipInX { - -webkit-backface-visibility: visible !important; - backface-visibility: visible !important; - -webkit-animation-name: flipInX; - animation-name: flipInX; -} - -@-webkit-keyframes flipInY { - from { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); - transform: perspective(400px) rotate3d(0, 1, 0, 90deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - opacity: 0; - } - - 40% { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg); - transform: perspective(400px) rotate3d(0, 1, 0, -20deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } - - 60% { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg); - transform: perspective(400px) rotate3d(0, 1, 0, 10deg); - opacity: 1; - } - - 80% { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg); - transform: perspective(400px) rotate3d(0, 1, 0, -5deg); - } - - to { - -webkit-transform: perspective(400px); - transform: perspective(400px); - } -} - -@keyframes flipInY { - from { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); - transform: perspective(400px) rotate3d(0, 1, 0, 90deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - opacity: 0; - } - - 40% { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg); - transform: perspective(400px) rotate3d(0, 1, 0, -20deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } - - 60% { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg); - transform: perspective(400px) rotate3d(0, 1, 0, 10deg); - opacity: 1; - } - - 80% { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg); - transform: perspective(400px) rotate3d(0, 1, 0, -5deg); - } - - to { - -webkit-transform: perspective(400px); - transform: perspective(400px); - } -} - -.flipInY { - -webkit-backface-visibility: visible !important; - backface-visibility: visible !important; - -webkit-animation-name: flipInY; - animation-name: flipInY; -} - -@-webkit-keyframes flipOutX { - from { - -webkit-transform: perspective(400px); - transform: perspective(400px); - } - - 30% { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); - transform: perspective(400px) rotate3d(1, 0, 0, -20deg); - opacity: 1; - } - - to { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); - transform: perspective(400px) rotate3d(1, 0, 0, 90deg); - opacity: 0; - } -} - -@keyframes flipOutX { - from { - -webkit-transform: perspective(400px); - transform: perspective(400px); - } - - 30% { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); - transform: perspective(400px) rotate3d(1, 0, 0, -20deg); - opacity: 1; - } - - to { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); - transform: perspective(400px) rotate3d(1, 0, 0, 90deg); - opacity: 0; - } -} - -.flipOutX { - -webkit-animation-name: flipOutX; - animation-name: flipOutX; - -webkit-backface-visibility: visible !important; - backface-visibility: visible !important; -} - -@-webkit-keyframes flipOutY { - from { - -webkit-transform: perspective(400px); - transform: perspective(400px); - } - - 30% { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg); - transform: perspective(400px) rotate3d(0, 1, 0, -15deg); - opacity: 1; - } - - to { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); - transform: perspective(400px) rotate3d(0, 1, 0, 90deg); - opacity: 0; - } -} - -@keyframes flipOutY { - from { - -webkit-transform: perspective(400px); - transform: perspective(400px); - } - - 30% { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg); - transform: perspective(400px) rotate3d(0, 1, 0, -15deg); - opacity: 1; - } - - to { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); - transform: perspective(400px) rotate3d(0, 1, 0, 90deg); - opacity: 0; - } -} - -.flipOutY { - -webkit-backface-visibility: visible !important; - backface-visibility: visible !important; - -webkit-animation-name: flipOutY; - animation-name: flipOutY; -} - -@-webkit-keyframes lightSpeedIn { - from { - -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg); - transform: translate3d(100%, 0, 0) skewX(-30deg); - opacity: 0; - } - - 60% { - -webkit-transform: skewX(20deg); - transform: skewX(20deg); - opacity: 1; - } - - 80% { - -webkit-transform: skewX(-5deg); - transform: skewX(-5deg); - opacity: 1; - } - - to { - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -@keyframes lightSpeedIn { - from { - -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg); - transform: translate3d(100%, 0, 0) skewX(-30deg); - opacity: 0; - } - - 60% { - -webkit-transform: skewX(20deg); - transform: skewX(20deg); - opacity: 1; - } - - 80% { - -webkit-transform: skewX(-5deg); - transform: skewX(-5deg); - opacity: 1; - } - - to { - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -.lightSpeedIn { - -webkit-animation-name: lightSpeedIn; - animation-name: lightSpeedIn; - -webkit-animation-timing-function: ease-out; - animation-timing-function: ease-out; -} - -@-webkit-keyframes lightSpeedOut { - from { - opacity: 1; - } - - to { - -webkit-transform: translate3d(100%, 0, 0) skewX(30deg); - transform: translate3d(100%, 0, 0) skewX(30deg); - opacity: 0; - } -} - -@keyframes lightSpeedOut { - from { - opacity: 1; - } - - to { - -webkit-transform: translate3d(100%, 0, 0) skewX(30deg); - transform: translate3d(100%, 0, 0) skewX(30deg); - opacity: 0; - } -} - -.lightSpeedOut { - -webkit-animation-name: lightSpeedOut; - animation-name: lightSpeedOut; - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; -} - -@-webkit-keyframes rotateIn { - from { - -webkit-transform-origin: center; - transform-origin: center; - -webkit-transform: rotate3d(0, 0, 1, -200deg); - transform: rotate3d(0, 0, 1, -200deg); - opacity: 0; - } - - to { - -webkit-transform-origin: center; - transform-origin: center; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -@keyframes rotateIn { - from { - -webkit-transform-origin: center; - transform-origin: center; - -webkit-transform: rotate3d(0, 0, 1, -200deg); - transform: rotate3d(0, 0, 1, -200deg); - opacity: 0; - } - - to { - -webkit-transform-origin: center; - transform-origin: center; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -.rotateIn { - -webkit-animation-name: rotateIn; - animation-name: rotateIn; -} - -@-webkit-keyframes rotateInDownLeft { - from { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: rotate3d(0, 0, 1, -45deg); - transform: rotate3d(0, 0, 1, -45deg); - opacity: 0; - } - - to { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -@keyframes rotateInDownLeft { - from { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: rotate3d(0, 0, 1, -45deg); - transform: rotate3d(0, 0, 1, -45deg); - opacity: 0; - } - - to { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -.rotateInDownLeft { - -webkit-animation-name: rotateInDownLeft; - animation-name: rotateInDownLeft; -} - -@-webkit-keyframes rotateInDownRight { - from { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: rotate3d(0, 0, 1, 45deg); - transform: rotate3d(0, 0, 1, 45deg); - opacity: 0; - } - - to { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -@keyframes rotateInDownRight { - from { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: rotate3d(0, 0, 1, 45deg); - transform: rotate3d(0, 0, 1, 45deg); - opacity: 0; - } - - to { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -.rotateInDownRight { - -webkit-animation-name: rotateInDownRight; - animation-name: rotateInDownRight; -} - -@-webkit-keyframes rotateInUpLeft { - from { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: rotate3d(0, 0, 1, 45deg); - transform: rotate3d(0, 0, 1, 45deg); - opacity: 0; - } - - to { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -@keyframes rotateInUpLeft { - from { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: rotate3d(0, 0, 1, 45deg); - transform: rotate3d(0, 0, 1, 45deg); - opacity: 0; - } - - to { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -.rotateInUpLeft { - -webkit-animation-name: rotateInUpLeft; - animation-name: rotateInUpLeft; -} - -@-webkit-keyframes rotateInUpRight { - from { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: rotate3d(0, 0, 1, -90deg); - transform: rotate3d(0, 0, 1, -90deg); - opacity: 0; - } - - to { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -@keyframes rotateInUpRight { - from { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: rotate3d(0, 0, 1, -90deg); - transform: rotate3d(0, 0, 1, -90deg); - opacity: 0; - } - - to { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -.rotateInUpRight { - -webkit-animation-name: rotateInUpRight; - animation-name: rotateInUpRight; -} - -@-webkit-keyframes rotateOut { - from { - -webkit-transform-origin: center; - transform-origin: center; - opacity: 1; - } - - to { - -webkit-transform-origin: center; - transform-origin: center; - -webkit-transform: rotate3d(0, 0, 1, 200deg); - transform: rotate3d(0, 0, 1, 200deg); - opacity: 0; - } -} - -@keyframes rotateOut { - from { - -webkit-transform-origin: center; - transform-origin: center; - opacity: 1; - } - - to { - -webkit-transform-origin: center; - transform-origin: center; - -webkit-transform: rotate3d(0, 0, 1, 200deg); - transform: rotate3d(0, 0, 1, 200deg); - opacity: 0; - } -} - -.rotateOut { - -webkit-animation-name: rotateOut; - animation-name: rotateOut; -} - -@-webkit-keyframes rotateOutDownLeft { - from { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - opacity: 1; - } - - to { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: rotate3d(0, 0, 1, 45deg); - transform: rotate3d(0, 0, 1, 45deg); - opacity: 0; - } -} - -@keyframes rotateOutDownLeft { - from { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - opacity: 1; - } - - to { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: rotate3d(0, 0, 1, 45deg); - transform: rotate3d(0, 0, 1, 45deg); - opacity: 0; - } -} - -.rotateOutDownLeft { - -webkit-animation-name: rotateOutDownLeft; - animation-name: rotateOutDownLeft; -} - -@-webkit-keyframes rotateOutDownRight { - from { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - opacity: 1; - } - - to { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: rotate3d(0, 0, 1, -45deg); - transform: rotate3d(0, 0, 1, -45deg); - opacity: 0; - } -} - -@keyframes rotateOutDownRight { - from { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - opacity: 1; - } - - to { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: rotate3d(0, 0, 1, -45deg); - transform: rotate3d(0, 0, 1, -45deg); - opacity: 0; - } -} - -.rotateOutDownRight { - -webkit-animation-name: rotateOutDownRight; - animation-name: rotateOutDownRight; -} - -@-webkit-keyframes rotateOutUpLeft { - from { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - opacity: 1; - } - - to { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: rotate3d(0, 0, 1, -45deg); - transform: rotate3d(0, 0, 1, -45deg); - opacity: 0; - } -} - -@keyframes rotateOutUpLeft { - from { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - opacity: 1; - } - - to { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: rotate3d(0, 0, 1, -45deg); - transform: rotate3d(0, 0, 1, -45deg); - opacity: 0; - } -} - -.rotateOutUpLeft { - -webkit-animation-name: rotateOutUpLeft; - animation-name: rotateOutUpLeft; -} - -@-webkit-keyframes rotateOutUpRight { - from { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - opacity: 1; - } - - to { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: rotate3d(0, 0, 1, 90deg); - transform: rotate3d(0, 0, 1, 90deg); - opacity: 0; - } -} - -@keyframes rotateOutUpRight { - from { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - opacity: 1; - } - - to { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: rotate3d(0, 0, 1, 90deg); - transform: rotate3d(0, 0, 1, 90deg); - opacity: 0; - } -} - -.rotateOutUpRight { - -webkit-animation-name: rotateOutUpRight; - animation-name: rotateOutUpRight; -} - -@-webkit-keyframes hinge { - 0% { - -webkit-transform-origin: top left; - transform-origin: top left; - -webkit-animation-timing-function: ease-in-out; - animation-timing-function: ease-in-out; - } - - 20%, 60% { - -webkit-transform: rotate3d(0, 0, 1, 80deg); - transform: rotate3d(0, 0, 1, 80deg); - -webkit-transform-origin: top left; - transform-origin: top left; - -webkit-animation-timing-function: ease-in-out; - animation-timing-function: ease-in-out; - } - - 40%, 80% { - -webkit-transform: rotate3d(0, 0, 1, 60deg); - transform: rotate3d(0, 0, 1, 60deg); - -webkit-transform-origin: top left; - transform-origin: top left; - -webkit-animation-timing-function: ease-in-out; - animation-timing-function: ease-in-out; - opacity: 1; - } - - to { - -webkit-transform: translate3d(0, 700px, 0); - transform: translate3d(0, 700px, 0); - opacity: 0; - } -} - -@keyframes hinge { - 0% { - -webkit-transform-origin: top left; - transform-origin: top left; - -webkit-animation-timing-function: ease-in-out; - animation-timing-function: ease-in-out; - } - - 20%, 60% { - -webkit-transform: rotate3d(0, 0, 1, 80deg); - transform: rotate3d(0, 0, 1, 80deg); - -webkit-transform-origin: top left; - transform-origin: top left; - -webkit-animation-timing-function: ease-in-out; - animation-timing-function: ease-in-out; - } - - 40%, 80% { - -webkit-transform: rotate3d(0, 0, 1, 60deg); - transform: rotate3d(0, 0, 1, 60deg); - -webkit-transform-origin: top left; - transform-origin: top left; - -webkit-animation-timing-function: ease-in-out; - animation-timing-function: ease-in-out; - opacity: 1; - } - - to { - -webkit-transform: translate3d(0, 700px, 0); - transform: translate3d(0, 700px, 0); - opacity: 0; - } -} - -.hinge { - -webkit-animation-name: hinge; - animation-name: hinge; -} - -/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ - -@-webkit-keyframes rollIn { - from { - opacity: 0; - -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); - transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -@keyframes rollIn { - from { - opacity: 0; - -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); - transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -.rollIn { - -webkit-animation-name: rollIn; - animation-name: rollIn; -} - -/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ - -@-webkit-keyframes rollOut { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); - transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); - } -} - -@keyframes rollOut { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); - transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); - } -} - -.rollOut { - -webkit-animation-name: rollOut; - animation-name: rollOut; -} - -@-webkit-keyframes zoomIn { - from { - opacity: 0; - -webkit-transform: scale3d(.3, .3, .3); - transform: scale3d(.3, .3, .3); - } - - 50% { - opacity: 1; - } -} - -@keyframes zoomIn { - from { - opacity: 0; - -webkit-transform: scale3d(.3, .3, .3); - transform: scale3d(.3, .3, .3); - } - - 50% { - opacity: 1; - } -} - -.zoomIn { - -webkit-animation-name: zoomIn; - animation-name: zoomIn; -} - -@-webkit-keyframes zoomInDown { - from { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); - transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); - transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -@keyframes zoomInDown { - from { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); - transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); - transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -.zoomInDown { - -webkit-animation-name: zoomInDown; - animation-name: zoomInDown; -} - -@-webkit-keyframes zoomInLeft { - from { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); - transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); - transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -@keyframes zoomInLeft { - from { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); - transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); - transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -.zoomInLeft { - -webkit-animation-name: zoomInLeft; - animation-name: zoomInLeft; -} - -@-webkit-keyframes zoomInRight { - from { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); - transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); - transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -@keyframes zoomInRight { - from { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); - transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); - transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -.zoomInRight { - -webkit-animation-name: zoomInRight; - animation-name: zoomInRight; -} - -@-webkit-keyframes zoomInUp { - from { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); - transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); - transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -@keyframes zoomInUp { - from { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); - transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); - transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -.zoomInUp { - -webkit-animation-name: zoomInUp; - animation-name: zoomInUp; -} - -@-webkit-keyframes zoomOut { - from { - opacity: 1; - } - - 50% { - opacity: 0; - -webkit-transform: scale3d(.3, .3, .3); - transform: scale3d(.3, .3, .3); - } - - to { - opacity: 0; - } -} - -@keyframes zoomOut { - from { - opacity: 1; - } - - 50% { - opacity: 0; - -webkit-transform: scale3d(.3, .3, .3); - transform: scale3d(.3, .3, .3); - } - - to { - opacity: 0; - } -} - -.zoomOut { - -webkit-animation-name: zoomOut; - animation-name: zoomOut; -} - -@-webkit-keyframes zoomOutDown { - 40% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); - transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - to { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); - transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); - -webkit-transform-origin: center bottom; - transform-origin: center bottom; - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -@keyframes zoomOutDown { - 40% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); - transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - to { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); - transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); - -webkit-transform-origin: center bottom; - transform-origin: center bottom; - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -.zoomOutDown { - -webkit-animation-name: zoomOutDown; - animation-name: zoomOutDown; -} - -@-webkit-keyframes zoomOutLeft { - 40% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); - transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); - } - - to { - opacity: 0; - -webkit-transform: scale(.1) translate3d(-2000px, 0, 0); - transform: scale(.1) translate3d(-2000px, 0, 0); - -webkit-transform-origin: left center; - transform-origin: left center; - } -} - -@keyframes zoomOutLeft { - 40% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); - transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); - } - - to { - opacity: 0; - -webkit-transform: scale(.1) translate3d(-2000px, 0, 0); - transform: scale(.1) translate3d(-2000px, 0, 0); - -webkit-transform-origin: left center; - transform-origin: left center; - } -} - -.zoomOutLeft { - -webkit-animation-name: zoomOutLeft; - animation-name: zoomOutLeft; -} - -@-webkit-keyframes zoomOutRight { - 40% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); - transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); - } - - to { - opacity: 0; - -webkit-transform: scale(.1) translate3d(2000px, 0, 0); - transform: scale(.1) translate3d(2000px, 0, 0); - -webkit-transform-origin: right center; - transform-origin: right center; - } -} - -@keyframes zoomOutRight { - 40% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); - transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); - } - - to { - opacity: 0; - -webkit-transform: scale(.1) translate3d(2000px, 0, 0); - transform: scale(.1) translate3d(2000px, 0, 0); - -webkit-transform-origin: right center; - transform-origin: right center; - } -} - -.zoomOutRight { - -webkit-animation-name: zoomOutRight; - animation-name: zoomOutRight; -} - -@-webkit-keyframes zoomOutUp { - 40% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); - transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - to { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); - transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); - -webkit-transform-origin: center bottom; - transform-origin: center bottom; - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -@keyframes zoomOutUp { - 40% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); - transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - to { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); - transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); - -webkit-transform-origin: center bottom; - transform-origin: center bottom; - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -.zoomOutUp { - -webkit-animation-name: zoomOutUp; - animation-name: zoomOutUp; -} - -@-webkit-keyframes slideInDown { - from { - -webkit-transform: translate3d(0, -100%, 0); - transform: translate3d(0, -100%, 0); - visibility: visible; - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -@keyframes slideInDown { - from { - -webkit-transform: translate3d(0, -100%, 0); - transform: translate3d(0, -100%, 0); - visibility: visible; - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -.slideInDown { - -webkit-animation-name: slideInDown; - animation-name: slideInDown; -} - -@-webkit-keyframes slideInLeft { - from { - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - visibility: visible; - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -@keyframes slideInLeft { - from { - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - visibility: visible; - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -.slideInLeft { - -webkit-animation-name: slideInLeft; - animation-name: slideInLeft; -} - -@-webkit-keyframes slideInRight { - from { - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - visibility: visible; - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -@keyframes slideInRight { - from { - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - visibility: visible; - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -.slideInRight { - -webkit-animation-name: slideInRight; - animation-name: slideInRight; -} - -@-webkit-keyframes slideInUp { - from { - -webkit-transform: translate3d(0, 100%, 0); - transform: translate3d(0, 100%, 0); - visibility: visible; - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -@keyframes slideInUp { - from { - -webkit-transform: translate3d(0, 100%, 0); - transform: translate3d(0, 100%, 0); - visibility: visible; - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -.slideInUp { - -webkit-animation-name: slideInUp; - animation-name: slideInUp; -} - -@-webkit-keyframes slideOutDown { - from { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - to { - visibility: hidden; - -webkit-transform: translate3d(0, 100%, 0); - transform: translate3d(0, 100%, 0); - } -} - -@keyframes slideOutDown { - from { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - to { - visibility: hidden; - -webkit-transform: translate3d(0, 100%, 0); - transform: translate3d(0, 100%, 0); - } -} - -.slideOutDown { - -webkit-animation-name: slideOutDown; - animation-name: slideOutDown; -} - -@-webkit-keyframes slideOutLeft { - from { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - to { - visibility: hidden; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } -} - -@keyframes slideOutLeft { - from { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - to { - visibility: hidden; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } -} - -.slideOutLeft { - -webkit-animation-name: slideOutLeft; - animation-name: slideOutLeft; -} - -@-webkit-keyframes slideOutRight { - from { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - to { - visibility: hidden; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } -} - -@keyframes slideOutRight { - from { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - to { - visibility: hidden; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } -} - -.slideOutRight { - -webkit-animation-name: slideOutRight; - animation-name: slideOutRight; -} - -@-webkit-keyframes slideOutUp { - from { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - to { - visibility: hidden; - -webkit-transform: translate3d(0, -100%, 0); - transform: translate3d(0, -100%, 0); - } -} - -@keyframes slideOutUp { - from { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - to { - visibility: hidden; - -webkit-transform: translate3d(0, -100%, 0); - transform: translate3d(0, -100%, 0); - } -} - -.slideOutUp { - -webkit-animation-name: slideOutUp; - animation-name: slideOutUp; -} diff --git a/old-client/client/less/lib/bootstrap-social/bootstrap-social.less b/old-client/client/less/lib/bootstrap-social/bootstrap-social.less deleted file mode 100755 index b0a0cf0e60..0000000000 --- a/old-client/client/less/lib/bootstrap-social/bootstrap-social.less +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Social Buttons for Bootstrap - * - * Copyright 2013-2014 Panayiotis Lipiridis - * Licensed under the MIT License - * - * https://github.com/lipis/bootstrap-social - */ - -@bs-height-base: (@line-height-computed + @padding-base-vertical * 2); -@bs-height-lg: (floor(@font-size-large * @line-height-base) + @padding-large-vertical * 2); -@bs-height-sm: (floor(@font-size-small * 1.5) + @padding-small-vertical * 2); -@bs-height-xs: (floor(@font-size-small * 1.2) + @padding-small-vertical + 1); - -.btn-social { - position: relative; - padding-left: (@bs-height-base + @padding-base-horizontal); - text-align: left; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - > :first-child { - position: absolute; - left: 0; - top: 0; - bottom: 0; - width: @bs-height-base; - line-height: (@bs-height-base + 2); - font-size: 1.6em; - text-align: center; - border-right: 1px solid rgba(0, 0, 0, 0.2); - } - &.btn-lg { - padding-left: ((@bs-height-lg + @padding-large-horizontal) - 3); - > :first-child { - line-height: @bs-height-lg; - width: @bs-height-lg; - font-size: 1.8em; - } - } - &.btn-sm { - padding-left: (@bs-height-sm + @padding-small-horizontal); - > :first-child { - line-height: @bs-height-sm; - width: @bs-height-sm; - font-size: 1.4em; - } - } - &.btn-xs { - padding-left: (@bs-height-xs + @padding-small-horizontal); - > :first-child { - line-height: @bs-height-xs; - width: @bs-height-xs; - font-size: 1.2em; - } - } -} - -.btn-social-icon { - .btn-social; - height: (@bs-height-base + 2); - width: (@bs-height-base + 2); - padding: 0; - > :first-child { - border: none; - text-align: center; - width: 100%!important; - } - &.btn-lg { - height: @bs-height-lg; - width: @bs-height-lg; - padding-left: 0; - padding-right: 0; - } - &.btn-sm { - height: (@bs-height-sm + 2); - width: (@bs-height-sm + 2); - padding-left: 0; - padding-right: 0; - } - &.btn-xs { - height: (@bs-height-xs + 2); - width: (@bs-height-xs + 2); - padding-left: 0; - padding-right: 0; - } -} - -.btn-social(@color-bg, @color: @color-bg) { - background-color: @color-bg; - .button-variant(@color, @color-bg, rgba(0,0,0,.2)); -} - - -.btn-facebook { .btn-social(#3b5998); } -.btn-github { .btn-social(#444444); } -.btn-google { .btn-social(#dd4b39); } -.btn-linkedin { .btn-social(#007bb6); } -.btn-twitter { .btn-social(#55acee); } diff --git a/old-client/client/less/lib/bootstrap/alerts.less b/old-client/client/less/lib/bootstrap/alerts.less deleted file mode 100755 index df070b8ab2..0000000000 --- a/old-client/client/less/lib/bootstrap/alerts.less +++ /dev/null @@ -1,68 +0,0 @@ -// -// Alerts -// -------------------------------------------------- - - -// Base styles -// ------------------------- - -.alert { - padding: @alert-padding; - margin-bottom: @line-height-computed; - border: 1px solid transparent; - border-radius: @alert-border-radius; - - // Headings for larger alerts - h4 { - margin-top: 0; - // Specified for the h4 to prevent conflicts of changing @headings-color - color: inherit; - } - // Provide class for links that match alerts - .alert-link { - font-weight: @alert-link-font-weight; - } - - // Improve alignment and spacing of inner content - > p, - > ul { - margin-bottom: 0; - } - > p + p { - margin-top: 5px; - } -} - -// Dismissible alerts -// -// Expand the right padding and account for the close button's positioning. - -.alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0. -.alert-dismissible { - padding-right: (@alert-padding + 20); - - // Adjust close link position - .close { - position: relative; - top: -2px; - right: -21px; - color: inherit; - } -} - -// Alternate styles -// -// Generate contextual modifier classes for colorizing the alert. - -.alert-success { - .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text); -} -.alert-info { - .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text); -} -.alert-warning { - .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text); -} -.alert-danger { - .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text); -} diff --git a/old-client/client/less/lib/bootstrap/badges.less b/old-client/client/less/lib/bootstrap/badges.less deleted file mode 100755 index 20624f30db..0000000000 --- a/old-client/client/less/lib/bootstrap/badges.less +++ /dev/null @@ -1,55 +0,0 @@ -// -// Badges -// -------------------------------------------------- - - -// Base class -.badge { - display: inline-block; - min-width: 10px; - padding: 3px 7px; - font-size: @font-size-small; - font-weight: @badge-font-weight; - color: @badge-color; - line-height: @badge-line-height; - vertical-align: baseline; - white-space: nowrap; - text-align: center; - background-color: @badge-bg; - border-radius: @badge-border-radius; - - // Empty badges collapse automatically (not available in IE8) - &:empty { - display: none; - } - - // Quick fix for badges in buttons - .btn & { - position: relative; - top: -1px; - } - .btn-xs & { - top: 0; - padding: 1px 5px; - } - - // Hover state, but only for links - a& { - &:hover, - &:focus { - color: @badge-link-hover-color; - text-decoration: none; - cursor: pointer; - } - } - - // Account for badges in navs - a.list-group-item.active > &, - .nav-pills > .active > a > & { - color: @badge-active-color; - background-color: @badge-active-bg; - } - .nav-pills > li > a > & { - margin-left: 3px; - } -} diff --git a/old-client/client/less/lib/bootstrap/bootstrap.less b/old-client/client/less/lib/bootstrap/bootstrap.less deleted file mode 100755 index 4f85a0dd36..0000000000 --- a/old-client/client/less/lib/bootstrap/bootstrap.less +++ /dev/null @@ -1,49 +0,0 @@ -// Core variables and mixins -@import "variables.less"; -@import "mixins.less"; - -// Reset and dependencies -@import "normalize.less"; -@import "print.less"; -@import "glyphicons.less"; - -// Core CSS -@import "scaffolding.less"; -@import "type.less"; -@import "code.less"; -@import "grid.less"; -@import "tables.less"; -@import "forms.less"; -@import "buttons.less"; - -// Components -@import "component-animations.less"; -@import "dropdowns.less"; -@import "button-groups.less"; -@import "input-groups.less"; -@import "navs.less"; -@import "navbar.less"; -@import "breadcrumbs.less"; -@import "pagination.less"; -@import "pager.less"; -@import "labels.less"; -@import "badges.less"; -@import "jumbotron.less"; -@import "thumbnails.less"; -@import "alerts.less"; -@import "progress-bars.less"; -@import "media.less"; -@import "list-group.less"; -@import "panels.less"; -@import "wells.less"; -@import "close.less"; - -// Components w/ JavaScript -@import "modals.less"; -@import "tooltip.less"; -@import "popovers.less"; -@import "carousel.less"; - -// Utility classes -@import "utilities.less"; -@import "responsive-utilities.less"; diff --git a/old-client/client/less/lib/bootstrap/breadcrumbs.less b/old-client/client/less/lib/bootstrap/breadcrumbs.less deleted file mode 100755 index cb01d503fb..0000000000 --- a/old-client/client/less/lib/bootstrap/breadcrumbs.less +++ /dev/null @@ -1,26 +0,0 @@ -// -// Breadcrumbs -// -------------------------------------------------- - - -.breadcrumb { - padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal; - margin-bottom: @line-height-computed; - list-style: none; - background-color: @breadcrumb-bg; - border-radius: @border-radius-base; - - > li { - display: inline-block; - - + li:before { - content: "@{breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space - padding: 0 5px; - color: @breadcrumb-color; - } - } - - > .active { - color: @breadcrumb-active-color; - } -} diff --git a/old-client/client/less/lib/bootstrap/button-groups.less b/old-client/client/less/lib/bootstrap/button-groups.less deleted file mode 100755 index fbcdf457b5..0000000000 --- a/old-client/client/less/lib/bootstrap/button-groups.less +++ /dev/null @@ -1,247 +0,0 @@ -// -// Button groups -// -------------------------------------------------- - -// Make the div behave like a button -.btn-group, -.btn-group-vertical { - position: relative; - display: inline-block; - vertical-align: middle; // match .btn alignment given font-size hack above - > .btn { - position: relative; - float: left; - // Bring the "active" button to the front - &:hover, - &:focus, - &:active, - &.active { - z-index: 2; - } - &:focus { - // Remove focus outline when dropdown JS adds it after closing the menu - outline: 0; - } - } -} - -// Prevent double borders when buttons are next to each other -.btn-group { - .btn + .btn, - .btn + .btn-group, - .btn-group + .btn, - .btn-group + .btn-group { - margin-left: -1px; - } -} - -// Optional: Group multiple button groups together for a toolbar -.btn-toolbar { - margin-left: -5px; // Offset the first child's margin - &:extend(.clearfix all); - - .btn-group, - .input-group { - float: left; - } - > .btn, - > .btn-group, - > .input-group { - margin-left: 5px; - } -} - -.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { - border-radius: 0; -} - -// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match -.btn-group > .btn:first-child { - margin-left: 0; - &:not(:last-child):not(.dropdown-toggle) { - .border-right-radius(0); - } -} -// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it -.btn-group > .btn:last-child:not(:first-child), -.btn-group > .dropdown-toggle:not(:first-child) { - .border-left-radius(0); -} - -// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group) -.btn-group > .btn-group { - float: left; -} -.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group > .btn-group:first-child { - > .btn:last-child, - > .dropdown-toggle { - .border-right-radius(0); - } -} -.btn-group > .btn-group:last-child > .btn:first-child { - .border-left-radius(0); -} - -// On active and open, don't show outline -.btn-group .dropdown-toggle:active, -.btn-group.open .dropdown-toggle { - outline: 0; -} - - -// Sizing -// -// Remix the default button sizing classes into new ones for easier manipulation. - -.btn-group-xs > .btn { &:extend(.btn-xs); } -.btn-group-sm > .btn { &:extend(.btn-sm); } -.btn-group-lg > .btn { &:extend(.btn-lg); } - - -// Split button dropdowns -// ---------------------- - -// Give the line between buttons some depth -.btn-group > .btn + .dropdown-toggle { - padding-left: 8px; - padding-right: 8px; -} -.btn-group > .btn-lg + .dropdown-toggle { - padding-left: 12px; - padding-right: 12px; -} - -// The clickable button for toggling the menu -// Remove the gradient and set the same inset shadow as the :active state -.btn-group.open .dropdown-toggle { - .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); - - // Show no shadow for `.btn-link` since it has no other button styles. - &.btn-link { - .box-shadow(none); - } -} - - -// Reposition the caret -.btn .caret { - margin-left: 0; -} -// Carets in other button sizes -.btn-lg .caret { - border-width: @caret-width-large @caret-width-large 0; - border-bottom-width: 0; -} -// Upside down carets for .dropup -.dropup .btn-lg .caret { - border-width: 0 @caret-width-large @caret-width-large; -} - - -// Vertical button groups -// ---------------------- - -.btn-group-vertical { - > .btn, - > .btn-group, - > .btn-group > .btn { - display: block; - float: none; - width: 100%; - max-width: 100%; - } - - // Clear floats so dropdown menus can be properly placed - > .btn-group { - &:extend(.clearfix all); - > .btn { - float: none; - } - } - - > .btn + .btn, - > .btn + .btn-group, - > .btn-group + .btn, - > .btn-group + .btn-group { - margin-top: -1px; - margin-left: 0; - } -} - -.btn-group-vertical > .btn { - &:not(:first-child):not(:last-child) { - border-radius: 0; - } - &:first-child:not(:last-child) { - border-top-right-radius: @border-radius-base; - .border-bottom-radius(0); - } - &:last-child:not(:first-child) { - border-bottom-left-radius: @border-radius-base; - .border-top-radius(0); - } -} -.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group-vertical > .btn-group:first-child:not(:last-child) { - > .btn:last-child, - > .dropdown-toggle { - .border-bottom-radius(0); - } -} -.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { - .border-top-radius(0); -} - - -// Justified button groups -// ---------------------- - -.btn-group-justified { - display: table; - width: 100%; - table-layout: fixed; - border-collapse: separate; - > .btn, - > .btn-group { - float: none; - display: table-cell; - width: 1%; - } - > .btn-group .btn { - width: 100%; - } - - > .btn-group .dropdown-menu { - left: auto; - } -} - - -// Checkbox and radio options -// -// In order to support the browser's form validation feedback, powered by the -// `required` attribute, we have to "hide" the inputs via `clip`. We cannot use -// `display: none;` or `visibility: hidden;` as that also hides the popover. -// Simply visually hiding the inputs via `opacity` would leave them clickable in -// certain cases which is prevented by using `clip` and `pointer-events`. -// This way, we ensure a DOM element is visible to position the popover from. -// -// See https://github.com/twbs/bootstrap/pull/12794 and -// https://github.com/twbs/bootstrap/pull/14559 for more information. - -[data-toggle="buttons"] { - > .btn, - > .btn-group > .btn { - input[type="radio"], - input[type="checkbox"] { - position: absolute; - clip: rect(0,0,0,0); - pointer-events: none; - } - } -} diff --git a/old-client/client/less/lib/bootstrap/buttons.less b/old-client/client/less/lib/bootstrap/buttons.less deleted file mode 100755 index 66a19bd066..0000000000 --- a/old-client/client/less/lib/bootstrap/buttons.less +++ /dev/null @@ -1,160 +0,0 @@ -// -// Buttons -// -------------------------------------------------- - - -// Base styles -// -------------------------------------------------- - -.btn { - display: inline-block; - margin-bottom: 0; // For input.btn - font-weight: @btn-font-weight; - text-align: center; - vertical-align: middle; - touch-action: manipulation; - cursor: pointer; - background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 - border: 1px solid transparent; - white-space: nowrap; - .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @border-radius-base); - .user-select(none); - - &, - &:active, - &.active { - &:focus, - &.focus { - .tab-focus(); - } - } - - &:hover, - &:focus, - &.focus { - color: @gray-lighter; - text-decoration: none; - } - - &:active, - &.active { - outline: 0; - background-image: none; - .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); - } - - &.disabled, - &[disabled], - fieldset[disabled] & { - cursor: @cursor-disabled; - pointer-events: none; // Future-proof disabling of clicks - .opacity(.65); - .box-shadow(none); - } -} - - -// Alternate buttons -// -------------------------------------------------- - -.btn-default { - .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border); -} -.btn-primary { - .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border); -} -// Success appears as green -.btn-success { - .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border); -} -// Info appears as blue-green -.btn-info { - .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border); -} -// Warning appears as orange -.btn-warning { - .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border); -} -// Danger and error appear as red -.btn-danger { - .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border); -} - - -// Link buttons -// ------------------------- - -// Make a button look and behave like a link -.btn-link { - color: @link-color; - font-weight: normal; - border-radius: 0; - - &, - &:active, - &.active, - &[disabled], - fieldset[disabled] & { - background-color: transparent; - .box-shadow(none); - } - &, - &:hover, - &:focus, - &:active { - border-color: transparent; - } - &:hover, - &:focus { - color: @link-hover-color; - text-decoration: underline; - background-color: transparent; - } - &[disabled], - fieldset[disabled] & { - &:hover, - &:focus { - color: @btn-link-disabled-color; - text-decoration: none; - } - } -} - - -// Button Sizes -// -------------------------------------------------- - -.btn-lg { - // line-height: ensure even-numbered height of button next to large input - .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large); -} -.btn-sm { - // line-height: ensure proper height of button next to small input - .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small); -} -.btn-xs { - .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @border-radius-small); -} - - -// Block button -// -------------------------------------------------- - -.btn-block { - display: block; - width: 100%; -} - -// Vertically space out multiple block buttons -.btn-block + .btn-block { - margin-top: 5px; -} - -// Specificity overrides -input[type="submit"], -input[type="reset"], -input[type="button"] { - &.btn-block { - width: 100%; - } -} diff --git a/old-client/client/less/lib/bootstrap/carousel.less b/old-client/client/less/lib/bootstrap/carousel.less deleted file mode 100755 index 5724d8a56e..0000000000 --- a/old-client/client/less/lib/bootstrap/carousel.less +++ /dev/null @@ -1,267 +0,0 @@ -// -// Carousel -// -------------------------------------------------- - - -// Wrapper for the slide container and indicators -.carousel { - position: relative; -} - -.carousel-inner { - position: relative; - overflow: hidden; - width: 100%; - - > .item { - display: none; - position: relative; - .transition(.6s ease-in-out left); - - // Account for jankitude on images - > img, - > a > img { - &:extend(.img-responsive); - line-height: 1; - } - - // WebKit CSS3 transforms for supported devices - @media all and (transform-3d), (-webkit-transform-3d) { - transition: transform .6s ease-in-out; - backface-visibility: hidden; - perspective: 1000; - - &.next, - &.active.right { - transform: translate3d(100%, 0, 0); - left: 0; - } - &.prev, - &.active.left { - transform: translate3d(-100%, 0, 0); - left: 0; - } - &.next.left, - &.prev.right, - &.active { - transform: translate3d(0, 0, 0); - left: 0; - } - } - } - - > .active, - > .next, - > .prev { - display: block; - } - - > .active { - left: 0; - } - - > .next, - > .prev { - position: absolute; - top: 0; - width: 100%; - } - - > .next { - left: 100%; - } - > .prev { - left: -100%; - } - > .next.left, - > .prev.right { - left: 0; - } - - > .active.left { - left: -100%; - } - > .active.right { - left: 100%; - } - -} - -// Left/right controls for nav -// --------------------------- - -.carousel-control { - position: absolute; - top: 0; - left: 0; - bottom: 0; - width: @carousel-control-width; - .opacity(@carousel-control-opacity); - font-size: @carousel-control-font-size; - color: @carousel-control-color; - text-align: center; - text-shadow: @carousel-text-shadow; - // We can't have this transition here because WebKit cancels the carousel - // animation if you trip this while in the middle of another animation. - - // Set gradients for backgrounds - &.left { - #gradient > .horizontal(@start-color: rgba(0,0,0,.5); @end-color: rgba(0,0,0,.0001)); - } - &.right { - left: auto; - right: 0; - #gradient > .horizontal(@start-color: rgba(0,0,0,.0001); @end-color: rgba(0,0,0,.5)); - } - - // Hover/focus state - &:hover, - &:focus { - outline: 0; - color: @carousel-control-color; - text-decoration: none; - .opacity(.9); - } - - // Toggles - .icon-prev, - .icon-next, - .glyphicon-chevron-left, - .glyphicon-chevron-right { - position: absolute; - top: 50%; - z-index: 5; - display: inline-block; - } - .icon-prev, - .glyphicon-chevron-left { - left: 50%; - margin-left: -10px; - } - .icon-next, - .glyphicon-chevron-right { - right: 50%; - margin-right: -10px; - } - .icon-prev, - .icon-next { - width: 20px; - height: 20px; - margin-top: -10px; - font-family: serif; - } - - - .icon-prev { - &:before { - content: '\2039';// SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039) - } - } - .icon-next { - &:before { - content: '\203a';// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A) - } - } -} - -// Optional indicator pips -// -// Add an unordered list with the following class and add a list item for each -// slide your carousel holds. - -.carousel-indicators { - position: absolute; - bottom: 10px; - left: 50%; - z-index: 15; - width: 60%; - margin-left: -30%; - padding-left: 0; - list-style: none; - text-align: center; - - li { - display: inline-block; - width: 10px; - height: 10px; - margin: 1px; - text-indent: -999px; - border: 1px solid @carousel-indicator-border-color; - border-radius: 10px; - cursor: pointer; - - // IE8-9 hack for event handling - // - // Internet Explorer 8-9 does not support clicks on elements without a set - // `background-color`. We cannot use `filter` since that's not viewed as a - // background color by the browser. Thus, a hack is needed. - // - // For IE8, we set solid black as it doesn't support `rgba()`. For IE9, we - // set alpha transparency for the best results possible. - background-color: #000 \9; // IE8 - background-color: rgba(0,0,0,0); // IE9 - } - .active { - margin: 0; - width: 12px; - height: 12px; - background-color: @carousel-indicator-active-bg; - } -} - -// Optional captions -// ----------------------------- -// Hidden by default for smaller viewports -.carousel-caption { - position: absolute; - left: 15%; - right: 15%; - bottom: 20px; - z-index: 10; - padding-top: 20px; - padding-bottom: 20px; - color: @carousel-caption-color; - text-align: center; - text-shadow: @carousel-text-shadow; - & .btn { - text-shadow: none; // No shadow for button elements in carousel-caption - } -} - - -// Scale up controls for tablets and up -@media screen and (min-width: @screen-sm-min) { - - // Scale up the controls a smidge - .carousel-control { - .glyphicon-chevron-left, - .glyphicon-chevron-right, - .icon-prev, - .icon-next { - width: 30px; - height: 30px; - margin-top: -15px; - font-size: 30px; - } - .glyphicon-chevron-left, - .icon-prev { - margin-left: -15px; - } - .glyphicon-chevron-right, - .icon-next { - margin-right: -15px; - } - } - - // Show and left align the captions - .carousel-caption { - left: 20%; - right: 20%; - padding-bottom: 30px; - } - - // Move up the indicators - .carousel-indicators { - bottom: 20px; - } -} diff --git a/old-client/client/less/lib/bootstrap/close.less b/old-client/client/less/lib/bootstrap/close.less deleted file mode 100755 index 9b4e74f2b8..0000000000 --- a/old-client/client/less/lib/bootstrap/close.less +++ /dev/null @@ -1,33 +0,0 @@ -// -// Close icons -// -------------------------------------------------- - - -.close { - float: right; - font-size: (@font-size-base * 1.5); - font-weight: @close-font-weight; - line-height: 1; - color: @close-color; - text-shadow: @close-text-shadow; - .opacity(.2); - - &:hover, - &:focus { - color: @close-color; - text-decoration: none; - cursor: pointer; - .opacity(.5); - } - - // Additional properties for button version - // iOS requires the button element instead of an anchor tag. - // If you want the anchor version, it requires `href="#"`. - button& { - padding: 0; - cursor: pointer; - background: transparent; - border: 0; - -webkit-appearance: none; - } -} diff --git a/old-client/client/less/lib/bootstrap/code.less b/old-client/client/less/lib/bootstrap/code.less deleted file mode 100755 index a08b4d48c4..0000000000 --- a/old-client/client/less/lib/bootstrap/code.less +++ /dev/null @@ -1,69 +0,0 @@ -// -// Code (inline and block) -// -------------------------------------------------- - - -// Inline and block code styles -code, -kbd, -pre, -samp { - font-family: @font-family-monospace; -} - -// Inline code -code { - padding: 2px 4px; - font-size: 90%; - color: @code-color; - background-color: @code-bg; - border-radius: @border-radius-base; -} - -// User input typically entered via keyboard -kbd { - padding: 2px 4px; - font-size: 90%; - color: @kbd-color; - background-color: @kbd-bg; - border-radius: @border-radius-small; - box-shadow: inset 0 -1px 0 rgba(0,0,0,.25); - - kbd { - padding: 0; - font-size: 100%; - font-weight: bold; - box-shadow: none; - } -} - -// Blocks of code -pre { - display: block; - padding: ((@line-height-computed - 1) / 2); - margin: 0 0 (@line-height-computed / 2); - font-size: (@font-size-base - 1); // 14px to 13px - line-height: @line-height-base; - word-break: break-all; - word-wrap: break-word; - color: @pre-color; - background-color: @pre-bg; - border: 1px solid @pre-border-color; - border-radius: @border-radius-base; - - // Account for some code outputs that place code tags in pre tags - code { - padding: 0; - font-size: inherit; - color: inherit; - white-space: pre-wrap; - background-color: transparent; - border-radius: 0; - } -} - -// Enable scrollable blocks of code -.pre-scrollable { - max-height: @pre-scrollable-max-height; - overflow-y: scroll; -} diff --git a/old-client/client/less/lib/bootstrap/component-animations.less b/old-client/client/less/lib/bootstrap/component-animations.less deleted file mode 100755 index 967715d98b..0000000000 --- a/old-client/client/less/lib/bootstrap/component-animations.less +++ /dev/null @@ -1,34 +0,0 @@ -// -// Component animations -// -------------------------------------------------- - -// Heads up! -// -// We don't use the `.opacity()` mixin here since it causes a bug with text -// fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552. - -.fade { - opacity: 0; - .transition(opacity .15s linear); - &.in { - opacity: 1; - } -} - -.collapse { - display: none; - visibility: hidden; - - &.in { display: block; visibility: visible; } - tr&.in { display: table-row; } - tbody&.in { display: table-row-group; } -} - -.collapsing { - position: relative; - height: 0; - overflow: hidden; - .transition-property(~"height, visibility"); - .transition-duration(.35s); - .transition-timing-function(ease); -} diff --git a/old-client/client/less/lib/bootstrap/dropdowns.less b/old-client/client/less/lib/bootstrap/dropdowns.less deleted file mode 100755 index 19fa1fee15..0000000000 --- a/old-client/client/less/lib/bootstrap/dropdowns.less +++ /dev/null @@ -1,214 +0,0 @@ -// -// Dropdown menus -// -------------------------------------------------- - - -// Dropdown arrow/caret -.caret { - display: inline-block; - width: 0; - height: 0; - margin-left: 2px; - vertical-align: middle; - border-top: @caret-width-base solid; - border-right: @caret-width-base solid transparent; - border-left: @caret-width-base solid transparent; -} - -// The dropdown wrapper (div) -.dropdown { - position: relative; -} - -// Prevent the focus on the dropdown toggle when closing dropdowns -.dropdown-toggle:focus { - outline: 0; -} - -// The dropdown menu (ul) -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: @zindex-dropdown; - display: none; // none by default, but block on "open" of the menu - float: left; - min-width: 0; - width: 100%; - padding: 5px 0; - margin: 2px 0 0; // override default ul - list-style: none; - font-size: @font-size-base; - text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer) - background-color: @dropdown-bg; - border: 1px solid @dropdown-fallback-border; // IE8 fallback - border: 1px solid @dropdown-border; - border-radius: @border-radius-base; - .box-shadow(0 6px 12px rgba(0,0,0,.175)); - background-clip: padding-box; - - // Aligns the dropdown menu to right - // - // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]` - &.pull-right { - right: 0; - left: auto; - } - - // Dividers (basically an hr) within the dropdown - .divider { - .nav-divider(@dropdown-divider-bg); - } - - // Links within the dropdown menu - > li > a { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: @line-height-base; - color: @dropdown-link-color; - white-space: nowrap; // prevent links from randomly breaking onto new lines - } -} - -// Hover/Focus state -.dropdown-menu > li > a { - &:hover, - &:focus { - text-decoration: none; - color: @dropdown-link-hover-color; - background-color: @dropdown-link-hover-bg; - } -} - -// Active state -.dropdown-menu > .active > a { - &, - &:hover, - &:focus { - color: @dropdown-link-active-color; - text-decoration: none; - outline: 0; - background-color: @dropdown-link-active-bg; - } -} - -// Disabled state -// -// Gray out text and ensure the hover/focus state remains gray - -.dropdown-menu > .disabled > a { - &, - &:hover, - &:focus { - color: @dropdown-link-disabled-color; - } - - // Nuke hover/focus effects - &:hover, - &:focus { - text-decoration: none; - background-color: transparent; - background-image: none; // Remove CSS gradient - .reset-filter(); - cursor: @cursor-disabled; - } -} - -// Open state for the dropdown -.open { - // Show the menu - > .dropdown-menu { - display: block; - } - - // Remove the outline when :focus is triggered - > a { - outline: 0; - } -} - -// Menu positioning -// -// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown -// menu with the parent. -.dropdown-menu-right { - left: auto; // Reset the default from `.dropdown-menu` - right: 0; -} -// With v3, we enabled auto-flipping if you have a dropdown within a right -// aligned nav component. To enable the undoing of that, we provide an override -// to restore the default dropdown menu alignment. -// -// This is only for left-aligning a dropdown menu within a `.navbar-right` or -// `.pull-right` nav component. -.dropdown-menu-left { - left: 0; - right: auto; -} - -// Dropdown section headers -.dropdown-header { - display: block; - padding: 3px 20px; - font-size: @font-size-small; - line-height: @line-height-base; - color: @dropdown-header-color; - white-space: nowrap; // as with > li > a -} - -// Backdrop to catch body clicks on mobile, etc. -.dropdown-backdrop { - position: fixed; - left: 0; - right: 0; - bottom: 0; - top: 0; - z-index: (@zindex-dropdown - 10); -} - -// Right aligned dropdowns -.pull-right > .dropdown-menu { - right: 0; - left: auto; -} - -// Allow for dropdowns to go bottom up (aka, dropup-menu) -// -// Just add .dropup after the standard .dropdown class and you're set, bro. -// TODO: abstract this so that the navbar fixed styles are not placed here? - -.dropup, -.navbar-fixed-bottom .dropdown { - // Reverse the caret - .caret { - border-top: 0; - border-bottom: @caret-width-base solid; - content: ""; - } - // Different positioning for bottom up menu - .dropdown-menu { - top: auto; - bottom: 100%; - margin-bottom: 1px; - } -} - - -// Component alignment -// -// Reiterate per navbar.less and the modified component alignment there. - -@media (min-width: @grid-float-breakpoint) { - .navbar-right { - .dropdown-menu { - .dropdown-menu-right(); - } - // Necessary for overrides of the default right aligned menu. - // Will remove come v4 in all likelihood. - .dropdown-menu-left { - .dropdown-menu-left(); - } - } -} diff --git a/old-client/client/less/lib/bootstrap/forms.less b/old-client/client/less/lib/bootstrap/forms.less deleted file mode 100755 index 38e4ce6d64..0000000000 --- a/old-client/client/less/lib/bootstrap/forms.less +++ /dev/null @@ -1,559 +0,0 @@ -// -// Forms -// -------------------------------------------------- - - -// Normalize non-controls -// -// Restyle and baseline non-control form elements. - -fieldset { - padding: 0; - margin: 0; - border: 0; - // Chrome and Firefox set a `min-width: min-content;` on fieldsets, - // so we reset that to ensure it behaves more like a standard block element. - // See https://github.com/twbs/bootstrap/issues/12359. - min-width: 0; -} - -legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: @line-height-computed; - font-size: (@font-size-base * 1.5); - line-height: inherit; - color: @legend-color; - border: 0; - border-bottom: 1px solid @legend-border-color; -} - -label { - display: inline-block; - max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141) - margin-bottom: 5px; - font-weight: bold; -} - - -// Normalize form controls -// -// While most of our form styles require extra classes, some basic normalization -// is required to ensure optimum display with or without those classes to better -// address browser inconsistencies. - -// Override content-box in Normalize (* isn't specific enough) -input[type="search"] { - .box-sizing(border-box); -} - -// Position radios and checkboxes better -input[type="radio"], -input[type="checkbox"] { - margin: 4px 0 0; - margin-top: 1px \9; // IE8-9 - line-height: normal; -} - -// Set the height of file controls to match text inputs -input[type="file"] { - display: block; -} - -// Make range inputs behave like textual form controls -input[type="range"] { - display: block; - width: 100%; -} - -// Make multiple select elements height not fixed -select[multiple], -select[size] { - height: auto; -} - -// Focus for file, radio, and checkbox -input[type="file"]:focus, -input[type="radio"]:focus, -input[type="checkbox"]:focus { - .tab-focus(); -} - -// Adjust output element -output { - display: block; - padding-top: (@padding-base-vertical + 1); - font-size: @font-size-base; - line-height: @line-height-base; - color: @input-color; -} - - -// Common form controls -// -// Shared size and type resets for form controls. Apply `.form-control` to any -// of the following form controls: -// -// select -// textarea -// input[type="text"] -// input[type="password"] -// input[type="datetime"] -// input[type="datetime-local"] -// input[type="date"] -// input[type="month"] -// input[type="time"] -// input[type="week"] -// input[type="number"] -// input[type="email"] -// input[type="url"] -// input[type="search"] -// input[type="tel"] -// input[type="color"] - -.form-control { - display: block; - width: 100%; - height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border) - padding: @padding-base-vertical @padding-base-horizontal; - font-size: @font-size-base; - line-height: @line-height-base; - color: @input-color; - background-color: @input-bg; - background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 - border: 1px solid @input-border; - border-radius: @input-border-radius; - .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); - .transition(~"border-color ease-in-out .15s, box-shadow ease-in-out .15s"); - - // Customize the `:focus` state to imitate native WebKit styles. - .form-control-focus(); - - // Placeholder - .placeholder(); - - // Disabled and read-only inputs - // - // HTML5 says that controls under a fieldset > legend:first-child won't be - // disabled if the fieldset is disabled. Due to implementation difficulty, we - // don't honor that edge case; we style them as disabled anyway. - &[disabled], - &[readonly], - fieldset[disabled] & { - cursor: @cursor-disabled; - background-color: @input-bg-disabled; - opacity: 1; // iOS fix for unreadable disabled content - } - - // Reset height for `textarea`s - textarea& { - height: auto; - } -} - - -// Search inputs in iOS -// -// This overrides the extra rounded corners on search inputs in iOS so that our -// `.form-control` class can properly style them. Note that this cannot simply -// be added to `.form-control` as it's not specific enough. For details, see -// https://github.com/twbs/bootstrap/issues/11586. - -input[type="search"] { - -webkit-appearance: none; -} - - -// Special styles for iOS temporal inputs -// -// In Mobile Safari, setting `display: block` on temporal inputs causes the -// text within the input to become vertically misaligned. -// As a workaround, we set a pixel line-height that matches the -// given height of the input. Since this fucks up everything else, we have to -// appropriately reset it for Internet Explorer and the size variations. - -input[type="date"], -input[type="time"], -input[type="datetime-local"], -input[type="month"] { - line-height: @input-height-base; - // IE8+ misaligns the text within date inputs, so we reset - line-height: @line-height-base ~"\0"; - - &.input-sm { - line-height: @input-height-small; - line-height: @line-height-small ~"\0"; - } - &.input-lg { - line-height: @input-height-large; - line-height: @line-height-large ~"\0"; - } -} - -// IE 11 hack to reverse the iOS temporal input hack. -_:-ms-fullscreen, :root input[type="date"], -_:-ms-fullscreen, :root input[type="time"], -_:-ms-fullscreen, :root input[type="datetime-local"], -_:-ms-fullscreen, :root input[type="month"] { - line-height: @line-height-base; - - &.input-sm { - line-height: @line-height-small; - } - &.input-lg { - line-height: @line-height-large; - } -} - - -// Form groups -// -// Designed to help with the organization and spacing of vertical forms. For -// horizontal forms, use the predefined grid classes. - -.form-group { - margin-bottom: 15px; -} - - -// Checkboxes and radios -// -// Indent the labels to position radios/checkboxes as hanging controls. - -.radio, -.checkbox { - position: relative; - display: block; - margin-top: 10px; - margin-bottom: 10px; - - label { - min-height: @line-height-computed; // Ensure the input doesn't jump when there is no text - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - cursor: pointer; - } -} -.radio input[type="radio"], -.radio-inline input[type="radio"], -.checkbox input[type="checkbox"], -.checkbox-inline input[type="checkbox"] { - position: absolute; - margin-left: -20px; - margin-top: 4px \9; -} - -.radio + .radio, -.checkbox + .checkbox { - margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing -} - -// Radios and checkboxes on same line -.radio-inline, -.checkbox-inline { - display: inline-block; - padding-left: 20px; - margin-bottom: 0; - vertical-align: middle; - font-weight: normal; - cursor: pointer; -} -.radio-inline + .radio-inline, -.checkbox-inline + .checkbox-inline { - margin-top: 0; - margin-left: 10px; // space out consecutive inline controls -} - -// Apply same disabled cursor tweak as for inputs -// Some special care is needed because