Compare commits

...

126 Commits

Author SHA1 Message Date
David Joy
261769cca9 Using hooks in DeleteAccount - goodnight, redux! 2019-07-09 17:10:13 -04:00
Douglas Hall
bfbcdf7ea0 Send event when discover new header link clicked (#107)
Send event when discover new header link clicked
2019-06-26 13:11:56 -04:00
Douglas Hall
68928291bc Send event when discover new header link clicked 2019-06-25 17:42:02 -04:00
edX Transifex Bot
917f7cb486 fix(i18n): update translations 2019-06-16 17:07:52 -04:00
Douglas Hall
d2ff8e1eec Merge pull request #105 from edx/douglashall/ARCH-903_905
Update header links.
2019-06-12 14:22:38 -04:00
Douglas Hall
29269fcdbb Update header links. 2019-06-12 11:29:57 -04:00
Douglas Hall
ea8b435c35 Merge pull request #104 from edx/douglashall/ARCH-873_2
Fix identityAuthenticatedUser call.
2019-06-12 10:39:52 -04:00
Douglas Hall
23e6ca6bc4 Fix identityAuthenticatedUser call. 2019-06-12 10:29:00 -04:00
albemarle
23f54949e8 fix(i18n): make detect_changed_source_translations work for both push and pull jobs (#103) 2019-06-11 12:21:35 -04:00
edX Transifex Bot
f43bc5597c fix(i18n): update translations 2019-06-09 17:02:59 -04:00
David Joy
481e38c308 Bump frontend-i18n version and use its scripts. (#100) 2019-06-07 11:13:11 -04:00
Douglas Hall
0a7d449c09 Merge pull request #101 from edx/douglashall/upgrade_analytics_1_0_4
Upgrade frontend-analytics to 1.0.4.
2019-06-06 16:27:48 -04:00
Douglas Hall
2acc15eaa6 Upgrade frontend-analytics to 1.0.4. 2019-06-06 16:19:55 -04:00
Douglas Hall
92fd7c4baa Merge pull request #99 from edx/douglashall/improve_error_boundary
Render ErrorPage component via app-level error boundary.
2019-06-06 14:10:40 -04:00
Douglas Hall
0d61d09ddd Render ErrorPage component via app-level error boundary. 2019-06-06 14:00:31 -04:00
David Joy
223ea212bc Adding tests for delete account. (#98)
- comprehensive reducer tests
- UI snapshot tests for most (all?) states
2019-06-06 09:40:35 -04:00
Douglas Hall
58519e5d7f Merge pull request #97 from edx/douglashall/ARCH-904_3
Handle Google Translate DOM manipulation error scenario.
2019-06-05 15:43:23 -04:00
Douglas Hall
4132963bfb Handle Google Translate DOM manipulation error scenario. 2019-06-05 15:34:01 -04:00
David Joy
bbe2a6ab49 Get correct path to tpaProviders. (#96) 2019-06-05 13:35:54 -04:00
David Joy
defd969a80 Fix third party auth naming issues (#95)
Two problems:

- The auth providers were being added to the wrong part of the store.
- The ThirdPartyAuth component expected them to have the name “providers”, not “authProviders” - great reason to keep names consistent!
2019-06-05 13:15:44 -04:00
David Joy
d33cb658e2 Modularizing third party auth. (#93) 2019-06-05 12:25:16 -04:00
Douglas Hall
2ac911945c Merge pull request #94 from edx/douglashall/upgrade_analytics_1_0_3
Upgrade frontend-analytics to 1.0.3.
2019-06-05 11:19:07 -04:00
Douglas Hall
a7530f84aa Upgrade frontend-analytics to 1.0.3. 2019-06-05 11:13:59 -04:00
David Joy
15708b1154 Modularizing ResetPassword. (#92) 2019-06-05 11:06:56 -04:00
David Joy
c3749e9c7f Cleaning up delete account actions and action types. (#91) 2019-06-04 20:12:25 -04:00
David Joy
30914e6d6f Misc whitespace, deletions, and simplifications. (#90) 2019-06-04 17:31:19 -04:00
David Joy
78b5991c89 Modularizing DeleteAccount (#87) 2019-06-04 16:03:42 -04:00
Adam Butterworth
0ea87767af fix: async states for tpa disconnect is per provider not shared (#89) 2019-06-04 13:02:30 -06:00
Adam Butterworth
3e50222528 feat: add scrollspy to jump nav (#88) 2019-06-04 13:02:06 -06:00
edX Transifex Bot
c912f0e5d5 fix(i18n): update translations 2019-06-04 12:20:02 -04:00
albemarle
19a88de031 refactor(i18n): nicer Makefile for translation jobs (#78) 2019-06-04 12:11:19 -04:00
Douglas Hall
8c76c3657d Merge pull request #85 from edx/douglashall/ARCH-873
Upgrade frontend-analytics to 1.0.2.
2019-06-04 11:40:30 -04:00
Douglas Hall
28c5be9897 Upgrade frontend-analytics to 1.0.2. 2019-06-04 11:34:32 -04:00
David Joy
386d2ab1f3 Tidy: step 1, shuffle common components and add a utility function (#81)
* Adding getModuleState helper method.

* Light component rearranging in preparation for moving into subdirectories.
2019-06-04 09:49:59 -04:00
edX Transifex Bot
e8369ff5b7 fix(i18n): update translations 2019-06-03 16:05:34 -04:00
Adam Butterworth
d9d14202c5 feat: handle i18n rtl with postcss-rtl (#83) 2019-06-03 13:43:56 -06:00
Douglas Hall
e1e7344533 Merge pull request #82 from edx/douglashall/upgrade_frontend_auth_5_3_3
Upgrade frontend-auth to 5.3.3.
2019-06-03 15:10:04 -04:00
Douglas Hall
029813646b Upgrade frontend-auth to 5.3.3. 2019-06-03 14:14:51 -04:00
Douglas Hall
87ea759e0e Merge pull request #80 from edx/douglashall/upgrade_i18n
Upgrade frontend-i18n to 1.1.2.
2019-06-03 09:48:51 -04:00
Douglas Hall
b67083b2e7 Upgrade frontend-i18n to 1.1.2. 2019-06-03 09:43:14 -04:00
edX Transifex Bot
14ded7cc23 fix(i18n): update translations 2019-06-02 17:08:03 -04:00
Douglas Hall
543e6ccae0 Merge pull request #77 from edx/douglashall/ARCH-738
Remove render block on fetchUserAccount.
2019-05-31 16:43:48 -04:00
Douglas Hall
e6246f3233 Remove render block on fetchUserAccount. 2019-05-31 16:04:28 -04:00
Douglas Hall
c04024ef00 Merge pull request #76 from edx/douglashall/bump_footer
Bump frontend-component-footer version.
2019-05-31 14:43:30 -04:00
Douglas Hall
81e9858be1 Bump frontend-component-footer and frontend-component-site-header version. 2019-05-31 14:37:42 -04:00
albemarle
4374c60dc6 fix(i18n): tell babel-plugin-react-intl where defineMessages and friends are now being imported from (#75) 2019-05-31 08:43:08 -04:00
Adam Butterworth
70e9bb31a5 feat: add purgecss to remove unused css from production build (#73)
* feat: add purgecss to remove unused styles

* docs: add note for purgecss to readme
2019-05-30 14:32:23 -06:00
albemarle
cee06f4e25 fix(delete my account): fix problem with password managers making modal open (#74) 2019-05-30 16:00:28 -04:00
David Joy
8070ec1acf Adding formdata polyfill for IE and Edge. (#72) 2019-05-30 13:53:04 -04:00
Douglas Hall
abf0c65be7 Upgrade frontend-i18n to pull in performance improvements (#71)
Upgrade frontend-i18n to pull in performance improvements
2019-05-30 10:53:56 -04:00
Douglas Hall
a6c671e824 Upgrade frontend-i18n to pull in performance improvements 2019-05-30 10:47:04 -04:00
albemarle
ed43fe3b37 feat(delete my account): improvements in error handling for confirmation modal (#70) 2019-05-29 17:40:37 -04:00
David Joy
a026f09b63 Polyfill URL class (#69) 2019-05-29 16:29:02 -04:00
Adam Butterworth
e322ae18aa fix: increase clarity of a11y landmarks (#68) 2019-05-29 10:58:26 -06:00
Adam Butterworth
71f007b9df Add empty states and ability to delete values from select fields (#67)
* fix: add empty states and ability to delete values from select fields

* refactor: change name of isEditable method

* refactor: make managed profile conditions clearer

* refactor: be positive
2019-05-28 12:55:23 -06:00
David Joy
747fe550c7 Adding is-es5 check (#65) 2019-05-28 13:43:34 -04:00
Douglas Hall
a84516daef Merge pull request #66 from edx/douglashall/reduce_fortawesome_bundle_size
Bundle only the icons that the app uses.
2019-05-28 13:13:39 -04:00
Douglas Hall
3d3e2a2e38 Bundle only the icons that the app uses. 2019-05-28 13:08:39 -04:00
Douglas Hall
27ba7f4a65 Merge pull request #62 from edx/douglashall/reduce_bundle_size
Reduce bundle size by upgrading frontend-component-footer.
2019-05-28 09:33:38 -04:00
Douglas Hall
c3f66ab92c Reduce bundle size by upgrading frontend-component-footer. 2019-05-24 14:43:27 -04:00
David Joy
590903bcd8 Bumping paragon and a few other version numbers. (#64) 2019-05-24 13:48:49 -04:00
David Joy
a1b1d0ea60 Addressing various UX/a11y feedback (#63)
- Adding more spacing between sections.
- Adding pencil icon to “Edit” buttons.
- adding aria-level=“3” to h6 elements
2019-05-24 10:56:44 -04:00
albemarle
8d099c0048 refactor(delete my account): use selectors instead of importing configuration (#61) 2019-05-23 17:26:03 -04:00
albemarle
b81f7ae8b9 fix(delete my account): must check if any TPA providers are connected (#60) 2019-05-22 11:05:44 -04:00
albemarle
27cc4fe692 fix(delete my account): correctly expose Third Party Auth providers (#59) 2019-05-21 17:27:20 -04:00
edX Transifex Bot
3471e81988 fix(i18n): update translations 2019-05-21 17:08:18 -04:00
David Joy
8f1231192b Fix duplicate i18n messages. (#57)
Also get file perms on i18n-concat.js correct so they can be called by make.
2019-05-21 16:48:58 -04:00
David Joy
994dbb6904 Update frontend-i18n dependency. (#56) 2019-05-21 16:09:39 -04:00
David Joy
f251ac7b7e Use frontend-i18n package instead of local i18n directory. (#54)
Delete the local copy of the i18n library.  Note: the package.json path MUST be updated prior to merging this PR.
2019-05-21 15:29:21 -04:00
albemarle
f14206bdf4 fix: add order history url (#55) 2019-05-20 17:06:01 -04:00
albemarle
cae761a00d fix(routing): let routing find error page and not found page (#53) 2019-05-20 11:36:45 -04:00
albemarle
879c9a3b33 fix(delete my account): url encode (#52) 2019-05-20 10:58:27 -04:00
edX Transifex Bot
faeed0ec13 fix(i18n): update translations 2019-05-19 17:03:28 -04:00
Douglas Hall
a65efe3bb4 Merge pull request #50 from edx/douglashall/ARCH-815
Fix password reset.
2019-05-16 16:38:16 -04:00
Douglas Hall
08d079ce40 Fix password reset. 2019-05-16 16:17:37 -04:00
albemarle
feae2f635a fix(delete account): keep confirmation dialog open on error (#49) 2019-05-16 15:40:36 -04:00
Adam Stankiewicz
da3acc278d Merge pull request #42 from edx/astankiewicz/update-footer
Update @edx/frontend-component-footer, add enterpriseMarketingLink and socialLinks prop
2019-05-16 14:45:59 -04:00
Adam Stankiewicz
a705e22d62 update configuration proptypes 2019-05-16 11:31:52 -07:00
Adam Stankiewicz
1db817e1c2 use configuration prop instead of process.env 2019-05-16 11:26:59 -07:00
Adam Stankiewicz
e60c24e476 upgrade footer component 2019-05-16 11:18:18 -07:00
Adam Stankiewicz
8c3a3c284b Updates for config vars 2019-05-16 11:18:18 -07:00
Adam Stankiewicz
b90deae9f6 Update @edx/frontend-component-footer, add enterpriseMarketingLink and socialLinks prop 2019-05-16 11:18:18 -07:00
albemarle
ea55ceff05 fix: correctly expose is_active field (#48) 2019-05-16 13:56:14 -04:00
Douglas Hall
6f9e94885b Merge pull request #47 from edx/douglashall/ARCH-789
Add newrelic sourcemap upload.
2019-05-16 12:55:48 -04:00
Douglas Hall
882a13fa76 Add newrelic sourcemap upload. 2019-05-16 11:45:51 -04:00
albemarle
bc3085b141 feat: delete my account (#37) 2019-05-16 10:43:08 -04:00
Douglas Hall
f1f9c86b15 Merge pull request #46 from edx/douglashall/ARCH-729
Fix social links in footer.
2019-05-15 17:07:49 -04:00
Douglas Hall
a437f9c91a Fix social links in footer. 2019-05-15 16:32:46 -04:00
Adam Butterworth
91d7b98e08 fix: add xhr header to eliminate redirects on setlang (#45) 2019-05-15 14:26:31 -06:00
Adam Butterworth
a93dc4e3b1 feat: add jump nav (#43) 2019-05-15 13:08:53 -06:00
Douglas Hall
4ba3414bd8 Merge pull request #44 from edx/douglashall/ARCH-775
Upgrade frontend-logging
2019-05-15 14:59:37 -04:00
Douglas Hall
d4d10287d4 Upgrade frontend-logging 2019-05-15 14:54:46 -04:00
Douglas Hall
0dfd8d8558 Merge pull request #41 from edx/douglashall/ARCH-802
Display duplicate TPA provider message.
2019-05-15 11:07:37 -04:00
Douglas Hall
c60c048c24 Display duplicate TPA provider message. 2019-05-15 11:02:20 -04:00
Robert Raposa
c57213714b Merge pull request #40 from edx/robrap/ARCH-748-remove-source
fix(i18n): remove transifex_input.json
2019-05-14 15:17:41 -04:00
Robert Raposa
777193f816 fix(i18n): remove transifex_input.json
This file will be generated before being pushed to Transifex. We
will not merge it to the repo.

ARCH-748
2019-05-14 15:03:45 -04:00
Robert Raposa
8c564b3d96 Merge pull request #39 from edx/robrap/ARCH-748-update-i18n-makefile
fix(i18n): update translation make targets
2019-05-14 14:43:26 -04:00
Robert Raposa
364684b0c8 fix(i18n): update translation make targets
* Update extract_translations to also concat
* Update push_translations to actually push to Transifex

ARCH-748
2019-05-14 14:33:28 -04:00
Nimisha Asthagiri
8abb8d4e5d Merge pull request #38 from edx/arch/beta-language-banner-followup
Follow-ups on Beta Language banner
2019-05-14 10:22:21 -04:00
Nimisha Asthagiri
932d47550e Follow-ups on Beta Language banner 2019-05-13 18:41:07 -04:00
Adam Butterworth
ab62699148 fix: send social link value when empty string is supplied (#36)
* fix: send social link value when empty string is supplied

* refactor: add clarity
2019-05-13 14:37:13 -06:00
Adam Butterworth
24819bc5df fix: assignment of thirdPartyAuth third time (#35) 2019-05-13 13:11:44 -06:00
Adam Butterworth
48ba152fb7 fix: proper assignment of thirdpartauthproviders (#34) 2019-05-13 12:18:22 -06:00
Adam Butterworth
741a9632f8 fix: refetch thirdPartyAuthProviders after disconnection (#33) 2019-05-13 11:44:43 -06:00
Nimisha Asthagiri
125113a154 Merge pull request #31 from edx/arch/router-default
Default URL goes to Account Settings
2019-05-13 12:03:53 -04:00
Douglas Hall
b1a84572fc Merge pull request #32 from edx/douglashall/update_dependencies
Update some dependencies.
2019-05-13 12:03:33 -04:00
Douglas Hall
ae8c9e2893 Update some dependencies. 2019-05-13 11:59:12 -04:00
Nimisha Asthagiri
945ef15b97 Default URL goes to Account Settings 2019-05-13 11:55:20 -04:00
Adam Butterworth
9837b85dce Fix auth disconnection add error states (#30)
* fix: properly disconnect auth

* fix: add error handling and states
2019-05-13 09:49:11 -06:00
Adam Butterworth
2e83d33de2 fix: saving an account setting will no longer delete a time zone pref… (#29)
* fix: saving an account setting will no longer delete a time zone preference

* Update service.js
2019-05-13 09:44:53 -06:00
Robert Raposa
c8d84fc5ab Merge pull request #26 from edx/robrap/ARCH-785-analytics-event
ARCH-785: add analytics event edx.user.settings.viewed
2019-05-13 11:38:24 -04:00
Douglas Hall
bdd87dfaa7 Merge pull request #28 from edx/douglashall/ARCH-694
Add recovery email field.
2019-05-13 11:36:49 -04:00
Douglas Hall
563f760e16 Add recovery email field. 2019-05-13 11:31:34 -04:00
Nimisha Asthagiri
bc41541848 Merge pull request #24 from edx/arch/beta-language-banner
Support for Beta Language Banner
2019-05-13 09:28:02 -04:00
Nimisha Asthagiri
e28f17d061 Refactor BetaLanguageBanner as a separate React Component 2019-05-11 12:01:41 -04:00
Nimisha Asthagiri
58c375b83a Support for Beta Language Banner 2019-05-11 12:01:40 -04:00
Adam Butterworth
0cd9f08539 feat: add timezone groups (#27)
* feat: add groupings for time zones

* fix: delete old constants

* fix: linting
2019-05-10 14:19:06 -06:00
Douglas Hall
e3db2b72e6 Make certain field uneditable for enterprise learners. (#23)
Make certain field uneditable for enterprise learners.
2019-05-10 15:42:11 -04:00
Douglas Hall
d353d8ecf0 Make certain field uneditable for enterprise learners. 2019-05-10 15:37:40 -04:00
Adam Butterworth
cfe1be3361 fix: verify parity (#25)
* fix: style and order update

* fix: password reset sends email along with request

* fix: add best guess for help text
2019-05-10 13:17:37 -06:00
Robert Raposa
1a17ac4934 add analytics event edx.user.settings.viewed 2019-05-10 14:44:01 -04:00
Adam Butterworth
efb99185e3 feat: add time zone (#22)
* feat: add timezone

* feat: add timezone swap to one redux action set

* fix: remove commented code

* fix: add help text

* fix: add better comment

* fix: allow user to unset the timezone
2019-05-08 15:39:36 -06:00
David Joy
f3693c156c ENVIRONMENT -> NODE_ENV 2019-05-08 14:22:58 -04:00
David Joy
19c12118b3 Site language dropdown - I hope. (#20)
* Updating i18n-loader with all our languages.

* Site language dropdown done.  I hope.
2019-05-08 13:51:17 -04:00
Adam Butterworth
13046c717a feat: upgrade frontend-auth (#21) 2019-05-08 09:59:47 -06:00
Adam Butterworth
c72fd2aceb Update README.rst 2019-05-08 11:34:10 -04:00
103 changed files with 6634 additions and 2844 deletions

View File

@@ -12,13 +12,28 @@
],
"plugins": [
"transform-object-rest-spread",
"transform-class-properties"
"transform-class-properties",
["transform-imports", {
"@fortawesome/free-brands-svg-icons": {
"transform": "@fortawesome/free-brands-svg-icons/${member}",
"skipDefaultConversion": true
},
"@fortawesome/free-regular-svg-icons": {
"transform": "@fortawesome/free-regular-svg-icons/${member}",
"skipDefaultConversion": true
},
"@fortawesome/free-solid-svg-icons": {
"transform": "@fortawesome/free-solid-svg-icons/${member}",
"skipDefaultConversion": true
}
}]
],
"env": {
"i18n": {
"plugins": [
["react-intl", {
"messagesDir": "./temp"
"messagesDir": "./temp/babel-plugin-react-intl",
"moduleSourceName": "@edx/frontend-i18n"
}]
]
}

2
.gitignore vendored
View File

@@ -6,6 +6,8 @@ npm-debug.log
coverage
dist/
src/i18n/transifex_input.json
temp/babel-plugin-react-intl
### pyenv ###
.python-version

View File

@@ -17,6 +17,7 @@ script:
- npm run lint
- npm run test
- npm run build
- npm run is-es5
after_success:
- npm run coveralls
- codecov

View File

@@ -1,36 +1,50 @@
extract_translations: ## no prerequisites so we can control order of operations
echo "We have to define this target due to tooling assumptions"
echo "Also we have to npm install using this hook b/c there's no other place for it in the current setup"
transifex_resource = frontend-app-account
transifex_langs = "ar,fr,es_419,zh_CN"
transifex_utils = ./node_modules/.bin/transifex-utils.js
i18n = ./src/i18n
transifex_input = $(i18n)/transifex_input.json
tx_url1 = https://www.transifex.com/api/2/project/edx-platform/resource/$(transifex_resource)/translation/en/strings/
tx_url2 = https://www.transifex.com/api/2/project/edx-platform/resource/$(transifex_resource)/source/
# This directory must match .babelrc .
transifex_temp = ./temp/babel-plugin-react-intl
requirements:
npm install
npm run-script i18n_extract
i18n.extract:
# Pulling display strings from .jsx files into .json files...
# Pulling display strings from .jsx files into .json files...
rm -rf $(transifex_temp)
npm run-script i18n_extract
i18n.concat:
# Gathering JSON messages into one file...
./src/i18n/i18n-concat.js ./temp/src ./src/i18n/transifex_input.json
# Gathering JSON messages into one file...
$(transifex_utils) $(transifex_temp) $(transifex_input)
extract_translations: | requirements i18n.extract i18n.concat
# Despite the name, we actually need this target to detect changes in the incoming translated message files as well.
detect_changed_source_translations:
git diff --exit-code ./src/i18n/transifex_input.json
# Checking for changed translations...
git diff --exit-code $(i18n)
tx_url1 = https://www.transifex.com/api/2/project/edx-platform/resource/frontend-app-account/translation/en/strings/
tx_url2 = https://www.transifex.com/api/2/project/edx-platform/resource/frontend-app-account/source/
# push translations to Transifex, doing magic so we can include the translator comments
push_translations: | i18n.extract
# Adding translator comments...
# Fetching strings from Transifex...
# Pushes translations to Transifex. You must run make extract_translations first.
push_translations:
# Pushing strings to Transifex...
tx push -s
# Fetching hashes from Transifex...
./node_modules/reactifex/bash_scripts/get_hashed_strings.sh $(tx_url1)
# Writing out comments to file...
./src/i18n/i18n-concat.js ./temp/src --comments
# Adding comments to Transifex...
# Writing out comments to file...
$(transifex_utils) $(transifex_temp) --comments
# Pushing comments to Transifex...
./node_modules/reactifex/bash_scripts/put_comments.sh $(tx_url2)
# pull translations from Transifex
pull_translations: ## must be exactly this name for edx tooling support, see ecommerce-scripts/transifex/pull.py
tx pull -f --mode reviewed --language="ar,fr,es_419,zh_CN"
# Pulls translations from Transifex.
pull_translations:
tx pull -f --mode reviewed --language=$(transifex_langs)
# This target is used by Travis.
validate-no-uncommitted-package-lock-changes:
# Checking for package-lock.json changes...
git diff --exit-code package-lock.json

View File

@@ -10,6 +10,26 @@ Introduction
React app for account settings.
Get Started
-----------
1. Start up your local devstack
2. If you don't have node installed. Install Node
3. In the project directory: npm install
4. Then run npm start
5. Open your browser to http://localhost:1997/account-settings
Important Note
--------------
The production Webpack configuration for this repo uses `Purgecss <https://www.purgecss.com/>`_
to remove unused CSS from the production css file. In webpack/webpack.prod.config.js the Purgecss
plugin is configured to scan directories to determine what css selectors should remain. Currently
the src/ directory is scanned along with all @edx/frontend-component* node modules and paragon.
If you add and use a component in this repo that relies on HTML classes or ids for styling you
must add it to the Purgecss configuration or it will be unstyled in the production build.
.. |Build Status| image:: https://api.travis-ci.org/edx/frontend-app-account.svg?branch=master
:target: https://travis-ci.org/edx/frontend-app-account
.. |Coveralls| image:: https://img.shields.io/coveralls/edx/frontend-app-account.svg?branch=master

3135
package-lock.json generated
View File

@@ -14,18 +14,18 @@
}
},
"@babel/core": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.4.3.tgz",
"integrity": "sha512-oDpASqKFlbspQfzAE7yaeTmdljSH2ADIvBlb0RwbStltTuWa0+7CCI1fYVINNv9saHPa1W7oaKeuNuKj+RQCvA==",
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.4.5.tgz",
"integrity": "sha512-OvjIh6aqXtlsA8ujtGKfC7LYWksYSX8yQcM8Ay3LuvVeQ63lcOKgoZWVqcpFwkd29aYU9rVx7jxhfhiEDV9MZA==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"@babel/generator": "^7.4.0",
"@babel/helpers": "^7.4.3",
"@babel/parser": "^7.4.3",
"@babel/template": "^7.4.0",
"@babel/traverse": "^7.4.3",
"@babel/types": "^7.4.0",
"@babel/generator": "^7.4.4",
"@babel/helpers": "^7.4.4",
"@babel/parser": "^7.4.5",
"@babel/template": "^7.4.4",
"@babel/traverse": "^7.4.5",
"@babel/types": "^7.4.4",
"convert-source-map": "^1.1.0",
"debug": "^4.1.0",
"json5": "^2.1.0",
@@ -53,12 +53,12 @@
}
},
"@babel/generator": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.0.tgz",
"integrity": "sha512-/v5I+a1jhGSKLgZDcmAUZ4K/VePi43eRkUs3yePW1HB1iANOD5tqJXwGSG4BZhSksP8J9ejSlwGeTiiOFZOrXQ==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz",
"integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==",
"dev": true,
"requires": {
"@babel/types": "^7.4.0",
"@babel/types": "^7.4.4",
"jsesc": "^2.5.1",
"lodash": "^4.17.11",
"source-map": "^0.5.0",
@@ -103,24 +103,24 @@
}
},
"@babel/helper-call-delegate": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.4.0.tgz",
"integrity": "sha512-SdqDfbVdNQCBp3WhK2mNdDvHd3BD6qbmIc43CAyjnsfCmgHMeqgDcM3BzY2lchi7HBJGJ2CVdynLWbezaE4mmQ==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz",
"integrity": "sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ==",
"dev": true,
"requires": {
"@babel/helper-hoist-variables": "^7.4.0",
"@babel/traverse": "^7.4.0",
"@babel/types": "^7.4.0"
"@babel/helper-hoist-variables": "^7.4.4",
"@babel/traverse": "^7.4.4",
"@babel/types": "^7.4.4"
}
},
"@babel/helper-define-map": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.4.0.tgz",
"integrity": "sha512-wAhQ9HdnLIywERVcSvX40CEJwKdAa1ID4neI9NXQPDOHwwA+57DqwLiPEVy2AIyWzAk0CQ8qx4awO0VUURwLtA==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.4.4.tgz",
"integrity": "sha512-IX3Ln8gLhZpSuqHJSnTNBWGDE9kdkTEWl21A/K7PQ00tseBwbqCHTvNLHSBd9M0R5rER4h5Rsvj9vw0R5SieBg==",
"dev": true,
"requires": {
"@babel/helper-function-name": "^7.1.0",
"@babel/types": "^7.4.0",
"@babel/types": "^7.4.4",
"lodash": "^4.17.11"
}
},
@@ -155,12 +155,12 @@
}
},
"@babel/helper-hoist-variables": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.0.tgz",
"integrity": "sha512-/NErCuoe/et17IlAQFKWM24qtyYYie7sFIrW/tIQXpck6vAu2hhtYYsKLBWQV+BQZMbcIYPU/QMYuTufrY4aQw==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz",
"integrity": "sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w==",
"dev": true,
"requires": {
"@babel/types": "^7.4.0"
"@babel/types": "^7.4.4"
}
},
"@babel/helper-member-expression-to-functions": {
@@ -182,16 +182,16 @@
}
},
"@babel/helper-module-transforms": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.4.3.tgz",
"integrity": "sha512-H88T9IySZW25anu5uqyaC1DaQre7ofM+joZtAaO2F8NBdFfupH0SZ4gKjgSFVcvtx/aAirqA9L9Clio2heYbZA==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.4.4.tgz",
"integrity": "sha512-3Z1yp8TVQf+B4ynN7WoHPKS8EkdTbgAEy0nU0rs/1Kw4pDgmvYH3rz3aI11KgxKCba2cn7N+tqzV1mY2HMN96w==",
"dev": true,
"requires": {
"@babel/helper-module-imports": "^7.0.0",
"@babel/helper-simple-access": "^7.1.0",
"@babel/helper-split-export-declaration": "^7.0.0",
"@babel/template": "^7.2.2",
"@babel/types": "^7.2.2",
"@babel/helper-split-export-declaration": "^7.4.4",
"@babel/template": "^7.4.4",
"@babel/types": "^7.4.4",
"lodash": "^4.17.11"
}
},
@@ -211,9 +211,9 @@
"dev": true
},
"@babel/helper-regex": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.4.3.tgz",
"integrity": "sha512-hnoq5u96pLCfgjXuj8ZLX3QQ+6nAulS+zSgi6HulUwFbEruRAKwbGLU5OvXkE14L8XW6XsQEKsIDfgthKLRAyA==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.4.4.tgz",
"integrity": "sha512-Y5nuB/kESmR3tKjU8Nkn1wMGEx1tjJX076HBMeL3XLQCu6vA/YRzuTW0bbb+qRnXvQGn+d6Rx953yffl8vEy7Q==",
"dev": true,
"requires": {
"lodash": "^4.17.11"
@@ -233,15 +233,15 @@
}
},
"@babel/helper-replace-supers": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.4.0.tgz",
"integrity": "sha512-PVwCVnWWAgnal+kJ+ZSAphzyl58XrFeSKSAJRiqg5QToTsjL+Xu1f9+RJ+d+Q0aPhPfBGaYfkox66k86thxNSg==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.4.4.tgz",
"integrity": "sha512-04xGEnd+s01nY1l15EuMS1rfKktNF+1CkKmHoErDppjAAZL+IUBZpzT748x262HF7fibaQPhbvWUl5HeSt1EXg==",
"dev": true,
"requires": {
"@babel/helper-member-expression-to-functions": "^7.0.0",
"@babel/helper-optimise-call-expression": "^7.0.0",
"@babel/traverse": "^7.4.0",
"@babel/types": "^7.4.0"
"@babel/traverse": "^7.4.4",
"@babel/types": "^7.4.4"
}
},
"@babel/helper-simple-access": {
@@ -255,12 +255,12 @@
}
},
"@babel/helper-split-export-declaration": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.0.tgz",
"integrity": "sha512-7Cuc6JZiYShaZnybDmfwhY4UYHzI6rlqhWjaIqbsJGsIqPimEYy5uh3akSRLMg65LSdSEnJ8a8/bWQN6u2oMGw==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz",
"integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==",
"dev": true,
"requires": {
"@babel/types": "^7.4.0"
"@babel/types": "^7.4.4"
}
},
"@babel/helper-wrap-function": {
@@ -276,14 +276,14 @@
}
},
"@babel/helpers": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.4.3.tgz",
"integrity": "sha512-BMh7X0oZqb36CfyhvtbSmcWc3GXocfxv3yNsAEuM0l+fAqSO22rQrUpijr3oE/10jCTrB6/0b9kzmG4VetCj8Q==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.4.4.tgz",
"integrity": "sha512-igczbR/0SeuPR8RFfC7tGrbdTbFL3QTvH6D+Z6zNxnTe//GyqmtHmDkzrqDmyZ3eSwPqB/LhyKoU5DXsp+Vp2A==",
"dev": true,
"requires": {
"@babel/template": "^7.4.0",
"@babel/traverse": "^7.4.3",
"@babel/types": "^7.4.0"
"@babel/template": "^7.4.4",
"@babel/traverse": "^7.4.4",
"@babel/types": "^7.4.4"
}
},
"@babel/highlight": {
@@ -298,9 +298,9 @@
}
},
"@babel/parser": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.3.tgz",
"integrity": "sha512-gxpEUhTS1sGA63EGQGuA+WESPR/6tz6ng7tSHFCmaTJK/cGK8y37cBTspX+U2xCAue2IQVvF6Z0oigmjwD8YGQ==",
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz",
"integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==",
"dev": true
},
"@babel/plugin-proposal-async-generator-functions": {
@@ -325,9 +325,9 @@
}
},
"@babel/plugin-proposal-object-rest-spread": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.3.tgz",
"integrity": "sha512-xC//6DNSSHVjq8O2ge0dyYlhshsH4T7XdCVoxbi5HzLYWfsC5ooFlJjrXk8RcAT+hjHAK9UjBXdylzSoDK3t4g==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.4.tgz",
"integrity": "sha512-dMBG6cSPBbHeEBdFXeQ2QLc5gUpg4Vkaz8octD4aoW/ISO+jBOcsuxYL7bsb5WSu8RLP6boxrBIALEHgoHtO9g==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0",
@@ -345,13 +345,13 @@
}
},
"@babel/plugin-proposal-unicode-property-regex": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.0.tgz",
"integrity": "sha512-h/KjEZ3nK9wv1P1FSNb9G079jXrNYR0Ko+7XkOx85+gM24iZbPn0rh4vCftk+5QKY7y1uByFataBTmX7irEF1w==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz",
"integrity": "sha512-j1NwnOqMG9mFUOH58JTFsA/+ZYzQLUZ/drqWUqxCYLGeu2JFZL8YrNC9hBxKmWtAuOCHPcRpgv7fhap09Fb4kA==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/helper-regex": "^7.0.0",
"@babel/helper-regex": "^7.4.4",
"regexpu-core": "^4.5.4"
}
},
@@ -410,9 +410,9 @@
}
},
"@babel/plugin-transform-async-to-generator": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.4.0.tgz",
"integrity": "sha512-EeaFdCeUULM+GPFEsf7pFcNSxM7hYjoj5fiYbyuiXobW4JhFnjAv9OWzNwHyHcKoPNpAfeRDuW6VyaXEDUBa7g==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.4.4.tgz",
"integrity": "sha512-YiqW2Li8TXmzgbXw+STsSqPBPFnGviiaSp6CYOq55X8GQ2SGVLrXB6pNid8HkqkZAzOH6knbai3snhP7v0fNwA==",
"dev": true,
"requires": {
"@babel/helper-module-imports": "^7.0.0",
@@ -430,9 +430,9 @@
}
},
"@babel/plugin-transform-block-scoping": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.4.0.tgz",
"integrity": "sha512-AWyt3k+fBXQqt2qb9r97tn3iBwFpiv9xdAiG+Gr2HpAZpuayvbL55yWrsV3MyHvXk/4vmSiedhDRl1YI2Iy5nQ==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.4.4.tgz",
"integrity": "sha512-jkTUyWZcTrwxu5DD4rWz6rDB5Cjdmgz6z7M7RLXOJyCUkFBawssDGcGh8M/0FTSB87avyJI1HsTwUXp9nKA1PA==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0",
@@ -440,18 +440,18 @@
}
},
"@babel/plugin-transform-classes": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.4.3.tgz",
"integrity": "sha512-PUaIKyFUDtG6jF5DUJOfkBdwAS/kFFV3XFk7Nn0a6vR7ZT8jYw5cGtIlat77wcnd0C6ViGqo/wyNf4ZHytF/nQ==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.4.4.tgz",
"integrity": "sha512-/e44eFLImEGIpL9qPxSRat13I5QNRgBLu2hOQJCF7VLy/otSM/sypV1+XaIw5+502RX/+6YaSAPmldk+nhHDPw==",
"dev": true,
"requires": {
"@babel/helper-annotate-as-pure": "^7.0.0",
"@babel/helper-define-map": "^7.4.0",
"@babel/helper-define-map": "^7.4.4",
"@babel/helper-function-name": "^7.1.0",
"@babel/helper-optimise-call-expression": "^7.0.0",
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/helper-replace-supers": "^7.4.0",
"@babel/helper-split-export-declaration": "^7.4.0",
"@babel/helper-replace-supers": "^7.4.4",
"@babel/helper-split-export-declaration": "^7.4.4",
"globals": "^11.1.0"
}
},
@@ -465,22 +465,22 @@
}
},
"@babel/plugin-transform-destructuring": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.3.tgz",
"integrity": "sha512-rVTLLZpydDFDyN4qnXdzwoVpk1oaXHIvPEOkOLyr88o7oHxVc/LyrnDx+amuBWGOwUb7D1s/uLsKBNTx08htZg==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.4.tgz",
"integrity": "sha512-/aOx+nW0w8eHiEHm+BTERB2oJn5D127iye/SUQl7NjHy0lf+j7h4MKMMSOwdazGq9OxgiNADncE+SRJkCxjZpQ==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0"
}
},
"@babel/plugin-transform-dotall-regex": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.3.tgz",
"integrity": "sha512-9Arc2I0AGynzXRR/oPdSALv3k0rM38IMFyto7kOCwb5F9sLUt2Ykdo3V9yUPR+Bgr4kb6bVEyLkPEiBhzcTeoA==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz",
"integrity": "sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/helper-regex": "^7.4.3",
"@babel/helper-regex": "^7.4.4",
"regexpu-core": "^4.5.4"
}
},
@@ -504,18 +504,18 @@
}
},
"@babel/plugin-transform-for-of": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.3.tgz",
"integrity": "sha512-UselcZPwVWNSURnqcfpnxtMehrb8wjXYOimlYQPBnup/Zld426YzIhNEvuRsEWVHfESIECGrxoI6L5QqzuLH5Q==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz",
"integrity": "sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0"
}
},
"@babel/plugin-transform-function-name": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.3.tgz",
"integrity": "sha512-uT5J/3qI/8vACBR9I1GlAuU/JqBtWdfCrynuOkrWG6nCDieZd5przB1vfP59FRHBZQ9DC2IUfqr/xKqzOD5x0A==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz",
"integrity": "sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA==",
"dev": true,
"requires": {
"@babel/helper-function-name": "^7.1.0",
@@ -551,23 +551,23 @@
}
},
"@babel/plugin-transform-modules-commonjs": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.4.3.tgz",
"integrity": "sha512-sMP4JqOTbMJMimqsSZwYWsMjppD+KRyDIUVW91pd7td0dZKAvPmhCaxhOzkzLParKwgQc7bdL9UNv+rpJB0HfA==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.4.4.tgz",
"integrity": "sha512-4sfBOJt58sEo9a2BQXnZq+Q3ZTSAUXyK3E30o36BOGnJ+tvJ6YSxF0PG6kERvbeISgProodWuI9UVG3/FMY6iw==",
"dev": true,
"requires": {
"@babel/helper-module-transforms": "^7.4.3",
"@babel/helper-module-transforms": "^7.4.4",
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/helper-simple-access": "^7.1.0"
}
},
"@babel/plugin-transform-modules-systemjs": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.4.0.tgz",
"integrity": "sha512-gjPdHmqiNhVoBqus5qK60mWPp1CmYWp/tkh11mvb0rrys01HycEGD7NvvSoKXlWEfSM9TcL36CpsK8ElsADptQ==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.4.4.tgz",
"integrity": "sha512-MSiModfILQc3/oqnG7NrP1jHaSPryO6tA2kOMmAQApz5dayPxWiHqmq4sWH2xF5LcQK56LlbKByCd8Aah/OIkQ==",
"dev": true,
"requires": {
"@babel/helper-hoist-variables": "^7.4.0",
"@babel/helper-hoist-variables": "^7.4.4",
"@babel/helper-plugin-utils": "^7.0.0"
}
},
@@ -582,18 +582,18 @@
}
},
"@babel/plugin-transform-named-capturing-groups-regex": {
"version": "7.4.2",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.2.tgz",
"integrity": "sha512-NsAuliSwkL3WO2dzWTOL1oZJHm0TM8ZY8ZSxk2ANyKkt5SQlToGA4pzctmq1BEjoacurdwZ3xp2dCQWJkME0gQ==",
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.5.tgz",
"integrity": "sha512-z7+2IsWafTBbjNsOxU/Iv5CvTJlr5w4+HGu1HovKYTtgJ362f7kBcQglkfmlspKKZ3bgrbSGvLfNx++ZJgCWsg==",
"dev": true,
"requires": {
"regexp-tree": "^0.1.0"
"regexp-tree": "^0.1.6"
}
},
"@babel/plugin-transform-new-target": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.0.tgz",
"integrity": "sha512-6ZKNgMQmQmrEX/ncuCwnnw1yVGoaOW5KpxNhoWI7pCQdA0uZ0HqHGqenCUIENAnxRjy2WwNQ30gfGdIgqJXXqw==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz",
"integrity": "sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0"
@@ -610,12 +610,12 @@
}
},
"@babel/plugin-transform-parameters": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.3.tgz",
"integrity": "sha512-ULJYC2Vnw96/zdotCZkMGr2QVfKpIT/4/K+xWWY0MbOJyMZuk660BGkr3bEKWQrrciwz6xpmft39nA4BF7hJuA==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz",
"integrity": "sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw==",
"dev": true,
"requires": {
"@babel/helper-call-delegate": "^7.4.0",
"@babel/helper-call-delegate": "^7.4.4",
"@babel/helper-get-function-arity": "^7.0.0",
"@babel/helper-plugin-utils": "^7.0.0"
}
@@ -680,12 +680,12 @@
}
},
"@babel/plugin-transform-regenerator": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.3.tgz",
"integrity": "sha512-kEzotPuOpv6/iSlHroCDydPkKYw7tiJGKlmYp6iJn4a6C/+b2FdttlJsLKYxolYHgotTJ5G5UY5h0qey5ka3+A==",
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz",
"integrity": "sha512-gBKRh5qAaCWntnd09S8QC7r3auLCqq5DI6O0DlfoyDjslSBVqBibrMdsqO+Uhmx3+BlOmE/Kw1HFxmGbv0N9dA==",
"dev": true,
"requires": {
"regenerator-transform": "^0.13.4"
"regenerator-transform": "^0.14.0"
}
},
"@babel/plugin-transform-reserved-words": {
@@ -726,9 +726,9 @@
}
},
"@babel/plugin-transform-template-literals": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.2.0.tgz",
"integrity": "sha512-FkPix00J9A/XWXv4VoKJBMeSkyY9x/TqIh76wzcdfl57RJJcf8CehQ08uwfhCDNtRQYtHQKBTwKZDEyjE13Lwg==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz",
"integrity": "sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g==",
"dev": true,
"requires": {
"@babel/helper-annotate-as-pure": "^7.0.0",
@@ -745,20 +745,20 @@
}
},
"@babel/plugin-transform-unicode-regex": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.3.tgz",
"integrity": "sha512-lnSNgkVjL8EMtnE8eSS7t2ku8qvKH3eqNf/IwIfnSPUqzgqYmRwzdsQWv4mNQAN9Nuo6Gz1Y0a4CSmdpu1Pp6g==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz",
"integrity": "sha512-il+/XdNw01i93+M9J9u4T7/e/Ue/vWfNZE4IRUQjplu2Mqb/AFTDimkw2tdEdSH50wuQXZAbXSql0UphQke+vA==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/helper-regex": "^7.4.3",
"@babel/helper-regex": "^7.4.4",
"regexpu-core": "^4.5.4"
}
},
"@babel/polyfill": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.4.3.tgz",
"integrity": "sha512-rkv8WIvJshA5Ev8iNMGgz5WZkRtgtiPexiT7w5qevGTuT7ZBfM3de9ox1y9JR5/OXb/sWGBbWlHNa7vQKqku3Q==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.4.4.tgz",
"integrity": "sha512-WlthFLfhQQhh+A2Gn5NSFl0Huxz36x86Jn+E9OW7ibK8edKPq+KLy4apM1yDpQ8kJOVi1OVjpP4vSDLdrI04dg==",
"dev": true,
"requires": {
"core-js": "^2.6.5",
@@ -766,56 +766,56 @@
}
},
"@babel/preset-env": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.4.3.tgz",
"integrity": "sha512-FYbZdV12yHdJU5Z70cEg0f6lvtpZ8jFSDakTm7WXeJbLXh4R0ztGEu/SW7G1nJ2ZvKwDhz8YrbA84eYyprmGqw==",
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.4.5.tgz",
"integrity": "sha512-f2yNVXM+FsR5V8UwcFeIHzHWgnhXg3NpRmy0ADvALpnhB0SLbCvrCRr4BLOUYbQNLS+Z0Yer46x9dJXpXewI7w==",
"dev": true,
"requires": {
"@babel/helper-module-imports": "^7.0.0",
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/plugin-proposal-async-generator-functions": "^7.2.0",
"@babel/plugin-proposal-json-strings": "^7.2.0",
"@babel/plugin-proposal-object-rest-spread": "^7.4.3",
"@babel/plugin-proposal-object-rest-spread": "^7.4.4",
"@babel/plugin-proposal-optional-catch-binding": "^7.2.0",
"@babel/plugin-proposal-unicode-property-regex": "^7.4.0",
"@babel/plugin-proposal-unicode-property-regex": "^7.4.4",
"@babel/plugin-syntax-async-generators": "^7.2.0",
"@babel/plugin-syntax-json-strings": "^7.2.0",
"@babel/plugin-syntax-object-rest-spread": "^7.2.0",
"@babel/plugin-syntax-optional-catch-binding": "^7.2.0",
"@babel/plugin-transform-arrow-functions": "^7.2.0",
"@babel/plugin-transform-async-to-generator": "^7.4.0",
"@babel/plugin-transform-async-to-generator": "^7.4.4",
"@babel/plugin-transform-block-scoped-functions": "^7.2.0",
"@babel/plugin-transform-block-scoping": "^7.4.0",
"@babel/plugin-transform-classes": "^7.4.3",
"@babel/plugin-transform-block-scoping": "^7.4.4",
"@babel/plugin-transform-classes": "^7.4.4",
"@babel/plugin-transform-computed-properties": "^7.2.0",
"@babel/plugin-transform-destructuring": "^7.4.3",
"@babel/plugin-transform-dotall-regex": "^7.4.3",
"@babel/plugin-transform-destructuring": "^7.4.4",
"@babel/plugin-transform-dotall-regex": "^7.4.4",
"@babel/plugin-transform-duplicate-keys": "^7.2.0",
"@babel/plugin-transform-exponentiation-operator": "^7.2.0",
"@babel/plugin-transform-for-of": "^7.4.3",
"@babel/plugin-transform-function-name": "^7.4.3",
"@babel/plugin-transform-for-of": "^7.4.4",
"@babel/plugin-transform-function-name": "^7.4.4",
"@babel/plugin-transform-literals": "^7.2.0",
"@babel/plugin-transform-member-expression-literals": "^7.2.0",
"@babel/plugin-transform-modules-amd": "^7.2.0",
"@babel/plugin-transform-modules-commonjs": "^7.4.3",
"@babel/plugin-transform-modules-systemjs": "^7.4.0",
"@babel/plugin-transform-modules-commonjs": "^7.4.4",
"@babel/plugin-transform-modules-systemjs": "^7.4.4",
"@babel/plugin-transform-modules-umd": "^7.2.0",
"@babel/plugin-transform-named-capturing-groups-regex": "^7.4.2",
"@babel/plugin-transform-new-target": "^7.4.0",
"@babel/plugin-transform-named-capturing-groups-regex": "^7.4.5",
"@babel/plugin-transform-new-target": "^7.4.4",
"@babel/plugin-transform-object-super": "^7.2.0",
"@babel/plugin-transform-parameters": "^7.4.3",
"@babel/plugin-transform-parameters": "^7.4.4",
"@babel/plugin-transform-property-literals": "^7.2.0",
"@babel/plugin-transform-regenerator": "^7.4.3",
"@babel/plugin-transform-regenerator": "^7.4.5",
"@babel/plugin-transform-reserved-words": "^7.2.0",
"@babel/plugin-transform-shorthand-properties": "^7.2.0",
"@babel/plugin-transform-spread": "^7.2.0",
"@babel/plugin-transform-sticky-regex": "^7.2.0",
"@babel/plugin-transform-template-literals": "^7.2.0",
"@babel/plugin-transform-template-literals": "^7.4.4",
"@babel/plugin-transform-typeof-symbol": "^7.2.0",
"@babel/plugin-transform-unicode-regex": "^7.4.3",
"@babel/types": "^7.4.0",
"browserslist": "^4.5.2",
"core-js-compat": "^3.0.0",
"@babel/plugin-transform-unicode-regex": "^7.4.4",
"@babel/types": "^7.4.4",
"browserslist": "^4.6.0",
"core-js-compat": "^3.1.1",
"invariant": "^2.2.2",
"js-levenshtein": "^1.1.3",
"semver": "^5.5.0"
@@ -835,9 +835,9 @@
}
},
"@babel/register": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/@babel/register/-/register-7.4.0.tgz",
"integrity": "sha512-ekziebXBnS/7V6xk8sBfLSSD6YZuy6P29igBtR6OL/tswKdxOV+Yqq0nzICMguVYtGRZYUCGpfGV8J9Za2iBdw==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/register/-/register-7.4.4.tgz",
"integrity": "sha512-sn51H88GRa00+ZoMqCVgOphmswG4b7mhf9VOB0LUBAieykq2GnRFerlN+JQkO/ntT7wz4jaHNSRPg9IdMPEUkA==",
"dev": true,
"requires": {
"core-js": "^3.0.0",
@@ -849,9 +849,9 @@
},
"dependencies": {
"core-js": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.0.1.tgz",
"integrity": "sha512-sco40rF+2KlE0ROMvydjkrVMMG1vYilP2ALoRXcYR4obqbYIuV3Bg+51GEDW+HF8n7NRA+iaA4qD0nD9lo9mew==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.1.2.tgz",
"integrity": "sha512-3poRGjbu56leCtZCZCzCgQ7GcKOflDFnjWIepaPFUsM0IXUBrne10sl3aa2Bkcz3+FjRdIxBe9dAMhIJmEnQNA==",
"dev": true
},
"find-cache-dir": {
@@ -946,36 +946,36 @@
}
},
"@babel/runtime": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.3.tgz",
"integrity": "sha512-9lsJwJLxDh/T3Q3SZszfWOTkk3pHbkmH+3KY+zwIDmsNlxsumuhS2TH3NIpktU4kNvfzy+k3eLT7aTJSPTo0OA==",
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz",
"integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==",
"requires": {
"regenerator-runtime": "^0.13.2"
}
},
"@babel/template": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.0.tgz",
"integrity": "sha512-SOWwxxClTTh5NdbbYZ0BmaBVzxzTh2tO/TeLTbF6MO6EzVhHTnff8CdBXx3mEtazFBoysmEM6GU/wF+SuSx4Fw==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz",
"integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"@babel/parser": "^7.4.0",
"@babel/types": "^7.4.0"
"@babel/parser": "^7.4.4",
"@babel/types": "^7.4.4"
}
},
"@babel/traverse": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.3.tgz",
"integrity": "sha512-HmA01qrtaCwwJWpSKpA948cBvU5BrmviAief/b3AVw936DtcdsTexlbyzNuDnthwhOQ37xshn7hvQaEQk7ISYQ==",
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.5.tgz",
"integrity": "sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"@babel/generator": "^7.4.0",
"@babel/generator": "^7.4.4",
"@babel/helper-function-name": "^7.1.0",
"@babel/helper-split-export-declaration": "^7.4.0",
"@babel/parser": "^7.4.3",
"@babel/types": "^7.4.0",
"@babel/helper-split-export-declaration": "^7.4.4",
"@babel/parser": "^7.4.5",
"@babel/types": "^7.4.4",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.11"
@@ -993,9 +993,9 @@
}
},
"@babel/types": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.0.tgz",
"integrity": "sha512-aPvkXyU2SPOnztlgo8n9cEiXW755mgyvueUPcpStqdzoSPm0fjO0vQBjLkt3JKJW7ufikfcnMTTPsN1xaTsBPA==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz",
"integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -1009,28 +1009,28 @@
"integrity": "sha512-APBpZvdQrC1MJWMzk33V7FR2RhBRtnH2QPLqZzS+qia7PixwgWNlnX7UfHjhx+YWkM53GdsZKs40EBkSwADuMA=="
},
"@edx/edx-bootstrap": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@edx/edx-bootstrap/-/edx-bootstrap-2.0.1.tgz",
"integrity": "sha512-tq9ScRJBkUYw+0ypTshd+9+9Hnow00FkS6Rvh0RfK4xZ+rh8wNAUIVkG3TMclaW5qxvXSIE7Qo7YO7i7JrqV6A==",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@edx/edx-bootstrap/-/edx-bootstrap-2.2.1.tgz",
"integrity": "sha512-HkQ45u46ejX7WYwJX6ar/0FGBjx4F7bZvt3Dd4A677st0ic/47X5vQlqMqmK/M08C+GXaunNbZdbyF/ROrmwxw==",
"requires": {
"bootstrap": "^4.3.1"
}
},
"@edx/frontend-analytics": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@edx/frontend-analytics/-/frontend-analytics-1.0.0.tgz",
"integrity": "sha512-OBrzWAVsCwSraW25KpUikOTzFIPgGBi7akcqmGDRT9BZcAe/He/TE+29mg8v1KwFDCY1Q6QOKW8c6HOJAHSdpA==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@edx/frontend-analytics/-/frontend-analytics-2.0.0.tgz",
"integrity": "sha512-dj01bUVFA0sIlxtMH1YhQrVjWdmmiUeDIR3QqSXAxU2vwX2F4bsYUm8t0kCyg+8CjfkTcuZvyHHfwvQwlFOx2g==",
"requires": {
"form-urlencoded": "^3.0.0",
"lodash.snakecase": "^4.1.1"
}
},
"@edx/frontend-auth": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@edx/frontend-auth/-/frontend-auth-5.1.1.tgz",
"integrity": "sha512-bkPTzSr4EO4VuceM+2Aks/fqoSfnUr/MWxkRS2WNNCrvLRhW26aG5eK3z6UtPCYS2CiUpflt6lJc5CQXBrAksA==",
"version": "5.3.4",
"resolved": "https://registry.npmjs.org/@edx/frontend-auth/-/frontend-auth-5.3.4.tgz",
"integrity": "sha512-0AQBSenO5awilrZGMTo6nJ9J8AhNtSyktzRnbyVGhkpUp28OPD065c2AaZTXG0+F9xNtyR9zd6QTi1JXUCE0qg==",
"requires": {
"@edx/frontend-logging": "^2.0.0",
"@edx/frontend-logging": "^2.0.1",
"axios": "^0.18.0",
"camelcase-keys": "^5.0.0",
"jwt-decode": "^2.2.0",
@@ -1041,29 +1041,66 @@
}
},
"@edx/frontend-component-footer": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@edx/frontend-component-footer/-/frontend-component-footer-2.0.3.tgz",
"integrity": "sha512-HSUs9vDZ18enSY5km15cFdt8WhBqippEaQZwtOBH2zX4iEtHb6kV0lX7gHD++Vnm/UCgJkvcbWZ7tFDImikvPA=="
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@edx/frontend-component-footer/-/frontend-component-footer-6.0.2.tgz",
"integrity": "sha512-TB9fc85uXLRbznjshMNYJcLcwOA1GgPxaRrga8qpybb/80p4fawZnBY1XsupHFs15j/Xltk6xvF+QIm26K7m7A==",
"requires": {
"query-string": "^5.1.1"
}
},
"@edx/frontend-component-site-header": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/@edx/frontend-component-site-header/-/frontend-component-site-header-2.1.6.tgz",
"integrity": "sha512-dIvXrHB9rIWIJHbt/hmMznVTk4pmurNM0K6F7Zi7/l5Gk1xbqM6899+Q9imT8uzOJp31D9QftgQ+gCfzCVoSZg==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@edx/frontend-component-site-header/-/frontend-component-site-header-2.4.0.tgz",
"integrity": "sha512-Z1JICIJxG6kcpBck7YxEubPZyiBGS2z0SrRJXX7JcRRpKjDNDcooOQYG/SiZt5OMVVwOOeJdQDxhMtu3zT+fSQ==",
"requires": {
"react-responsive": "^6.1.1",
"react-transition-group": "^2.5.2"
}
},
"@edx/frontend-i18n": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@edx/frontend-i18n/-/frontend-i18n-2.1.0.tgz",
"integrity": "sha512-TmaxNNAFFRWT0EkRxy7gQrnnIzJuFn+HO8HOaYoO4vWJhvvfr++G+0ysgyNLuxms9CdFB1LuB64yEGupFSqcVA==",
"requires": {
"@cospired/i18n-iso-languages": "^2.0.2",
"glob": "^7.1.4",
"i18n-iso-countries": "^4.0.0",
"iso-countries-languages": "^0.2.1",
"react-intl": "^2.9.0",
"universal-cookie": "^4.0.0"
},
"dependencies": {
"i18n-iso-countries": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-4.1.0.tgz",
"integrity": "sha512-ttqCFBUvVSwUCgyjjIG95lilFg/61INah89ih/znBYHrZAcD5HsFUr8CJBmEgIOPbw0jZFgAPAsYRPGQexMTeA=="
},
"universal-cookie": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.0.tgz",
"integrity": "sha512-6JVx+3oGPjslGqFhQ8YSIBHmYTx8HbyAEH++2/b6SKNXsbsdQ7lU7wRG2bYcRB5JVCz8GYgQ+Ixew91hn3Dy9w==",
"requires": {
"@types/cookie": "^0.3.1",
"@types/object-assign": "^4.0.30",
"cookie": "^0.3.1",
"object-assign": "^4.1.0"
}
}
}
},
"@edx/frontend-logging": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@edx/frontend-logging/-/frontend-logging-2.0.0.tgz",
"integrity": "sha512-/nzaWXSD93YNhpXCwn9pjjoAMX69Jch4yU2qEkSJYEoQAwvHMlkIDLHsazwi3l1TqFBbeGulGCV8fi/GF6Uvqw=="
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@edx/frontend-logging/-/frontend-logging-2.0.2.tgz",
"integrity": "sha512-DFj4CXcy5jStwkEN/sBiTFV5IB06KnTdWIfk/OYpx7EnMJCXL7b8BawW74xstyVjkGXRhQr08sgt/cY+hNGd7A=="
},
"@edx/paragon": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-4.1.3.tgz",
"integrity": "sha512-WO15oF2sRxZo0oSS5AIYX/nugP4BGlbgpr4TqgxzUaAwCdQs7Zwh+VWsaYGYtcIYvsaekRxptGFzw7aqPzpDSg==",
"version": "4.2.6",
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-4.2.6.tgz",
"integrity": "sha512-HOSClLgOe2hGxFuRORgK+6iSrEixFvaz5Cg3pdQa+SeU9WBqiPlEevG4TnWl0qlihltsYR9Vba80tzG9ci84hQ==",
"requires": {
"@fortawesome/fontawesome-svg-core": "^1.2.18",
"@fortawesome/free-solid-svg-icons": "^5.8.2",
"@fortawesome/react-fontawesome": "^0.1.4",
"airbnb-prop-types": "^2.12.0",
"classnames": "^2.2.6",
"email-prop-type": "^3.0.0",
@@ -1074,7 +1111,7 @@
"react-dom": "^16.8.6",
"react-proptype-conditional-require": "^1.0.4",
"react-responsive": "^6.1.1",
"react-transition-group": "^2.8.0",
"react-transition-group": "^4.0.0",
"sanitize-html": "^1.20.0"
},
"dependencies": {
@@ -1085,44 +1122,54 @@
"requires": {
"email-validator": "^2.0.4"
}
},
"react-transition-group": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.0.1.tgz",
"integrity": "sha512-SsLcBYhO4afXJC9esL8XMxi/y0ZvEc7To0TvtrBELqzpjXQHPZOTxvuPh2/4EhYc0uSMfp2SExIxsyJ0pBdNzg==",
"requires": {
"dom-helpers": "^3.4.0",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
}
}
}
},
"@fortawesome/fontawesome-common-types": {
"version": "0.2.17",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.17.tgz",
"integrity": "sha512-DEYsEb/iiGVoMPQGjhG2uOylLVuMzTxOxysClkabZ5n80Q3oFDWGnshCLKvOvKoeClsgmKhWVrnnqvsMI1cAbw=="
"version": "0.2.18",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.18.tgz",
"integrity": "sha512-834DrzO2Ne3upCW+mJJPC/E6BsFcj+2Z1HmPIhbpbj8UaKmXWum4NClqLpUiMetugRlHuG4jbIHNdv2/lc3c1Q=="
},
"@fortawesome/fontawesome-svg-core": {
"version": "1.2.17",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.17.tgz",
"integrity": "sha512-TORMW/wIX2QyyGBd4XwHGPir4/0U18Wxf+iDBAUW3EIJ0/VC/ZMpJOiyiCe1f8g9h0PPzA7sqVtl8JtTUtm4uA==",
"version": "1.2.18",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.18.tgz",
"integrity": "sha512-1vyLWVQqxQ8q8bA2zgZcljk3RkeELlDJ757ymLk+ebK019AFgEFH5kTnR5OMN1SFsTwW1OHlFQO3VufdeCg/Gg==",
"requires": {
"@fortawesome/fontawesome-common-types": "^0.2.17"
"@fortawesome/fontawesome-common-types": "^0.2.18"
}
},
"@fortawesome/free-brands-svg-icons": {
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.8.1.tgz",
"integrity": "sha512-NN5Nap2D5e7Lusa5uarAUkcaO7PMbme5wmUF8kofZzPUZR753zDg/UFffi+LLE2Mi9zRXCJEYmIRfMON9SxLPg==",
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.8.2.tgz",
"integrity": "sha512-nhEWctDOP6f+Ka10LXAFoF+6mtWidC2iQgTBGRGgydmhBtcIEwyxWVx5wQHa86A1zAMi5TnipDAYQs2qn7DD6A==",
"requires": {
"@fortawesome/fontawesome-common-types": "^0.2.17"
"@fortawesome/fontawesome-common-types": "^0.2.18"
}
},
"@fortawesome/free-regular-svg-icons": {
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.8.1.tgz",
"integrity": "sha512-U+tFjDyQpVdD0UPWoKRBVLhh0J1/q3iaWDrnxNMJKuKRmerc4d0jfiZdM2X7agOTcG7amvcllRBiWCu2FwYlMA==",
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.8.2.tgz",
"integrity": "sha512-499WODlsDcXA9hM+Y3ZqoWj1kKhVLCHS8PnJs3zEaoPr5W6yrE9Jnp3C9Hb9KpwnRMh3IRXZ3XqxyUaQFMMRFw==",
"requires": {
"@fortawesome/fontawesome-common-types": "^0.2.17"
"@fortawesome/fontawesome-common-types": "^0.2.18"
}
},
"@fortawesome/free-solid-svg-icons": {
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.8.1.tgz",
"integrity": "sha512-FUcxR75PtMOo3ihRHJOZz64IsWIVdWgB2vCMLJjquTv487wVVCMH5H5gWa72et2oI9lKKD2jvjQ+y+7mxhscVQ==",
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.8.2.tgz",
"integrity": "sha512-5tF6WOFlqqO95zY5ukSB6jliDa3jnk1p5L4K/a58ccDFsbjSkhfGuvZkRkeWxH8uMms81pZd6yQTwQrkedeJmg==",
"requires": {
"@fortawesome/fontawesome-common-types": "^0.2.17"
"@fortawesome/fontawesome-common-types": "^0.2.18"
}
},
"@fortawesome/react-fontawesome": {
@@ -1152,6 +1199,86 @@
"semver": "^5.5.1"
}
},
"@newrelic/publish-sourcemap": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/@newrelic/publish-sourcemap/-/publish-sourcemap-4.4.1.tgz",
"integrity": "sha1-ixYzusIghmsUyUw+PPlQY73Xskc=",
"dev": true,
"requires": {
"superagent": "^3.4.1",
"yargs": "^6.6.0"
},
"dependencies": {
"camelcase": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
"integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
"dev": true
},
"cliui": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
"integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
"dev": true,
"requires": {
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1",
"wrap-ansi": "^2.0.0"
}
},
"os-locale": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
"integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
"dev": true,
"requires": {
"lcid": "^1.0.0"
}
},
"which-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
"integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
"dev": true
},
"y18n": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
"integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
"dev": true
},
"yargs": {
"version": "6.6.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz",
"integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=",
"dev": true,
"requires": {
"camelcase": "^3.0.0",
"cliui": "^3.2.0",
"decamelize": "^1.1.1",
"get-caller-file": "^1.0.1",
"os-locale": "^1.4.0",
"read-pkg-up": "^1.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^1.0.1",
"set-blocking": "^2.0.0",
"string-width": "^1.0.2",
"which-module": "^1.0.0",
"y18n": "^3.2.1",
"yargs-parser": "^4.2.0"
}
},
"yargs-parser": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz",
"integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=",
"dev": true,
"requires": {
"camelcase": "^3.0.0"
}
}
}
},
"@newrelic/superagent": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@newrelic/superagent/-/superagent-1.0.3.tgz",
@@ -1207,70 +1334,6 @@
"resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.0.2.tgz",
"integrity": "sha512-8/qcMh15507AnXJ3lBeuhsdFwnWQqnp68EpUuHlYPixJ5vjVmls7/Jq48cnUlrZI8Jd9U1jkhfCl0gaT5KMgVw=="
},
"@romainberger/css-diff": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@romainberger/css-diff/-/css-diff-1.0.3.tgz",
"integrity": "sha1-ztOHU11PQqQqwf4TwJ3pf1rhNEw=",
"requires": {
"lodash.merge": "^4.4.0",
"postcss": "^5.0.21"
},
"dependencies": {
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
},
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"requires": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
},
"dependencies": {
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
}
}
},
"has-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
"integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo="
},
"postcss": {
"version": "5.2.18",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
"integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
"requires": {
"chalk": "^1.1.3",
"js-base64": "^2.1.9",
"source-map": "^0.5.6",
"supports-color": "^3.2.3"
}
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
},
"supports-color": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
"integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
"requires": {
"has-flag": "^1.0.0"
}
}
}
},
"@sindresorhus/is": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz",
@@ -1416,10 +1479,33 @@
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.2.tgz",
"integrity": "sha512-aHQA072E10/8iUQsPH7mQU/KUyQBZAGzTVRCUvnSz8mSvbrYsP4xEO2RSA0Pjltolzi0j8+8ixrm//Hr4umPzw=="
},
"@types/events": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
"integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==",
"dev": true
},
"@types/glob": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
"integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==",
"dev": true,
"requires": {
"@types/events": "*",
"@types/minimatch": "*",
"@types/node": "*"
}
},
"@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
"integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==",
"dev": true
},
"@types/node": {
"version": "11.13.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.4.tgz",
"integrity": "sha512-+rabAZZ3Yn7tF/XPGHupKIL5EcAbrLxnTr/hgQICxbeuAfWtT0UZSfULE+ndusckBItcv4o6ZeOJplQikVcLvQ==",
"version": "12.0.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.2.tgz",
"integrity": "sha512-5tabW/i+9mhrfEOUcLDu2xBPsHJ+X5Orqy9FKpale3SjDA17j5AEpYq5vfy3oAeAHGcvANRCO3NV3d2D6q3NiA==",
"dev": true
},
"@types/object-assign": {
@@ -1430,7 +1516,8 @@
"@types/q": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz",
"integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw=="
"integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==",
"dev": true
},
"@types/tapable": {
"version": "0.2.5",
@@ -1692,13 +1779,13 @@
"dev": true
},
"accepts": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
"integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"dev": true,
"requires": {
"mime-types": "~2.1.18",
"negotiator": "0.6.1"
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"acorn": {
@@ -1714,9 +1801,9 @@
"dev": true
},
"acorn-globals": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.1.tgz",
"integrity": "sha512-gJSiKY8dBIjV/0jagZIFBdVMtfQyA5QHCvAT48H2q8REQoW8Fs5AOjqBql1LgSXgrMWdevcE+8cdZ33NtVbIBA==",
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.2.tgz",
"integrity": "sha512-BbzvZhVtZP+Bs1J1HcwrQe8ycfO0wStkSGxuul3He3GkHOIZ6eTqOkPuw9IP1X3+IkOo4wiJmwkobzXYz4wewQ==",
"dev": true,
"requires": {
"acorn": "^6.0.1",
@@ -1812,7 +1899,8 @@
"alphanum-sort": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz",
"integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM="
"integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=",
"dev": true
},
"amdefine": {
"version": "1.0.1",
@@ -1889,7 +1977,8 @@
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true
},
"ansi-styles": {
"version": "3.2.1",
@@ -1986,6 +2075,7 @@
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"dev": true,
"requires": {
"sprintf-js": "~1.0.2"
}
@@ -2102,12 +2192,12 @@
"dev": true
},
"array.prototype.find": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.0.4.tgz",
"integrity": "sha1-VWpcU2LAhkgyPdrrnenRS8GGTJA=",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.1.0.tgz",
"integrity": "sha512-Wn41+K1yuO5p7wRZDl7890c3xvv5UBrfVXTVIe28rSQb6LS0fZMDrQB6PAcxQFRFy6vJTLDc3A2+3CjQdzVKRg==",
"requires": {
"define-properties": "^1.1.2",
"es-abstract": "^1.7.0"
"define-properties": "^1.1.3",
"es-abstract": "^1.13.0"
}
},
"array.prototype.flat": {
@@ -2148,11 +2238,12 @@
}
},
"assert": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz",
"integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
"integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==",
"dev": true,
"requires": {
"object-assign": "^4.1.1",
"util": "0.10.3"
},
"dependencies": {
@@ -2211,9 +2302,9 @@
}
},
"async-each": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.2.tgz",
"integrity": "sha512-6xrbvN0MOBKSJDdonmSSz2OwFSgxRaVtBDes26mj9KIGtDo+g9xosFRSC+i1gQh2oAN/tQ62AI/pGZGQjVOiRg==",
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
"integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
"dev": true
},
"async-foreach": {
@@ -2267,12 +2358,40 @@
"dev": true
},
"axios": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz",
"integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=",
"version": "0.18.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz",
"integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==",
"requires": {
"follow-redirects": "^1.3.0",
"is-buffer": "^1.1.5"
"follow-redirects": "1.5.10",
"is-buffer": "^2.0.2"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"follow-redirects": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
"requires": {
"debug": "=3.1.0"
}
},
"is-buffer": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz",
"integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw=="
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"axios-mock-adapter": {
@@ -3167,6 +3286,20 @@
"babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-imports": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-imports/-/babel-plugin-transform-imports-1.5.1.tgz",
"integrity": "sha512-Jkb0tjqye8kjOD7GdcKJTGB3dC9fruQhwRFZCeYS0sZO2otyjG6SohKR8nZiSm/OvhY+Ny2ktzVE59XKgIqskA==",
"dev": true,
"requires": {
"babel-types": "^6.6.0",
"is-valid-path": "^0.1.1",
"lodash.camelcase": "^4.3.0",
"lodash.findkey": "^4.6.0",
"lodash.kebabcase": "^4.1.1",
"lodash.snakecase": "^4.1.1"
}
},
"babel-plugin-transform-object-rest-spread": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz",
@@ -3460,9 +3593,9 @@
"dev": true
},
"bail": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/bail/-/bail-1.0.3.tgz",
"integrity": "sha512-1X8CnjFVQ+a+KW36uBNMTU5s8+v5FzeqrP7hTG5aTb4aPreSbZJlhwPon9VKMuEVgV++JM+SQrALY3kr7eswdg==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/bail/-/bail-1.0.4.tgz",
"integrity": "sha512-S8vuDB4w6YpRhICUDET3guPlQpaJl7od94tpZ0Fvnyp+MKW/HyDTcRDck+29C9g+d/qQHnddRH3+94kZdrW0Ww==",
"dev": true
},
"balanced-match": {
@@ -3552,6 +3685,18 @@
"tweetnacl": "^0.14.3"
}
},
"bfj": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/bfj/-/bfj-6.1.1.tgz",
"integrity": "sha512-+GUNvzHR4nRyGybQc2WpNJL4MJazMuvf92ueIyA0bIkPRwhhQu3IfZQ2PSoVPpCBJfmoSdOxu5rnotfFLlvYRQ==",
"dev": true,
"requires": {
"bluebird": "^3.5.1",
"check-types": "^7.3.0",
"hoopy": "^0.1.2",
"tryer": "^1.0.0"
}
},
"big.js": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
@@ -3837,9 +3982,9 @@
}
},
"bluebird": {
"version": "3.5.4",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz",
"integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==",
"version": "3.5.5",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz",
"integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==",
"dev": true
},
"bn.js": {
@@ -3849,23 +3994,29 @@
"dev": true
},
"body-parser": {
"version": "1.18.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
"integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"dev": true,
"requires": {
"bytes": "3.0.0",
"bytes": "3.1.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "~1.6.3",
"iconv-lite": "0.4.23",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.5.2",
"raw-body": "2.3.3",
"type-is": "~1.6.16"
"qs": "6.7.0",
"raw-body": "2.4.0",
"type-is": "~1.6.17"
},
"dependencies": {
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
"dev": true
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -3875,20 +4026,17 @@
"ms": "2.0.0"
}
},
"iconv-lite": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
"dev": true,
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
},
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
"dev": true
}
}
},
@@ -3909,7 +4057,8 @@
"boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
"dev": true
},
"bootstrap": {
"version": "4.3.1",
@@ -4091,13 +4240,14 @@
}
},
"browserslist": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.5.4.tgz",
"integrity": "sha512-rAjx494LMjqKnMPhFkuLmLp8JWEX0o8ADTGeAbOqaF+XCvYLreZrG5uVjnPBlAQ8REZK4pzXGvp0bWgrFtKaag==",
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.0.tgz",
"integrity": "sha512-Jk0YFwXBuMOOol8n6FhgkDzn3mY9PYLYGk29zybF05SbRTsMgPqmTNeQQhOghCxq5oFqAXE3u4sYddr4C0uRhg==",
"dev": true,
"requires": {
"caniuse-lite": "^1.0.30000955",
"electron-to-chromium": "^1.3.122",
"node-releases": "^1.1.13"
"caniuse-lite": "^1.0.30000967",
"electron-to-chromium": "^1.3.133",
"node-releases": "^1.1.19"
}
},
"bser": {
@@ -4252,17 +4402,6 @@
"integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=",
"dev": true
},
"query-string": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
"integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
"dev": true,
"requires": {
"decode-uri-component": "^0.2.0",
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
}
},
"sort-keys": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz",
@@ -4278,6 +4417,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
"integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
"dev": true,
"requires": {
"callsites": "^2.0.0"
}
@@ -4286,6 +4426,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
"integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
"dev": true,
"requires": {
"caller-callsite": "^2.0.0"
}
@@ -4293,7 +4434,8 @@
"callsites": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
"integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA="
"integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
"dev": true
},
"camel-case": {
"version": "3.0.0",
@@ -4320,72 +4462,35 @@
"quick-lru": "^1.0.0"
}
},
"caniuse-api": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
"integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
"requires": {
"browserslist": "^4.0.0",
"caniuse-lite": "^1.0.0",
"lodash.memoize": "^4.1.2",
"lodash.uniq": "^4.5.0"
}
},
"caniuse-db": {
"version": "1.0.30000960",
"resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000960.tgz",
"integrity": "sha512-lgvTGHSJcROw38PaUzjAA1M3VA9hBkGsrTfo+Uiq6jSqfQXRSflpcQsM3MbytlFwBhPH6VD0GwLPihJgjqWrew==",
"version": "1.0.30000971",
"resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000971.tgz",
"integrity": "sha512-ubSZfYXO2KMYtCVmDez82mjodeZa+mBYWAnBMAmFBPAn4C2PY4SD0eC/diYQD4Rj1K+WNdp0vr0JDtm0SQ6GNg==",
"dev": true
},
"caniuse-lite": {
"version": "1.0.30000960",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000960.tgz",
"integrity": "sha512-7nK5qs17icQaX6V3/RYrJkOsZyRNnroA4+ZwxaKJzIKy+crIy0Mz5CBlLySd2SNV+4nbUZeqeNfiaEieUBu3aA=="
"version": "1.0.30000971",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000971.tgz",
"integrity": "sha512-TQFYFhRS0O5rdsmSbF1Wn+16latXYsQJat66f7S7lizXW1PVpWJeZw9wqqVLIjuxDRz7s7xRUj13QCfd8hKn6g==",
"dev": true
},
"caporal": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/caporal/-/caporal-0.10.0.tgz",
"integrity": "sha512-Df9b3SxFqXkdvFdc/i4fNU2wFGYezSKTkENew9txshZhfazE8Ts70OKDmhOD7NRJjz81Xd9Jb+wF9XeqM20WsA==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/caporal/-/caporal-1.1.0.tgz",
"integrity": "sha512-R5qo2QGoqBM6RvzHonGhUuEJSeqEa4lD1r+cPUEY2+YsXhpQVTS2TvScfIbi6ydFdhzFCNeNUB1v0YrRBvsbdg==",
"dev": true,
"requires": {
"bluebird": "^3.4.7",
"chalk": "^1.1.3",
"cli-table2": "^0.2.0",
"cli-table3": "^0.5.0",
"colorette": "1.0.1",
"fast-levenshtein": "^2.0.6",
"lodash.camelcase": "^4.3.0",
"lodash.kebabcase": "^4.1.1",
"lodash.merge": "^4.6.0",
"micromist": "^1.0.1",
"micromist": "1.1.0",
"prettyjson": "^1.2.1",
"tabtab": "^2.2.2",
"winston": "^2.3.1"
},
"dependencies": {
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
"dev": true
},
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
}
},
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
"dev": true
}
}
},
"capture-exit": {
@@ -4430,9 +4535,9 @@
}
},
"ccount": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.3.tgz",
"integrity": "sha512-Jt9tIBkRc9POUof7QA/VwWd+58fKkEEfI+/t1/eOlxKM7ZhrczNzMFefge7Ai+39y1pR/pP6cI19guHy3FSLmw==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.4.tgz",
"integrity": "sha512-fpZ81yYfzentuieinmGnphk0pLkOTMm6MZdVqwd77ROvhko6iujLNGrHH5E7utq3ygWklwfmwuG+A7P+NpqT6w==",
"dev": true
},
"chalk": {
@@ -4451,6 +4556,12 @@
"integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=",
"dev": true
},
"check-types": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/check-types/-/check-types-7.4.0.tgz",
"integrity": "sha512-YbulWHdfP99UfZ73NcUDlNJhEIDgm9Doq9GhpyXbF+7Aegi3CVV7qqMCKTTqJxlvEvnQBp9IA+dxsGN6xK/nSg==",
"dev": true
},
"cheerio": {
"version": "1.0.0-rc.3",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz",
@@ -4650,17 +4761,23 @@
"restore-cursor": "^1.0.1"
}
},
"cli-table2": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/cli-table2/-/cli-table2-0.2.0.tgz",
"integrity": "sha1-LR738hig54biFFQFYtS9F3/jLZc=",
"cli-table3": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz",
"integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==",
"dev": true,
"requires": {
"colors": "^1.1.2",
"lodash": "^3.10.1",
"string-width": "^1.0.1"
"object-assign": "^4.1.0",
"string-width": "^2.1.1"
},
"dependencies": {
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
"colors": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz",
@@ -4668,11 +4785,30 @@
"dev": true,
"optional": true
},
"lodash": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
"integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=",
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"dev": true,
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
}
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dev": true,
"requires": {
"ansi-regex": "^3.0.0"
}
}
}
},
@@ -4764,6 +4900,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz",
"integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==",
"dev": true,
"requires": {
"@types/q": "^1.5.1",
"chalk": "^2.4.1",
@@ -4777,15 +4914,15 @@
"dev": true
},
"codecov": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/codecov/-/codecov-3.3.0.tgz",
"integrity": "sha512-S70c3Eg9SixumOvxaKE/yKUxb9ihu/uebD9iPO2IR73IdP4i6ZzjXEULj3d0HeyWPr0DqBfDkjNBWxURjVO5hw==",
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/codecov/-/codecov-3.5.0.tgz",
"integrity": "sha512-/OsWOfIHaQIr7aeZ4pY0UC1PZT6kimoKFOFYFNb6wxo3iw12nRrh+mNGH72rnXxNsq6SGfesVPizm/6Q3XqcFQ==",
"dev": true,
"requires": {
"argv": "^0.0.2",
"ignore-walk": "^3.0.1",
"js-yaml": "^3.12.0",
"teeny-request": "^3.7.0",
"js-yaml": "^3.13.1",
"teeny-request": "^3.11.3",
"urlgrey": "^0.4.4"
}
},
@@ -4799,15 +4936,6 @@
"object-visit": "^1.0.0"
}
},
"color": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/color/-/color-3.1.0.tgz",
"integrity": "sha512-CwyopLkuRYO5ei2EpzpIh6LqJMt6Mt+jZhO5VI5f/wJLZriXQE32/SSqzmrh+QB+AZT81Cj8yv+7zwToW8ahZg==",
"requires": {
"color-convert": "^1.9.1",
"color-string": "^1.5.2"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -4821,14 +4949,11 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"color-string": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
"integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
"requires": {
"color-name": "^1.0.0",
"simple-swizzle": "^0.2.2"
}
"colorette": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.0.1.tgz",
"integrity": "sha512-40MnlppkzHhFjRhtXunbpqKUT+eJn0gyVGi8aQlNSG8T2CCy31NdD7yktcS0aizH1VP2OhhQCyGMeTp0a/fvaw==",
"dev": true
},
"colormin": {
"version": "1.1.2",
@@ -4866,30 +4991,29 @@
"colors": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz",
"integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w="
"integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=",
"dev": true
},
"combined-stream": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
"integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==",
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"requires": {
"delayed-stream": "~1.0.0"
}
},
"comma-separated-tokens": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.5.tgz",
"integrity": "sha512-Cg90/fcK93n0ecgYTAz1jaA3zvnQ0ExlmKY1rdbyHqAx6BHxwoJc+J7HDu0iuQ7ixEs1qaa+WyQ6oeuBpYP1iA==",
"dev": true,
"requires": {
"trim": "0.0.1"
}
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.7.tgz",
"integrity": "sha512-Jrx3xsP4pPv4AwJUDWY9wOXGtwPXARej6Xd99h4TUGotmf8APuquKMpK+dnD3UgyxK7OEWaisjZz+3b5jtL6xQ==",
"dev": true
},
"commander": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz",
"integrity": "sha1-0SG7roYNmZKj1Re6lvVliOR8Z4E="
"integrity": "sha1-0SG7roYNmZKj1Re6lvVliOR8Z4E=",
"dev": true
},
"commondir": {
"version": "1.0.1",
@@ -4904,12 +5028,12 @@
"dev": true
},
"compressible": {
"version": "2.0.16",
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.16.tgz",
"integrity": "sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA==",
"version": "2.0.17",
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz",
"integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==",
"dev": true,
"requires": {
"mime-db": ">= 1.38.0 < 2"
"mime-db": ">= 1.40.0 < 2"
}
},
"compression": {
@@ -5067,6 +5191,12 @@
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
"dev": true
},
"cookiejar": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz",
"integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==",
"dev": true
},
"copy-concurrently": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
@@ -5121,40 +5251,33 @@
}
},
"core-js": {
"version": "2.6.5",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz",
"integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A=="
"version": "2.6.8",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.8.tgz",
"integrity": "sha512-RWlREFU74TEkdXzyl1bka66O3kYp8jeTXrvJZDzVVMH8AiHUSOFpL1yfhQJ+wHocAm1m+4971W1PPzfLuCv1vg=="
},
"core-js-compat": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.0.1.tgz",
"integrity": "sha512-2pC3e+Ht/1/gD7Sim/sqzvRplMiRnFQVlPpDVaHtY9l7zZP7knamr3VRD6NyGfHd84MrDC0tAM9ulNxYMW0T3g==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.1.2.tgz",
"integrity": "sha512-X0Ch5f6itrHxhg5HSJucX6nNLNAGr+jq+biBh6nPGc3YAWz2a8p/ZIZY8cUkDzSRNG54omAuu3hoEF8qZbu/6Q==",
"dev": true,
"requires": {
"browserslist": "^4.5.4",
"core-js": "3.0.1",
"core-js-pure": "3.0.1",
"browserslist": "^4.6.0",
"core-js-pure": "3.1.2",
"semver": "^6.0.0"
},
"dependencies": {
"core-js": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.0.1.tgz",
"integrity": "sha512-sco40rF+2KlE0ROMvydjkrVMMG1vYilP2ALoRXcYR4obqbYIuV3Bg+51GEDW+HF8n7NRA+iaA4qD0nD9lo9mew==",
"dev": true
},
"semver": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz",
"integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==",
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.1.0.tgz",
"integrity": "sha512-kCqEOOHoBcFs/2Ccuk4Xarm/KiWRSLEX9CAZF8xkJ6ZPlIoTZ8V5f7J16vYLJqDbR7KrxTJpR2lqjIEm2Qx9cQ==",
"dev": true
}
}
},
"core-js-pure": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.0.1.tgz",
"integrity": "sha512-mSxeQ6IghKW3MoyF4cz19GJ1cMm7761ON+WObSyLfTu/Jn3x7w4NwNFnrZxgl4MTSvYYepVLNuRtlB4loMwJ5g==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.1.2.tgz",
"integrity": "sha512-5ckIdBF26B3ldK9PM177y2ZcATP2oweam9RskHSoqfZCrJ2As6wVg8zJ1zTriFsZf6clj/N1ThDFRGaomMsh9w==",
"dev": true
},
"core-util-is": {
@@ -5164,13 +5287,14 @@
"dev": true
},
"cosmiconfig": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.0.tgz",
"integrity": "sha512-nxt+Nfc3JAqf4WIWd0jXLjTJZmsPLrA9DDc4nRw2KFJQJK7DNooqSXrNI7tzLG50CF8axczly5UV929tBmh/7g==",
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
"integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
"dev": true,
"requires": {
"import-fresh": "^2.0.0",
"is-directory": "^0.3.1",
"js-yaml": "^3.13.0",
"js-yaml": "^3.13.1",
"parse-json": "^4.0.0"
}
},
@@ -5259,16 +5383,8 @@
"css-color-names": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
"integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA="
},
"css-declaration-sorter": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz",
"integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==",
"requires": {
"postcss": "^7.0.1",
"timsort": "^0.3.0"
}
"integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=",
"dev": true
},
"css-loader": {
"version": "0.28.11",
@@ -5746,6 +5862,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-2.0.2.tgz",
"integrity": "sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ==",
"dev": true,
"requires": {
"boolbase": "^1.0.0",
"css-what": "^2.1.2",
@@ -5756,7 +5873,8 @@
"css-select-base-adapter": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz",
"integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w=="
"integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==",
"dev": true
},
"css-selector-tokenizer": {
"version": "0.7.1",
@@ -5813,6 +5931,7 @@
"version": "1.0.0-alpha.28",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.28.tgz",
"integrity": "sha512-joNNW1gCp3qFFzj4St6zk+Wh/NBv0vM5YbEreZk0SD4S23S+1xBKb6cLDg2uj4P4k/GUMlIm6cKIDqIG+vdt0w==",
"dev": true,
"requires": {
"mdn-data": "~1.1.0",
"source-map": "^0.5.3"
@@ -5821,105 +5940,28 @@
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
"dev": true
}
}
},
"css-unit-converter": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.1.tgz",
"integrity": "sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY="
},
"css-url-regex": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/css-url-regex/-/css-url-regex-1.1.0.tgz",
"integrity": "sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w="
"integrity": "sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w=",
"dev": true
},
"css-what": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
"integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg=="
},
"cssesc": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz",
"integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg=="
},
"cssnano": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz",
"integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==",
"requires": {
"cosmiconfig": "^5.0.0",
"cssnano-preset-default": "^4.0.7",
"is-resolvable": "^1.0.0",
"postcss": "^7.0.0"
}
},
"cssnano-preset-default": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz",
"integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==",
"requires": {
"css-declaration-sorter": "^4.0.1",
"cssnano-util-raw-cache": "^4.0.1",
"postcss": "^7.0.0",
"postcss-calc": "^7.0.1",
"postcss-colormin": "^4.0.3",
"postcss-convert-values": "^4.0.1",
"postcss-discard-comments": "^4.0.2",
"postcss-discard-duplicates": "^4.0.2",
"postcss-discard-empty": "^4.0.1",
"postcss-discard-overridden": "^4.0.1",
"postcss-merge-longhand": "^4.0.11",
"postcss-merge-rules": "^4.0.3",
"postcss-minify-font-values": "^4.0.2",
"postcss-minify-gradients": "^4.0.2",
"postcss-minify-params": "^4.0.2",
"postcss-minify-selectors": "^4.0.2",
"postcss-normalize-charset": "^4.0.1",
"postcss-normalize-display-values": "^4.0.2",
"postcss-normalize-positions": "^4.0.2",
"postcss-normalize-repeat-style": "^4.0.2",
"postcss-normalize-string": "^4.0.2",
"postcss-normalize-timing-functions": "^4.0.2",
"postcss-normalize-unicode": "^4.0.1",
"postcss-normalize-url": "^4.0.1",
"postcss-normalize-whitespace": "^4.0.2",
"postcss-ordered-values": "^4.1.2",
"postcss-reduce-initial": "^4.0.3",
"postcss-reduce-transforms": "^4.0.2",
"postcss-svgo": "^4.0.2",
"postcss-unique-selectors": "^4.0.1"
}
},
"cssnano-util-get-arguments": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz",
"integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8="
},
"cssnano-util-get-match": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz",
"integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0="
},
"cssnano-util-raw-cache": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz",
"integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==",
"requires": {
"postcss": "^7.0.0"
}
},
"cssnano-util-same-parent": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz",
"integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q=="
"integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==",
"dev": true
},
"csso": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/csso/-/csso-3.5.1.tgz",
"integrity": "sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg==",
"dev": true,
"requires": {
"css-tree": "1.0.0-alpha.29"
},
@@ -5928,6 +5970,7 @@
"version": "1.0.0-alpha.29",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.29.tgz",
"integrity": "sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==",
"dev": true,
"requires": {
"mdn-data": "~1.1.0",
"source-map": "^0.5.3"
@@ -5936,7 +5979,8 @@
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
"dev": true
}
}
},
@@ -5988,9 +6032,9 @@
"dev": true
},
"damerau-levenshtein": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz",
"integrity": "sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ=",
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.5.tgz",
"integrity": "sha512-CBCRqFnpu715iPmw1KrdOrzRqbdFwQTwAWyyyYS42+iAgHCuXZ+/TdMgQkUENPomxEz9z1BEzuQU2Xw0kUuAgA==",
"dev": true
},
"dashdash": {
@@ -6049,8 +6093,7 @@
"decode-uri-component": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
"dev": true
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
},
"decompress": {
"version": "4.2.0",
@@ -6325,11 +6368,12 @@
"dev": true
},
"del": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/del/-/del-4.1.0.tgz",
"integrity": "sha512-C4kvKNlYrwXhKxz97BuohF8YoGgQ23Xm9lvoHmgT7JaPGprSEjk3+XFled74Yt/x0ZABUHg2D67covzAPUKx5Q==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz",
"integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==",
"dev": true,
"requires": {
"@types/glob": "^7.1.1",
"globby": "^6.1.0",
"is-path-cwd": "^2.0.0",
"is-path-in-cwd": "^2.0.0",
@@ -6605,6 +6649,7 @@
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
"integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==",
"dev": true,
"requires": {
"is-obj": "^1.0.0"
}
@@ -6714,10 +6759,17 @@
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
"dev": true
},
"ejs": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz",
"integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==",
"dev": true
},
"electron-to-chromium": {
"version": "1.3.124",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.124.tgz",
"integrity": "sha512-glecGr/kFdfeXUHOHAWvGcXrxNU+1wSO/t5B23tT1dtlvYB26GY8aHzZSWD7HqhqC800Lr+w/hQul6C5AF542w=="
"version": "1.3.137",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.137.tgz",
"integrity": "sha512-kGi32g42a8vS/WnYE7ELJyejRT7hbr3UeOOu0WeuYuQ29gCpg9Lrf6RdcTQVXSt/v0bjCfnlb/EWOOsiKpTmkw==",
"dev": true
},
"elliptic": {
"version": "6.4.1",
@@ -6820,12 +6872,13 @@
}
},
"enzyme-adapter-react-16": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.12.1.tgz",
"integrity": "sha512-GB61gvY97XvrA6qljExGY+lgI6BBwz+ASLaRKct9VQ3ozu0EraqcNn3CcrUckSGIqFGa1+CxO5gj5is5t3lwrw==",
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.13.1.tgz",
"integrity": "sha512-DCKbkiVlfLTbn4SXO8mXDQx1SmmwON5oKXn2QfQSMCt8eTYGwUXy/OBGSuss6KKwY5w5QfK1sQFxhgFOkMCjrw==",
"dev": true,
"requires": {
"enzyme-adapter-utils": "^1.11.0",
"enzyme-adapter-utils": "^1.12.0",
"has": "^1.0.3",
"object.assign": "^4.1.0",
"object.values": "^1.1.0",
"prop-types": "^15.7.2",
@@ -6835,12 +6888,12 @@
}
},
"enzyme-adapter-utils": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.11.0.tgz",
"integrity": "sha512-0VZeoE9MNx+QjTfsjmO1Mo+lMfunucYB4wt5ficU85WB/LoetTJrbuujmHP3PJx6pSoaAuLA+Mq877x4LoxdNg==",
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.12.0.tgz",
"integrity": "sha512-wkZvE0VxcFx/8ZsBw0iAbk3gR1d9hK447ebnSYBf95+r32ezBq+XDSAvRErkc4LZosgH8J7et7H7/7CtUuQfBA==",
"dev": true,
"requires": {
"airbnb-prop-types": "^2.12.0",
"airbnb-prop-types": "^2.13.2",
"function.prototype.name": "^1.1.0",
"object.assign": "^4.1.0",
"object.fromentries": "^2.0.0",
@@ -6861,6 +6914,7 @@
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"dev": true,
"requires": {
"is-arrayish": "^0.2.1"
}
@@ -6879,14 +6933,22 @@
}
},
"es-check": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/es-check/-/es-check-2.3.0.tgz",
"integrity": "sha512-12KfOJdPS/hiVpMvOlPLa8PiMmDNWJ9NRL3ZSr7VKykZP2T6An55YEdvcVFO7JkfEhmK1vyS9j2NXm2SMWypuA==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/es-check/-/es-check-5.0.0.tgz",
"integrity": "sha512-30n+EZt5KjazXEvyYr2DXJCOJJWfdT1unRp5+Szlcja6uGAB3Sh3QPjRsxd2xgN9SFj4S5P8pdBISwGcDdS45Q==",
"dev": true,
"requires": {
"acorn": "~5.7.0",
"caporal": "^0.10.0",
"acorn": "6.0.4",
"caporal": "1.1.0",
"glob": "^7.1.2"
},
"dependencies": {
"acorn": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.4.tgz",
"integrity": "sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg==",
"dev": true
}
}
},
"es-to-primitive": {
@@ -7249,9 +7311,9 @@
"dev": true
},
"eslint-plugin-import": {
"version": "2.17.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.17.1.tgz",
"integrity": "sha512-lzD9uvRvW4MsHzIOMJEDSb5MOV9LzgxRPBaovvOhJqzgxRHYfGy9QOrMuwHIh5ehKFJ7Z3DcrcGKDQ0IbP0EdQ==",
"version": "2.17.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.17.2.tgz",
"integrity": "sha512-m+cSVxM7oLsIpmwNn2WXTJoReOF9f/CtLMo7qOVmKd1KntBy0hEcuNZ3erTmWjx+DxRO0Zcrm5KwAvI9wHcV5g==",
"dev": true,
"requires": {
"array-includes": "^3.0.3",
@@ -7373,18 +7435,18 @@
}
},
"eslint-plugin-react": {
"version": "7.12.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.12.4.tgz",
"integrity": "sha512-1puHJkXJY+oS1t467MjbqjvX53uQ05HXwjqDgdbGBqf5j9eeydI54G3KwiJmWciQ0HTBacIKw2jgwSBSH3yfgQ==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.13.0.tgz",
"integrity": "sha512-uA5LrHylu8lW/eAH3bEQe9YdzpPaFd9yAJTwTi/i/BKTD7j6aQMKVAdGM/ML72zD6womuSK7EiGtMKuK06lWjQ==",
"dev": true,
"requires": {
"array-includes": "^3.0.3",
"doctrine": "^2.1.0",
"has": "^1.0.3",
"jsx-ast-utils": "^2.0.1",
"jsx-ast-utils": "^2.1.0",
"object.fromentries": "^2.0.0",
"prop-types": "^15.6.2",
"resolve": "^1.9.0"
"prop-types": "^15.7.2",
"resolve": "^1.10.1"
}
},
"eslint-restricted-globals": {
@@ -7422,7 +7484,8 @@
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true
},
"esquery": {
"version": "1.0.1",
@@ -7461,9 +7524,9 @@
"dev": true
},
"eventemitter3": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz",
"integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz",
"integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==",
"dev": true
},
"events": {
@@ -7599,39 +7662,39 @@
}
},
"express": {
"version": "4.16.4",
"resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
"integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
"version": "4.17.0",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.0.tgz",
"integrity": "sha512-1Z7/t3Z5ZnBG252gKUPyItc4xdeaA0X934ca2ewckAsVsw9EG71i++ZHZPYnus8g/s5Bty8IMpSVEuRkmwwPRQ==",
"dev": true,
"requires": {
"accepts": "~1.3.5",
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.18.3",
"content-disposition": "0.5.2",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.3.1",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.1.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.2",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.4",
"qs": "6.5.2",
"range-parser": "~1.2.0",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.16.2",
"serve-static": "1.13.2",
"setprototypeof": "1.1.0",
"statuses": "~1.4.0",
"type-is": "~1.6.16",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
@@ -7642,10 +7705,10 @@
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
"dev": true
},
"content-disposition": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
"integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=",
"cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==",
"dev": true
},
"debug": {
@@ -7668,6 +7731,12 @@
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
"dev": true
},
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
"dev": true
}
}
},
@@ -7931,17 +8000,17 @@
}
},
"finalhandler": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
"integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"dev": true,
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.2",
"statuses": "~1.4.0",
"parseurl": "~1.3.3",
"statuses": "~1.5.0",
"unpipe": "~1.0.0"
},
"dependencies": {
@@ -8004,6 +8073,7 @@
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/findup/-/findup-0.1.5.tgz",
"integrity": "sha1-itkpozk7rGJ5V6fl3kYjsGsOLOs=",
"dev": true,
"requires": {
"colors": "~0.6.0-1",
"commander": "~2.1.0"
@@ -8385,6 +8455,7 @@
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz",
"integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==",
"dev": true,
"requires": {
"debug": "^3.2.6"
}
@@ -8431,6 +8502,17 @@
"resolved": "https://registry.npmjs.org/form-urlencoded/-/form-urlencoded-3.0.0.tgz",
"integrity": "sha512-zSgBjyO5Yo3TCfElJKqlSBGOEq8lZjltZAYqew4X5trf4AbH4sZhBesvywy7IpD1mfbu8JS6733V3zv3YdIW1g=="
},
"formdata-polyfill": {
"version": "3.0.18",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-3.0.18.tgz",
"integrity": "sha512-qydEiPA/DWm1reRCfBJyFHs/wQhjvjoQjN8P/FatoaZJ+Efc/1kVwrHPR7Ek+BuIGINr26mHXT9KpR5WYmWZww=="
},
"formidable": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz",
"integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==",
"dev": true
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
@@ -8556,37 +8638,41 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"fsevents": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz",
"integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==",
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
"integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==",
"dev": true,
"optional": true,
"requires": {
"nan": "^2.9.2",
"node-pre-gyp": "^0.10.0"
"nan": "^2.12.1",
"node-pre-gyp": "^0.12.0"
},
"dependencies": {
"abbrev": {
"version": "1.1.1",
"bundled": true,
"resolved": false,
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true,
"optional": true
},
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
"bundled": true,
"resolved": false,
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
"dev": true,
"optional": true
},
"are-we-there-yet": {
"version": "1.1.5",
"bundled": true,
"resolved": false,
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
"dev": true,
"optional": true,
"requires": {
@@ -8596,13 +8682,15 @@
},
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"resolved": false,
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"optional": true,
"requires": {
@@ -8612,64 +8700,74 @@
},
"chownr": {
"version": "1.1.1",
"bundled": true,
"resolved": false,
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
"dev": true,
"optional": true
},
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"dev": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"dev": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true,
"optional": true
},
"debug": {
"version": "2.6.9",
"bundled": true,
"version": "4.1.1",
"resolved": false,
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"dev": true,
"optional": true,
"requires": {
"ms": "2.0.0"
"ms": "^2.1.1"
}
},
"deep-extend": {
"version": "0.6.0",
"bundled": true,
"resolved": false,
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"dev": true,
"optional": true
},
"delegates": {
"version": "1.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
"dev": true,
"optional": true
},
"detect-libc": {
"version": "1.0.3",
"bundled": true,
"resolved": false,
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
"dev": true,
"optional": true
},
"fs-minipass": {
"version": "1.2.5",
"bundled": true,
"resolved": false,
"integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
"dev": true,
"optional": true,
"requires": {
@@ -8678,13 +8776,15 @@
},
"fs.realpath": {
"version": "1.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true,
"optional": true
},
"gauge": {
"version": "2.7.4",
"bundled": true,
"resolved": false,
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"dev": true,
"optional": true,
"requires": {
@@ -8700,7 +8800,8 @@
},
"glob": {
"version": "7.1.3",
"bundled": true,
"resolved": false,
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"dev": true,
"optional": true,
"requires": {
@@ -8714,13 +8815,15 @@
},
"has-unicode": {
"version": "2.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
"dev": true,
"optional": true
},
"iconv-lite": {
"version": "0.4.24",
"bundled": true,
"resolved": false,
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dev": true,
"optional": true,
"requires": {
@@ -8729,7 +8832,8 @@
},
"ignore-walk": {
"version": "3.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
"dev": true,
"optional": true,
"requires": {
@@ -8738,7 +8842,8 @@
},
"inflight": {
"version": "1.0.6",
"bundled": true,
"resolved": false,
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"optional": true,
"requires": {
@@ -8748,19 +8853,22 @@
},
"inherits": {
"version": "2.0.3",
"bundled": true,
"resolved": false,
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true,
"optional": true
},
"ini": {
"version": "1.3.5",
"bundled": true,
"resolved": false,
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"dev": true,
"optional": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"dev": true,
"optional": true,
"requires": {
@@ -8769,13 +8877,15 @@
},
"isarray": {
"version": "1.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"dev": true,
"optional": true
},
"minimatch": {
"version": "3.0.4",
"bundled": true,
"resolved": false,
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"optional": true,
"requires": {
@@ -8784,13 +8894,15 @@
},
"minimist": {
"version": "0.0.8",
"bundled": true,
"resolved": false,
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true,
"optional": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"resolved": false,
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
"dev": true,
"optional": true,
"requires": {
@@ -8800,7 +8912,8 @@
},
"minizlib": {
"version": "1.2.1",
"bundled": true,
"resolved": false,
"integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
"dev": true,
"optional": true,
"requires": {
@@ -8809,7 +8922,8 @@
},
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"optional": true,
"requires": {
@@ -8817,25 +8931,28 @@
}
},
"ms": {
"version": "2.0.0",
"bundled": true,
"version": "2.1.1",
"resolved": false,
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"dev": true,
"optional": true
},
"needle": {
"version": "2.2.4",
"bundled": true,
"version": "2.3.0",
"resolved": false,
"integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==",
"dev": true,
"optional": true,
"requires": {
"debug": "^2.1.2",
"debug": "^4.1.0",
"iconv-lite": "^0.4.4",
"sax": "^1.2.4"
}
},
"node-pre-gyp": {
"version": "0.10.3",
"bundled": true,
"version": "0.12.0",
"resolved": false,
"integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==",
"dev": true,
"optional": true,
"requires": {
@@ -8853,7 +8970,8 @@
},
"nopt": {
"version": "4.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
"dev": true,
"optional": true,
"requires": {
@@ -8862,14 +8980,16 @@
}
},
"npm-bundled": {
"version": "1.0.5",
"bundled": true,
"version": "1.0.6",
"resolved": false,
"integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==",
"dev": true,
"optional": true
},
"npm-packlist": {
"version": "1.2.0",
"bundled": true,
"version": "1.4.1",
"resolved": false,
"integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==",
"dev": true,
"optional": true,
"requires": {
@@ -8879,7 +8999,8 @@
},
"npmlog": {
"version": "4.1.2",
"bundled": true,
"resolved": false,
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"dev": true,
"optional": true,
"requires": {
@@ -8891,19 +9012,22 @@
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"dev": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"dev": true,
"optional": true
},
"once": {
"version": "1.4.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"optional": true,
"requires": {
@@ -8912,19 +9036,22 @@
},
"os-homedir": {
"version": "1.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
"dev": true,
"optional": true
},
"os-tmpdir": {
"version": "1.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"dev": true,
"optional": true
},
"osenv": {
"version": "0.1.5",
"bundled": true,
"resolved": false,
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"dev": true,
"optional": true,
"requires": {
@@ -8934,19 +9061,22 @@
},
"path-is-absolute": {
"version": "1.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true,
"optional": true
},
"process-nextick-args": {
"version": "2.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
"dev": true,
"optional": true
},
"rc": {
"version": "1.2.8",
"bundled": true,
"resolved": false,
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"dev": true,
"optional": true,
"requires": {
@@ -8958,7 +9088,8 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true,
"optional": true
}
@@ -8966,7 +9097,8 @@
},
"readable-stream": {
"version": "2.3.6",
"bundled": true,
"resolved": false,
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"optional": true,
"requires": {
@@ -8981,7 +9113,8 @@
},
"rimraf": {
"version": "2.6.3",
"bundled": true,
"resolved": false,
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"dev": true,
"optional": true,
"requires": {
@@ -8990,43 +9123,50 @@
},
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
"resolved": false,
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
"bundled": true,
"resolved": false,
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true,
"optional": true
},
"sax": {
"version": "1.2.4",
"bundled": true,
"resolved": false,
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"dev": true,
"optional": true
},
"semver": {
"version": "5.6.0",
"bundled": true,
"version": "5.7.0",
"resolved": false,
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
"dev": true,
"optional": true
},
"set-blocking": {
"version": "2.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
"dev": true,
"optional": true
},
"signal-exit": {
"version": "3.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"dev": true,
"optional": true
},
"string-width": {
"version": "1.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true,
"optional": true,
"requires": {
@@ -9037,7 +9177,8 @@
},
"string_decoder": {
"version": "1.1.1",
"bundled": true,
"resolved": false,
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"optional": true,
"requires": {
@@ -9046,7 +9187,8 @@
},
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"optional": true,
"requires": {
@@ -9055,13 +9197,15 @@
},
"strip-json-comments": {
"version": "2.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"dev": true,
"optional": true
},
"tar": {
"version": "4.4.8",
"bundled": true,
"resolved": false,
"integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
"dev": true,
"optional": true,
"requires": {
@@ -9076,13 +9220,15 @@
},
"util-deprecate": {
"version": "1.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"dev": true,
"optional": true
},
"wide-align": {
"version": "1.1.3",
"bundled": true,
"resolved": false,
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
"dev": true,
"optional": true,
"requires": {
@@ -9091,22 +9237,24 @@
},
"wrappy": {
"version": "1.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true,
"optional": true
},
"yallist": {
"version": "3.0.3",
"bundled": true,
"resolved": false,
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
"dev": true,
"optional": true
}
}
},
"fstream": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
"integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
"integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
@@ -9262,9 +9410,9 @@
}
},
"glob": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
"integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -9333,9 +9481,9 @@
}
},
"globals": {
"version": "11.11.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz",
"integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==",
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"dev": true
},
"globby": {
@@ -9498,6 +9646,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
"dev": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -9642,11 +9791,6 @@
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true
},
"hex-color-regex": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
},
"history": {
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/history/-/history-4.9.0.tgz",
@@ -9672,9 +9816,12 @@
}
},
"hoist-non-react-statics": {
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
"integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz",
"integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==",
"requires": {
"react-is": "^16.7.0"
}
},
"home-or-tmp": {
"version": "2.0.0",
@@ -9695,6 +9842,12 @@
"parse-passwd": "^1.0.0"
}
},
"hoopy": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
"integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==",
"dev": true
},
"hosted-git-info": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
@@ -9745,20 +9898,11 @@
}
}
},
"hsl-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz",
"integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4="
},
"hsla-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz",
"integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg="
},
"html-comment-regex": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz",
"integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ=="
"integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==",
"dev": true
},
"html-element-map": {
"version": "1.0.1",
@@ -9893,15 +10037,16 @@
"dev": true
},
"http-errors": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"dev": true,
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.0",
"statuses": ">= 1.4.0 < 2"
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
}
},
"http-parser-js": {
@@ -10575,6 +10720,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
"integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
"dev": true,
"requires": {
"caller-path": "^2.0.0",
"resolve-from": "^3.0.0"
@@ -10629,7 +10775,8 @@
"indexes-of": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
"integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc="
"integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
"dev": true
},
"indexof": {
"version": "0.0.1",
@@ -10714,14 +10861,6 @@
"requires": {
"default-gateway": "^4.2.0",
"ipaddr.js": "^1.9.0"
},
"dependencies": {
"ipaddr.js": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
"integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==",
"dev": true
}
}
},
"interpret": {
@@ -10731,9 +10870,9 @@
"dev": true
},
"intl-format-cache": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/intl-format-cache/-/intl-format-cache-2.1.0.tgz",
"integrity": "sha1-BKNp/sv61tpgBbrh8UMzMy3PkxY="
"version": "2.2.9",
"resolved": "https://registry.npmjs.org/intl-format-cache/-/intl-format-cache-2.2.9.tgz",
"integrity": "sha512-Zv/u8wRpekckv0cLkwpVdABYST4hZNTDaX7reFetrYTJwxExR2VyTqQm+l0WmL0Qo8Mjb9Tf33qnfj0T7pjxdQ=="
},
"intl-messageformat": {
"version": "2.2.0",
@@ -10749,9 +10888,9 @@
"integrity": "sha1-tD1FqXRoytvkQzHXS7Ho3qRPwHU="
},
"intl-relativeformat": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/intl-relativeformat/-/intl-relativeformat-2.1.0.tgz",
"integrity": "sha1-AQ8RBYAiUfQKxH0OPhogE0iiVd8=",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/intl-relativeformat/-/intl-relativeformat-2.2.0.tgz",
"integrity": "sha512-4bV/7kSKaPEmu6ArxXf9xjv1ny74Zkwuey8Pm01NH4zggPP7JHwg2STk8Y3JdspCKRDriwIyLRfEXnj2ZLr4Bw==",
"requires": {
"intl-messageformat": "^2.0.0"
}
@@ -10793,15 +10932,16 @@
"dev": true
},
"ipaddr.js": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
"integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=",
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
"integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==",
"dev": true
},
"is-absolute-url": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz",
"integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY="
"integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=",
"dev": true
},
"is-accessor-descriptor": {
"version": "0.1.6",
@@ -10815,7 +10955,8 @@
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
"dev": true
},
"is-binary-path": {
"version": "1.0.1",
@@ -10835,7 +10976,8 @@
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"is-callable": {
"version": "1.1.4",
@@ -10851,19 +10993,6 @@
"ci-info": "^1.5.0"
}
},
"is-color-stop": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz",
"integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=",
"requires": {
"css-color-names": "^0.0.4",
"hex-color-regex": "^1.1.0",
"hsl-regex": "^1.0.0",
"hsla-regex": "^1.0.0",
"rgb-regex": "^1.0.1",
"rgba-regex": "^1.0.0"
}
},
"is-cwebp-readable": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-cwebp-readable/-/is-cwebp-readable-2.0.1.tgz",
@@ -10909,7 +11038,8 @@
"is-directory": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
"integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE="
"integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
"dev": true
},
"is-dotfile": {
"version": "1.0.3",
@@ -10998,6 +11128,15 @@
"is-path-inside": "^1.0.0"
}
},
"is-invalid-path": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/is-invalid-path/-/is-invalid-path-0.1.0.tgz",
"integrity": "sha1-MHqFWzzxqTi0TqcNLGEQYFNxTzQ=",
"dev": true,
"requires": {
"is-glob": "^2.0.0"
}
},
"is-jpg": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-jpg/-/is-jpg-2.0.0.tgz",
@@ -11034,7 +11173,8 @@
"is-obj": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8="
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
"dev": true
},
"is-object": {
"version": "1.0.1",
@@ -11043,18 +11183,29 @@
"dev": true
},
"is-path-cwd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.0.0.tgz",
"integrity": "sha512-m5dHHzpOXEiv18JEORttBO64UgTEypx99vCxQLjbBvGhOJxnTNglYoFXxwo6AbsQb79sqqycQEHv2hWkHZAijA==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.1.0.tgz",
"integrity": "sha512-Sc5j3/YnM8tDeyCsVeKlm/0p95075DyLmDEIkSgQ7mXkrOX+uTCtmQFm0CYzVyJwcCCmO3k8qfJt17SxQwB5Zw==",
"dev": true
},
"is-path-in-cwd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.0.0.tgz",
"integrity": "sha512-6Vz5Gc9s/sDA3JBVu0FzWufm8xaBsqy1zn8Q6gmvGP6nSDMw78aS4poBNeatWjaRpTpxxLn1WOndAiOlk+qY8A==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz",
"integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==",
"dev": true,
"requires": {
"is-path-inside": "^1.0.0"
"is-path-inside": "^2.1.0"
},
"dependencies": {
"is-path-inside": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz",
"integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==",
"dev": true,
"requires": {
"path-is-inside": "^1.0.2"
}
}
}
},
"is-path-inside": {
@@ -11122,7 +11273,8 @@
"is-resolvable": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
"integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg=="
"integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
"dev": true
},
"is-retry-allowed": {
"version": "1.1.0",
@@ -11158,6 +11310,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz",
"integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==",
"dev": true,
"requires": {
"html-comment-regex": "^1.1.0"
}
@@ -11182,6 +11335,15 @@
"integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
"dev": true
},
"is-valid-path": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-valid-path/-/is-valid-path-0.1.1.tgz",
"integrity": "sha1-EQ+f90w39mPh7HkV60UfLbk6yd8=",
"dev": true,
"requires": {
"is-invalid-path": "^0.1.0"
}
},
"is-windows": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
@@ -11716,7 +11878,8 @@
"js-base64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz",
"integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw=="
"integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==",
"dev": true
},
"js-levenshtein": {
"version": "1.1.6",
@@ -11733,6 +11896,7 @@
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
"dev": true,
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
@@ -11801,7 +11965,8 @@
"json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
"dev": true
},
"json-schema": {
"version": "0.2.3",
@@ -11877,9 +12042,9 @@
}
},
"jsx-ast-utils": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz",
"integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.1.0.tgz",
"integrity": "sha512-yDGDG2DS4JcqhA6blsuYbtsT09xL8AoLuUR2Gb5exrw7UEM19sBcOTq+YBBhrNbl0PUC4R4LnFu+dHg2HKeVvA==",
"dev": true,
"requires": {
"array-includes": "^3.0.3"
@@ -12033,12 +12198,6 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
},
"lodash.assign": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
"integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=",
"dev": true
},
"lodash.camelcase": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
@@ -12066,6 +12225,17 @@
"resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
"integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c="
},
"lodash.findindex": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.findindex/-/lodash.findindex-4.6.0.tgz",
"integrity": "sha1-oyRd7mH7m24GJLU1ElYku2nBEQY="
},
"lodash.findkey": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.findkey/-/lodash.findkey-4.6.0.tgz",
"integrity": "sha1-gwWOkDtRy7dZ0JzPVG3qPqOcRxg=",
"dev": true
},
"lodash.flattendeep": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
@@ -12077,6 +12247,11 @@
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
},
"lodash.isempty": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz",
"integrity": "sha1-b4bL7di+TsmHvpqvM8loTbGzHn4="
},
"lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
@@ -12102,18 +12277,25 @@
"lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4="
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
"dev": true
},
"lodash.merge": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz",
"integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ=="
"integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==",
"dev": true
},
"lodash.mergewith": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz",
"integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ=="
},
"lodash.omit": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz",
"integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA="
},
"lodash.pad": {
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz",
@@ -12157,7 +12339,8 @@
"lodash.uniq": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
"dev": true
},
"logalot": {
"version": "2.1.0",
@@ -12242,6 +12425,16 @@
"cast-array": "~1.0.0",
"object-filter": "~1.0.2",
"query-string": "~2.4.1"
},
"dependencies": {
"query-string": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-2.4.2.tgz",
"integrity": "sha1-fbBmZCCAS6qSrp8miWKFWnYUPfs=",
"requires": {
"strict-uri-encode": "^1.0.0"
}
}
}
},
"make-dir": {
@@ -12339,7 +12532,8 @@
"mdn-data": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz",
"integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA=="
"integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==",
"dev": true
},
"media-typer": {
"version": "0.3.0",
@@ -12356,6 +12550,11 @@
"mimic-fn": "^1.0.0"
}
},
"memoize-one": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.0.4.tgz",
"integrity": "sha512-P0z5IeAH6qHHGkJIXWw0xC2HNEgkx/9uWWBQw64FJj3/ol14VYdfVGWWr0fXfjhhv3TKVIqUq65os6O4GUNksA=="
},
"memory-fs": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
@@ -12556,32 +12755,24 @@
}
},
"mime": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.2.tgz",
"integrity": "sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"dev": true
},
"mime-db": {
"version": "1.39.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.39.0.tgz",
"integrity": "sha512-DTsrw/iWVvwHH+9Otxccdyy0Tgiil6TWK/xhfARJZF/QFhwOgZgOIvA2/VIGpM8U7Q8z5nDmdDWC6tuVMJNibw==",
"version": "1.40.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
"integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==",
"dev": true
},
"mime-types": {
"version": "2.1.22",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz",
"integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==",
"version": "2.1.24",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
"integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
"dev": true,
"requires": {
"mime-db": "~1.38.0"
},
"dependencies": {
"mime-db": {
"version": "1.38.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz",
"integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==",
"dev": true
}
"mime-db": "1.40.0"
}
},
"mimic-fn": {
@@ -12673,7 +12864,8 @@
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
},
"mississippi": {
"version": "2.0.0",
@@ -12780,6 +12972,7 @@
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"requires": {
"minimist": "0.0.8"
}
@@ -12843,9 +13036,9 @@
"dev": true
},
"nan": {
"version": "2.13.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz",
"integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw=="
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
},
"nanomatch": {
"version": "1.2.13",
@@ -12914,21 +13107,30 @@
}
},
"negotiator": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=",
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
"dev": true
},
"neo-async": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz",
"integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==",
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
"integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==",
"dev": true
},
"new-relic-source-map-webpack-plugin": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/new-relic-source-map-webpack-plugin/-/new-relic-source-map-webpack-plugin-1.1.0.tgz",
"integrity": "sha512-evLeCEU6T2ir5ZWN79q2n9Rl94jv62O6XQvhl2vilsFDrUgDu5xzamwXQQtcvySE3gKcO34oozW1xFDp89IaMQ==",
"dev": true,
"requires": {
"@newrelic/publish-sourcemap": "^4.1.2"
}
},
"newrelic": {
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/newrelic/-/newrelic-5.6.3.tgz",
"integrity": "sha512-dO5XBaItg26tmBFmOrN1vJoqO6qQvc6fCmVlcYwUCAXDPyMpbHze0+PYtyuXREOBAbLSztxNPvaVkbDU5m2Xdw==",
"version": "5.9.0",
"resolved": "https://registry.npmjs.org/newrelic/-/newrelic-5.9.0.tgz",
"integrity": "sha512-a1JyPuNhrAK7Vewl53wH5siElbrdmR48nWKVh+jwB8wVZTScbJ/1Op/m0m47U6RSu5t3nMmUs6DSPZtSwh6QbA==",
"requires": {
"@newrelic/koa": "^1.0.8",
"@newrelic/native-metrics": "^4.0.0",
@@ -12958,9 +13160,9 @@
}
},
"node-fetch": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz",
"integrity": "sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA==",
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==",
"dev": true
},
"node-forge": {
@@ -13105,17 +13307,18 @@
}
},
"node-releases": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.14.tgz",
"integrity": "sha512-d58EpVZRhQE60kWiWUaaPlK9dyC4zg3ZoMcHcky2d4hDksyQj0rUozwInOl0C66mBsqo01Tuns8AvxnL5S7PKg==",
"version": "1.1.21",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.21.tgz",
"integrity": "sha512-TwnURTCjc8a+ElJUjmDqU6+12jhli1Q61xOQmdZ7ECZVBZuQpN/1UnembiIHDM1wCcfLvh5wrWXUF5H6ufX64Q==",
"dev": true,
"requires": {
"semver": "^5.3.0"
}
},
"node-sass": {
"version": "4.11.0",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.11.0.tgz",
"integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==",
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz",
"integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==",
"dev": true,
"requires": {
"async-foreach": "^0.1.3",
@@ -13125,12 +13328,10 @@
"get-stdin": "^4.0.1",
"glob": "^7.0.3",
"in-publish": "^2.0.0",
"lodash.assign": "^4.2.0",
"lodash.clonedeep": "^4.3.2",
"lodash.mergewith": "^4.6.0",
"lodash": "^4.17.11",
"meow": "^3.7.0",
"mkdirp": "^0.5.1",
"nan": "^2.10.0",
"nan": "^2.13.2",
"node-gyp": "^3.8.0",
"npmlog": "^4.0.0",
"request": "^2.88.0",
@@ -13243,7 +13444,8 @@
"normalize-url": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz",
"integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg=="
"integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==",
"dev": true
},
"npm-conf": {
"version": "1.1.3",
@@ -13287,6 +13489,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
"integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
"dev": true,
"requires": {
"boolbase": "~1.0.0"
}
@@ -13303,9 +13506,9 @@
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
},
"nwsapi": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.3.tgz",
"integrity": "sha512-RowAaJGEgYXEZfQ7tvvdtAQUKPyTR6T6wNu0fwlNsGQYr/h3yQc6oI8WnVZh3Y/Sylwc+dtAlvPqfFZjhTyk3A==",
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.4.tgz",
"integrity": "sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw==",
"dev": true
},
"oauth-sign": {
@@ -13409,6 +13612,7 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
"integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=",
"dev": true,
"requires": {
"define-properties": "^1.1.2",
"es-abstract": "^1.5.1"
@@ -13437,6 +13641,7 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz",
"integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.12.0",
@@ -13479,6 +13684,12 @@
"integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
"dev": true
},
"opener": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz",
"integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==",
"dev": true
},
"opn": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/opn/-/opn-5.2.0.tgz",
@@ -13828,6 +14039,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"dev": true,
"requires": {
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
@@ -14076,9 +14288,9 @@
"dev": true
},
"postcss": {
"version": "7.0.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz",
"integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==",
"version": "7.0.16",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz",
"integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==",
"requires": {
"chalk": "^2.4.2",
"source-map": "^0.6.1",
@@ -14095,70 +14307,6 @@
}
}
},
"postcss-calc": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.1.tgz",
"integrity": "sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ==",
"requires": {
"css-unit-converter": "^1.1.1",
"postcss": "^7.0.5",
"postcss-selector-parser": "^5.0.0-rc.4",
"postcss-value-parser": "^3.3.1"
}
},
"postcss-colormin": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz",
"integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==",
"requires": {
"browserslist": "^4.0.0",
"color": "^3.0.0",
"has": "^1.0.0",
"postcss": "^7.0.0",
"postcss-value-parser": "^3.0.0"
}
},
"postcss-convert-values": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz",
"integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==",
"requires": {
"postcss": "^7.0.0",
"postcss-value-parser": "^3.0.0"
}
},
"postcss-discard-comments": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz",
"integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==",
"requires": {
"postcss": "^7.0.0"
}
},
"postcss-discard-duplicates": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz",
"integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==",
"requires": {
"postcss": "^7.0.0"
}
},
"postcss-discard-empty": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz",
"integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==",
"requires": {
"postcss": "^7.0.0"
}
},
"postcss-discard-overridden": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz",
"integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==",
"requires": {
"postcss": "^7.0.0"
}
},
"postcss-discard-unused": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz",
@@ -14454,104 +14602,12 @@
}
}
},
"postcss-merge-longhand": {
"version": "4.0.11",
"resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz",
"integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==",
"requires": {
"css-color-names": "0.0.4",
"postcss": "^7.0.0",
"postcss-value-parser": "^3.0.0",
"stylehacks": "^4.0.0"
}
},
"postcss-merge-rules": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz",
"integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==",
"requires": {
"browserslist": "^4.0.0",
"caniuse-api": "^3.0.0",
"cssnano-util-same-parent": "^4.0.0",
"postcss": "^7.0.0",
"postcss-selector-parser": "^3.0.0",
"vendors": "^1.0.0"
},
"dependencies": {
"postcss-selector-parser": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz",
"integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=",
"requires": {
"dot-prop": "^4.1.1",
"indexes-of": "^1.0.1",
"uniq": "^1.0.1"
}
}
}
},
"postcss-message-helpers": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz",
"integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=",
"dev": true
},
"postcss-minify-font-values": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz",
"integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==",
"requires": {
"postcss": "^7.0.0",
"postcss-value-parser": "^3.0.0"
}
},
"postcss-minify-gradients": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz",
"integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==",
"requires": {
"cssnano-util-get-arguments": "^4.0.0",
"is-color-stop": "^1.0.0",
"postcss": "^7.0.0",
"postcss-value-parser": "^3.0.0"
}
},
"postcss-minify-params": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz",
"integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==",
"requires": {
"alphanum-sort": "^1.0.0",
"browserslist": "^4.0.0",
"cssnano-util-get-arguments": "^4.0.0",
"postcss": "^7.0.0",
"postcss-value-parser": "^3.0.0",
"uniqs": "^2.0.0"
}
},
"postcss-minify-selectors": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz",
"integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==",
"requires": {
"alphanum-sort": "^1.0.0",
"has": "^1.0.0",
"postcss": "^7.0.0",
"postcss-selector-parser": "^3.0.0"
},
"dependencies": {
"postcss-selector-parser": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz",
"integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=",
"requires": {
"dot-prop": "^4.1.1",
"indexes-of": "^1.0.1",
"uniq": "^1.0.1"
}
}
}
},
"postcss-modules-extract-imports": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz",
@@ -14643,106 +14699,6 @@
}
}
},
"postcss-normalize-charset": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz",
"integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==",
"requires": {
"postcss": "^7.0.0"
}
},
"postcss-normalize-display-values": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz",
"integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==",
"requires": {
"cssnano-util-get-match": "^4.0.0",
"postcss": "^7.0.0",
"postcss-value-parser": "^3.0.0"
}
},
"postcss-normalize-positions": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz",
"integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==",
"requires": {
"cssnano-util-get-arguments": "^4.0.0",
"has": "^1.0.0",
"postcss": "^7.0.0",
"postcss-value-parser": "^3.0.0"
}
},
"postcss-normalize-repeat-style": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz",
"integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==",
"requires": {
"cssnano-util-get-arguments": "^4.0.0",
"cssnano-util-get-match": "^4.0.0",
"postcss": "^7.0.0",
"postcss-value-parser": "^3.0.0"
}
},
"postcss-normalize-string": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz",
"integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==",
"requires": {
"has": "^1.0.0",
"postcss": "^7.0.0",
"postcss-value-parser": "^3.0.0"
}
},
"postcss-normalize-timing-functions": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz",
"integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==",
"requires": {
"cssnano-util-get-match": "^4.0.0",
"postcss": "^7.0.0",
"postcss-value-parser": "^3.0.0"
}
},
"postcss-normalize-unicode": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz",
"integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==",
"requires": {
"browserslist": "^4.0.0",
"postcss": "^7.0.0",
"postcss-value-parser": "^3.0.0"
}
},
"postcss-normalize-url": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz",
"integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==",
"requires": {
"is-absolute-url": "^2.0.0",
"normalize-url": "^3.0.0",
"postcss": "^7.0.0",
"postcss-value-parser": "^3.0.0"
}
},
"postcss-normalize-whitespace": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz",
"integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==",
"requires": {
"postcss": "^7.0.0",
"postcss-value-parser": "^3.0.0"
}
},
"postcss-ordered-values": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz",
"integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==",
"requires": {
"cssnano-util-get-arguments": "^4.0.0",
"postcss": "^7.0.0",
"postcss-value-parser": "^3.0.0"
}
},
"postcss-reduce-idents": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz",
@@ -14815,63 +14771,20 @@
}
}
},
"postcss-reduce-initial": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz",
"integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==",
"postcss-rtl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/postcss-rtl/-/postcss-rtl-1.3.3.tgz",
"integrity": "sha512-iKdSko4zSvchZSPplz0iLD4dQyd4RiiARivMwboknD2eHu4tde1K7BqZ4CizZmnxrfhi4fvS774YSQPX6LJfMQ==",
"dev": true,
"requires": {
"browserslist": "^4.0.0",
"caniuse-api": "^3.0.0",
"has": "^1.0.0",
"postcss": "^7.0.0"
}
},
"postcss-reduce-transforms": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz",
"integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==",
"requires": {
"cssnano-util-get-match": "^4.0.0",
"has": "^1.0.0",
"postcss": "^7.0.0",
"postcss-value-parser": "^3.0.0"
}
},
"postcss-selector-parser": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz",
"integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==",
"requires": {
"cssesc": "^2.0.0",
"indexes-of": "^1.0.1",
"uniq": "^1.0.1"
}
},
"postcss-svgo": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz",
"integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==",
"requires": {
"is-svg": "^3.0.0",
"postcss": "^7.0.0",
"postcss-value-parser": "^3.0.0",
"svgo": "^1.0.0"
}
},
"postcss-unique-selectors": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz",
"integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==",
"requires": {
"alphanum-sort": "^1.0.0",
"postcss": "^7.0.0",
"uniqs": "^2.0.0"
"rtlcss": "^2.4.0"
}
},
"postcss-value-parser": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
"integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
"integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
"dev": true
},
"postcss-zindex": {
"version": "2.2.0",
@@ -15067,9 +14980,9 @@
}
},
"property-information": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/property-information/-/property-information-5.0.1.tgz",
"integrity": "sha512-nAtBDVeSwFM3Ot/YxT7s4NqZmqXI7lLzf46BThvotEtYf2uk2yH0ACYuWQkJ7gxKs49PPtKVY0UlDGkyN9aJlw==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/property-information/-/property-information-5.1.0.tgz",
"integrity": "sha512-tODH6R3+SwTkAQckSp2S9xyYX8dEKYkeXw+4TmJzTxnNzd6mQPu1OD4f9zPrvw/Rm4wpPgI+Zp63mNSGNzUgHg==",
"dev": true,
"requires": {
"xtend": "^4.0.1"
@@ -15082,13 +14995,13 @@
"dev": true
},
"proxy-addr": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
"integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==",
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
"integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
"dev": true,
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.8.0"
"ipaddr.js": "1.9.0"
}
},
"prr": {
@@ -15155,10 +15068,292 @@
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
},
"purgecss": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/purgecss/-/purgecss-1.3.0.tgz",
"integrity": "sha512-0UMnr8aUsPO7RbzAT72UELRvwMHhadtuunDm7rcgRS6b8pCVO8yglIqikiYFwQk2XP606mk+GpjI1G74Auxgtg==",
"dev": true,
"requires": {
"glob": "^7.1.3",
"postcss": "^7.0.14",
"postcss-selector-parser": "^6.0.0",
"yargs": "^13.2.2"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
"cliui": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
"integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
"dev": true,
"requires": {
"string-width": "^3.1.0",
"strip-ansi": "^5.2.0",
"wrap-ansi": "^5.1.0"
}
},
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
"integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
"dev": true,
"requires": {
"nice-try": "^1.0.4",
"path-key": "^2.0.1",
"semver": "^5.5.0",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
}
},
"cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
"dev": true
},
"emoji-regex": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
"dev": true
},
"execa": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
"integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
"dev": true,
"requires": {
"cross-spawn": "^6.0.0",
"get-stream": "^4.0.0",
"is-stream": "^1.1.0",
"npm-run-path": "^2.0.0",
"p-finally": "^1.0.0",
"signal-exit": "^3.0.0",
"strip-eof": "^1.0.0"
}
},
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
"locate-path": "^3.0.0"
}
},
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true
},
"get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
"integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
"dev": true,
"requires": {
"pump": "^3.0.0"
}
},
"invert-kv": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
"integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
"dev": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"lcid": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
"integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
"dev": true,
"requires": {
"invert-kv": "^2.0.0"
}
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dev": true,
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
}
},
"mem": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
"integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==",
"dev": true,
"requires": {
"map-age-cleaner": "^0.1.1",
"mimic-fn": "^2.0.0",
"p-is-promise": "^2.0.0"
}
},
"mimic-fn": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"dev": true
},
"os-locale": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
"integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
"dev": true,
"requires": {
"execa": "^1.0.0",
"lcid": "^2.0.0",
"mem": "^4.0.0"
}
},
"p-is-promise": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz",
"integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==",
"dev": true
},
"p-limit": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
"integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dev": true,
"requires": {
"p-limit": "^2.0.0"
}
},
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
},
"postcss-selector-parser": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
"integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
"dev": true,
"requires": {
"cssesc": "^3.0.0",
"indexes-of": "^1.0.1",
"uniq": "^1.0.1"
}
},
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"dev": true,
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"dev": true
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^5.1.0"
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
}
},
"wrap-ansi": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
"integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.0",
"string-width": "^3.0.0",
"strip-ansi": "^5.0.0"
}
},
"yargs": {
"version": "13.2.4",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz",
"integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==",
"dev": true,
"requires": {
"cliui": "^5.0.0",
"find-up": "^3.0.0",
"get-caller-file": "^2.0.1",
"os-locale": "^3.1.0",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^3.0.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^13.1.0"
}
},
"yargs-parser": {
"version": "13.1.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.0.tgz",
"integrity": "sha512-Yq+32PrijHRri0vVKQEm+ys8mbqWjLiwQkMFNXEENutzLPP0bE4Lcd4iA3OQY5HF+GD3xXxf0MEHb8E4/SA3AA==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
}
}
},
"purgecss-webpack-plugin": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/purgecss-webpack-plugin/-/purgecss-webpack-plugin-1.5.0.tgz",
"integrity": "sha512-ZSU6lok2DuDBuR7VCte5V12eke0Tx8xsCKxMbOnMfuJNPccPGv4jflRUm2Wvr2yGB8lFzKNZaTWaSk9g3kCv5A==",
"dev": true,
"requires": {
"purgecss": "^1.3.0",
"webpack-sources": "^1.3.0"
}
},
"q": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
"integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc="
"integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
"dev": true
},
"qs": {
"version": "6.5.2",
@@ -15167,10 +15362,12 @@
"dev": true
},
"query-string": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-2.4.2.tgz",
"integrity": "sha1-fbBmZCCAS6qSrp8miWKFWnYUPfs=",
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
"integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
"requires": {
"decode-uri-component": "^0.2.0",
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
}
},
@@ -15266,31 +15463,28 @@
}
},
"range-parser": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
"integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=",
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"dev": true
},
"raw-body": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
"integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"dev": true,
"requires": {
"bytes": "3.0.0",
"http-errors": "1.6.3",
"iconv-lite": "0.4.23",
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"dependencies": {
"iconv-lite": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
"dev": true,
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
"dev": true
}
}
},
@@ -15560,11 +15754,11 @@
"dev": true
},
"react-intl": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-2.8.0.tgz",
"integrity": "sha512-1cSasNkHxZOXYYhms9Q1tSEWF8AWZQNq3nPLB/j8mYV0ZTSt2DhGQXHfKrKQMu4cgj9J1Crqg7xFPICTBgzqtQ==",
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-2.9.0.tgz",
"integrity": "sha512-27jnDlb/d2A7mSJwrbOBnUgD+rPep+abmoJE511Tf8BnoONIAUehy/U1zZCHGO17mnOwMWxqN4qC0nW11cD6rA==",
"requires": {
"hoist-non-react-statics": "^2.5.5",
"hoist-non-react-statics": "^3.3.0",
"intl-format-cache": "^2.0.5",
"intl-messageformat": "^2.1.0",
"intl-relativeformat": "^2.1.0",
@@ -15598,22 +15792,12 @@
"prop-types": "^15.6.1",
"react-is": "^16.6.0",
"react-lifecycles-compat": "^3.0.0"
},
"dependencies": {
"hoist-non-react-statics": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz",
"integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==",
"requires": {
"react-is": "^16.7.0"
}
}
}
},
"react-responsive": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-6.1.1.tgz",
"integrity": "sha512-Po6pOEz70Agp+2lUmTxAnhfdkk0zp0IFgo/6bGcxv/S4Pa1sz0YG06WzkrIcASbyKSQ8x6AkcggeozXW3zj3kA==",
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-6.1.2.tgz",
"integrity": "sha512-AXentVC/kN3KED9zhzJv2pu4vZ0i6cSHdTtbCScVV1MT6F5KXaG2qs5D7WLmhdaOvmiMX8UfmS4ZSO+WPwDt4g==",
"requires": {
"hyphenate-style-name": "^1.0.0",
"matchmediaquery": "^0.3.0",
@@ -15632,6 +15816,13 @@
"path-to-regexp": "^1.7.0",
"prop-types": "^15.6.1",
"warning": "^4.0.1"
},
"dependencies": {
"hoist-non-react-statics": {
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
"integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
}
}
},
"react-router-dom": {
@@ -15647,6 +15838,24 @@
"warning": "^4.0.1"
}
},
"react-router-hash-link": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/react-router-hash-link/-/react-router-hash-link-1.2.1.tgz",
"integrity": "sha512-ddkCtmk/JwMmuU087TGShQHYyNjsJ+/9CTyuVdvvKf6ACgqk2Ma9ndX2xogo7WWmyq9AjuziBm5bmJ12zBxtsQ==",
"requires": {
"prop-types": "^15.6.0"
}
},
"react-scrollspy": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/react-scrollspy/-/react-scrollspy-3.4.0.tgz",
"integrity": "sha512-l7XDF6ZWuP0X+pLw+i5V8akWph/IQZ6/fnPXPN2R/xsgbXzXLtC/3URNcxj/hKIRjQpdYMrJR2VKqMkw2nnTvQ==",
"requires": {
"babel-runtime": "^6.26.0",
"classnames": "^2.2.5",
"prop-types": "^15.5.10"
}
},
"react-test-renderer": {
"version": "16.8.6",
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.8.6.tgz",
@@ -16196,9 +16405,9 @@
"dev": true
},
"regenerate-unicode-properties": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.0.2.tgz",
"integrity": "sha512-SbA/iNrBUf6Pv2zU8Ekv1Qbhv92yxL4hiDa2siuxs4KKn4oOoMDHXjAf7+Nz9qinUQ46B1LcWEi/PhJfPWpZWQ==",
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz",
"integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==",
"dev": true,
"requires": {
"regenerate": "^1.4.0"
@@ -16210,9 +16419,9 @@
"integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA=="
},
"regenerator-transform": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.13.4.tgz",
"integrity": "sha512-T0QMBjK3J0MtxjPmdIMXm72Wvj2Abb0Bd4HADdfijwMdoIsyQZ6fWC7kDFhk2YinBBEMZDL7Y7wh0J1sGx3S4A==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.0.tgz",
"integrity": "sha512-rtOelq4Cawlbmq9xuMR5gdFmv7ku/sFoB7sRiywx7aq53bc52b4j6zvH7Te1Vt/X2YveDKnCGUbioieU7FEL3w==",
"dev": true,
"requires": {
"private": "^0.1.6"
@@ -16238,9 +16447,9 @@
}
},
"regexp-tree": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.5.tgz",
"integrity": "sha512-nUmxvfJyAODw+0B13hj8CFVAxhe7fDEAgJgaotBu3nnR+IgGgZq59YedJP5VYTlkEfqjuK6TuRpnymKdatLZfQ==",
"version": "0.1.10",
"resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.10.tgz",
"integrity": "sha512-K1qVSbcedffwuIslMwpe6vGlj+ZXRnGkvjAtFHfDZZZuEdA/h0dxljAPu9vhUo6Rrx2U2AwJ+nSQ6hK+lrP5MQ==",
"dev": true
},
"regexpp": {
@@ -16520,9 +16729,9 @@
"integrity": "sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA=="
},
"resolve": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
"integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz",
"integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
@@ -16550,7 +16759,8 @@
"resolve-from": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
"dev": true
},
"resolve-pathname": {
"version": "2.2.0",
@@ -16594,16 +16804,6 @@
"integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
"dev": true
},
"rgb-regex": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz",
"integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE="
},
"rgba-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz",
"integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM="
},
"rimraf": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
@@ -16643,6 +16843,7 @@
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-2.4.0.tgz",
"integrity": "sha512-hdjFhZ5FCI0ABOfyXOMOhBtwPWtANLCG7rOiOcRf+yi5eDdxmDjqBruWouEnwVdzfh/TWF6NNncIEsigOCFZOA==",
"dev": true,
"requires": {
"chalk": "^2.3.0",
"findup": "^0.1.5",
@@ -16655,6 +16856,7 @@
"version": "6.0.23",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
"integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
"dev": true,
"requires": {
"chalk": "^2.4.1",
"source-map": "^0.6.1",
@@ -17041,9 +17243,9 @@
}
},
"sanitize-html": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.20.0.tgz",
"integrity": "sha512-BpxXkBoAG+uKCHjoXFmox6kCSYpnulABoGcZ/R3QyY9ndXbIM5S94eOr1IqnzTG8TnbmXaxWoDDzKC5eJv7fEQ==",
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.20.1.tgz",
"integrity": "sha512-txnH8TQjaQvg2Q0HY06G6CDJLVYCpbnxrdO0WN8gjCKaU5J0KbyGYhZxx5QJg3WLZ1lB7XU9kDkfrCXUozqptA==",
"requires": {
"chalk": "^2.4.1",
"htmlparser2": "^3.10.0",
@@ -17209,7 +17411,8 @@
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"dev": true
},
"scheduler": {
"version": "0.13.6",
@@ -17353,9 +17556,9 @@
}
},
"send": {
"version": "0.16.2",
"resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
"integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"dev": true,
"requires": {
"debug": "2.6.9",
@@ -17365,12 +17568,12 @@
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.6.2",
"mime": "1.4.1",
"ms": "2.0.0",
"http-errors": "~1.7.2",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"range-parser": "~1.2.0",
"statuses": "~1.4.0"
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
},
"dependencies": {
"debug": {
@@ -17380,19 +17583,15 @@
"dev": true,
"requires": {
"ms": "2.0.0"
},
"dependencies": {
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
}
}
},
"mime": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==",
"dev": true
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
}
}
},
@@ -17426,24 +17625,42 @@
"ms": "2.0.0"
}
},
"http-errors": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
"dev": true,
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.0",
"statuses": ">= 1.4.0 < 2"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
},
"setprototypeof": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
"dev": true
}
}
},
"serve-static": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
"integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"dev": true,
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.2",
"send": "0.16.2"
"parseurl": "~1.3.3",
"send": "0.17.1"
}
},
"set-blocking": {
@@ -17482,9 +17699,9 @@
"dev": true
},
"setprototypeof": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==",
"dev": true
},
"sha.js": {
@@ -17573,21 +17790,6 @@
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"dev": true
},
"simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
"integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
"requires": {
"is-arrayish": "^0.3.1"
},
"dependencies": {
"is-arrayish": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
}
}
},
"slash": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
@@ -17823,13 +18025,24 @@
"source-list-map": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
"integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw=="
"integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==",
"dev": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"source-map-loader": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.4.tgz",
"integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==",
"dev": true,
"requires": {
"async": "^2.5.0",
"loader-utils": "^1.1.0"
}
},
"source-map-resolve": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz",
@@ -17867,13 +18080,10 @@
"dev": true
},
"space-separated-tokens": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.2.tgz",
"integrity": "sha512-G3jprCEw+xFEs0ORweLmblJ3XLymGGr6hxZYTYZjIlvDti9vOBUjRQa1Rzjt012aRrocKstHwdNi+F7HguPsEA==",
"dev": true,
"requires": {
"trim": "0.0.1"
}
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.4.tgz",
"integrity": "sha512-UyhMSmeIqZrQn2UdjYpxEkwY9JUrn8pP+7L4f91zRzOQuI8MF1FGLfYU9DKCYeLdo7LXMxwrX5zKFy7eeeVHuA==",
"dev": true
},
"spawn-sync": {
"version": "1.0.15",
@@ -18022,7 +18232,8 @@
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
"dev": true
},
"squeak": {
"version": "1.3.0",
@@ -18100,7 +18311,8 @@
"stable": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
"integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w=="
"integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
"dev": true
},
"stack-trace": {
"version": "0.0.10",
@@ -18136,9 +18348,9 @@
}
},
"statuses": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
"integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
"dev": true
},
"stdout-stream": {
@@ -18357,6 +18569,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -18394,7 +18607,8 @@
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"dev": true
},
"strip-outer": {
"version": "1.0.1",
@@ -18415,24 +18629,52 @@
"schema-utils": "^0.4.5"
}
},
"stylehacks": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz",
"integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==",
"superagent": {
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz",
"integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==",
"dev": true,
"requires": {
"browserslist": "^4.0.0",
"postcss": "^7.0.0",
"postcss-selector-parser": "^3.0.0"
"component-emitter": "^1.2.0",
"cookiejar": "^2.1.0",
"debug": "^3.1.0",
"extend": "^3.0.0",
"form-data": "^2.3.1",
"formidable": "^1.2.0",
"methods": "^1.1.1",
"mime": "^1.4.1",
"qs": "^6.5.1",
"readable-stream": "^2.3.5"
},
"dependencies": {
"postcss-selector-parser": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz",
"integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=",
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"dev": true
},
"readable-stream": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
"dot-prop": "^4.1.1",
"indexes-of": "^1.0.1",
"uniq": "^1.0.1"
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
}
}
}
@@ -18446,9 +18688,10 @@
}
},
"svgo": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/svgo/-/svgo-1.2.1.tgz",
"integrity": "sha512-Y1+LyT4/y1ms4/0yxPMSlvx6dIbgklE9w8CIOnfeoFGB74MEkq8inSfEr6NhocTaFbyYp0a1dvNgRKGRmEBlzA==",
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/svgo/-/svgo-1.2.2.tgz",
"integrity": "sha512-rAfulcwp2D9jjdGu+0CuqlrAUin6bBWrpoqXWwKDZZZJfXcUXQSxLJOFJCQCSA0x0pP2U0TxSlJu2ROq5Bq6qA==",
"dev": true,
"requires": {
"chalk": "^2.4.1",
"coa": "^2.0.2",
@@ -18457,7 +18700,7 @@
"css-tree": "1.0.0-alpha.28",
"css-url-regex": "^1.1.0",
"csso": "^3.5.1",
"js-yaml": "^3.13.0",
"js-yaml": "^3.13.1",
"mkdirp": "~0.5.1",
"object.values": "^1.1.0",
"sax": "~1.2.4",
@@ -18570,13 +18813,13 @@
"dev": true
},
"tar": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
"integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=",
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
"integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
"dev": true,
"requires": {
"block-stream": "*",
"fstream": "^1.0.2",
"fstream": "^1.0.12",
"inherits": "2"
}
},
@@ -18664,9 +18907,9 @@
}
},
"terser": {
"version": "3.17.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz",
"integrity": "sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.0.0.tgz",
"integrity": "sha512-dOapGTU0hETFl1tCo4t56FN+2jffoKyER9qBGoUFyZ6y7WLoKT0bF+lAYi6B6YsILcGF3q1C2FBh8QcKSCgkgA==",
"dev": true,
"requires": {
"commander": "^2.19.0",
@@ -18693,19 +18936,21 @@
}
},
"terser-webpack-plugin": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.3.tgz",
"integrity": "sha512-GOK7q85oAb/5kE12fMuLdn2btOS9OBZn4VsecpHDywoUC/jLhSAKOiYo0ezx7ss2EXPMzyEWFoE0s1WLE+4+oA==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.3.0.tgz",
"integrity": "sha512-W2YWmxPjjkUcOWa4pBEv4OP4er1aeQJlSo2UhtCFQCuRXEHjOFscO8VyWHj9JLlA0RzQb8Y2/Ta78XZvT54uGg==",
"dev": true,
"requires": {
"cacache": "^11.0.2",
"cacache": "^11.3.2",
"find-cache-dir": "^2.0.0",
"is-wsl": "^1.1.0",
"loader-utils": "^1.2.3",
"schema-utils": "^1.0.0",
"serialize-javascript": "^1.4.0",
"serialize-javascript": "^1.7.0",
"source-map": "^0.6.1",
"terser": "^3.16.1",
"webpack-sources": "^1.1.0",
"worker-farm": "^1.5.2"
"terser": "^4.0.0",
"webpack-sources": "^1.3.0",
"worker-farm": "^1.7.0"
},
"dependencies": {
"ajv": {
@@ -19040,11 +19285,6 @@
"setimmediate": "^1.0.4"
}
},
"timsort": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
},
"tiny-invariant": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.4.tgz",
@@ -19157,6 +19397,12 @@
"to-no-case": "^1.0.0"
}
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
"dev": true
},
"toposort": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz",
@@ -19471,12 +19717,6 @@
}
}
},
"trim": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz",
"integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=",
"dev": true
},
"trim-newlines": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
@@ -19499,9 +19739,9 @@
"dev": true
},
"trough": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/trough/-/trough-1.0.3.tgz",
"integrity": "sha512-fwkLWH+DimvA4YCy+/nvJd61nWQQ2liO/nF/RjkTpiOGi+zxZzVkhb1mvbHIIW4b/8nDsYI8uTmAlc0nNkRMOw==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/trough/-/trough-1.0.4.tgz",
"integrity": "sha512-tdzBRDGWcI1OpPVmChbdSKhvSVurznZ8X36AYURAcl+0o2ldlCY2XPzyXNNxwJwwyIU+rIglTCG4kxtNKBQH7Q==",
"dev": true
},
"true-case-path": {
@@ -19513,6 +19753,12 @@
"glob": "^7.1.2"
}
},
"tryer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz",
"integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==",
"dev": true
},
"tslib": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
@@ -19550,13 +19796,13 @@
}
},
"type-is": {
"version": "1.6.16",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
"integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dev": true,
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.18"
"mime-types": "~2.1.24"
}
},
"typedarray": {
@@ -19715,12 +19961,14 @@
"uniq": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
"integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8="
"integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
"dev": true
},
"uniqs": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz",
"integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI="
"integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=",
"dev": true
},
"unique-filename": {
"version": "1.1.1",
@@ -19775,7 +20023,8 @@
"unquote": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz",
"integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ="
"integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=",
"dev": true
},
"unset-value": {
"version": "1.0.0",
@@ -19947,6 +20196,12 @@
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"mime": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.3.tgz",
"integrity": "sha512-QgrPRJfE+riq5TPZMcHZOtm8c6K/yYrMbKIoRfapfiGLxS8OTeIfRhUGW5LU7MlRa52KOAGCfUNruqLrIBvWZw==",
"dev": true
},
"schema-utils": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
@@ -19961,11 +20216,11 @@
}
},
"url-parse": {
"version": "1.4.6",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.6.tgz",
"integrity": "sha512-/B8AD9iQ01seoXmXf9z/MjLZQIdOoYl/+gvsQF6+mpnxaTfG9P7srYaiqaDMyKkR36XMXfhqSHss5MyFAO8lew==",
"version": "1.4.7",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz",
"integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==",
"requires": {
"querystringify": "^2.0.0",
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
}
},
@@ -19978,6 +20233,11 @@
"prepend-http": "^1.0.1"
}
},
"url-polyfill": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/url-polyfill/-/url-polyfill-1.1.5.tgz",
"integrity": "sha512-9XjIJ6nwrU+nGd8t90Ze0Zs7t8A+SU0gqsqPttj6j3zAVe5q0HFcuv37nDBdVSPpi4aTHTfbUF/i+ZVD+o2EbA=="
},
"url-to-options": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz",
@@ -20020,6 +20280,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz",
"integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==",
"dev": true,
"requires": {
"define-properties": "^1.1.2",
"object.getownpropertydescriptors": "^2.0.3"
@@ -20044,9 +20305,9 @@
"dev": true
},
"v8-compile-cache": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz",
"integrity": "sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw==",
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz",
"integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==",
"dev": true
},
"v8flags": {
@@ -20080,9 +20341,10 @@
"dev": true
},
"vendors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.2.tgz",
"integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ=="
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.3.tgz",
"integrity": "sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw==",
"dev": true
},
"verror": {
"version": "1.10.0",
@@ -20115,9 +20377,9 @@
"dev": true
},
"unist-util-stringify-position": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.0.tgz",
"integrity": "sha512-Uz5negUTrf9zm2ZT2Z9kdOL7Mr7FJLyq3ByqagUi7QZRVK1HnspVazvSqwHt73jj7APHtpuJ4K110Jm8O6/elw==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.1.tgz",
"integrity": "sha512-Zqlf6+FRI39Bah8Q6ZnNGrEHUhwJOkHde2MHVk96lLyftfJJckaPslKgzhVcviXj8KcE9UJM9F+a4JEiBUTYgA==",
"dev": true,
"requires": {
"@types/unist": "^2.0.2"
@@ -20279,9 +20541,9 @@
}
},
"chokidar": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.5.tgz",
"integrity": "sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A==",
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz",
"integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==",
"dev": true,
"requires": {
"anymatch": "^2.0.0",
@@ -20594,9 +20856,9 @@
}
},
"web-namespaces": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.2.tgz",
"integrity": "sha512-II+n2ms4mPxK+RnIxRPOw3zwF2jRscdJIUE9BfkKHm4FYEg9+biIoTMnaZF5MpemE3T+VhMLrhbyD4ilkPCSbg==",
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.3.tgz",
"integrity": "sha512-r8sAtNmgR0WKOKOxzuSgk09JsHlpKlB+uHi937qypOu3PZ17UxPrierFKDye/uNHjNTTEshu5PId8rojIPj/tA==",
"dev": true
},
"webidl-conversions": {
@@ -20606,9 +20868,9 @@
"dev": true
},
"webpack": {
"version": "4.30.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.30.0.tgz",
"integrity": "sha512-4hgvO2YbAFUhyTdlR4FNyt2+YaYBYHavyzjCMbZzgglo02rlKi/pcsEzwCuCpsn1ryzIl1cq/u8ArIKu8JBYMg==",
"version": "4.32.2",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.32.2.tgz",
"integrity": "sha512-F+H2Aa1TprTQrpodRAWUMJn7A8MgDx82yQiNvYMaj3d1nv3HetKU0oqEulL9huj8enirKi8KvEXQ3QtuHF89Zg==",
"dev": true,
"requires": {
"@webassemblyjs/ast": "1.8.5",
@@ -20979,10 +21241,76 @@
}
}
},
"webpack-bundle-analyzer": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.3.2.tgz",
"integrity": "sha512-7qvJLPKB4rRWZGjVp5U1KEjwutbDHSKboAl0IfafnrdXMrgC0tOtZbQD6Rw0u4cmpgRN4O02Fc0t8eAT+FgGzA==",
"dev": true,
"requires": {
"acorn": "^6.0.7",
"acorn-walk": "^6.1.1",
"bfj": "^6.1.1",
"chalk": "^2.4.1",
"commander": "^2.18.0",
"ejs": "^2.6.1",
"express": "^4.16.3",
"filesize": "^3.6.1",
"gzip-size": "^5.0.0",
"lodash": "^4.17.10",
"mkdirp": "^0.5.1",
"opener": "^1.5.1",
"ws": "^6.0.0"
},
"dependencies": {
"acorn": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz",
"integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==",
"dev": true
},
"commander": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
"integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
"dev": true
},
"filesize": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz",
"integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==",
"dev": true
},
"gzip-size": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
"integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==",
"dev": true,
"requires": {
"duplexer": "^0.1.1",
"pify": "^4.0.1"
}
},
"pify": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"dev": true
},
"ws": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
"dev": true,
"requires": {
"async-limiter": "~1.0.0"
}
}
}
},
"webpack-cli": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.0.tgz",
"integrity": "sha512-t1M7G4z5FhHKJ92WRKwZ1rtvi7rHc0NZoZRbSkol0YKl4HvcC8+DsmGDmK7MmZxHSAetHagiOsjOB6MmzC2TUw==",
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.2.tgz",
"integrity": "sha512-FLkobnaJJ+03j5eplxlI0TUxhGCOdfewspIGuvDVtpOlrAuKMFC57K42Ukxqs1tn8947/PM6tP95gQc0DCzRYA==",
"dev": true,
"requires": {
"chalk": "^2.4.1",
@@ -21220,35 +21548,43 @@
}
},
"webpack-dev-middleware": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.6.2.tgz",
"integrity": "sha512-A47I5SX60IkHrMmZUlB0ZKSWi29TZTcPz7cha1Z75yYOsgWh/1AcPmQEbC8ZIbU3A1ytSv1PMU0PyPz2Lmz2jg==",
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.0.tgz",
"integrity": "sha512-qvDesR1QZRIAZHOE3iQ4CXLZZSQ1lAUsSpnQmlB1PBfoN/xdRjmge3Dok0W4IdaVLJOGJy3sGI4sZHwjRU0PCA==",
"dev": true,
"requires": {
"memory-fs": "^0.4.1",
"mime": "^2.3.1",
"range-parser": "^1.0.3",
"mime": "^2.4.2",
"range-parser": "^1.2.1",
"webpack-log": "^2.0.0"
},
"dependencies": {
"mime": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.3.tgz",
"integrity": "sha512-QgrPRJfE+riq5TPZMcHZOtm8c6K/yYrMbKIoRfapfiGLxS8OTeIfRhUGW5LU7MlRa52KOAGCfUNruqLrIBvWZw==",
"dev": true
}
}
},
"webpack-dev-server": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.3.1.tgz",
"integrity": "sha512-jY09LikOyGZrxVTXK0mgIq9y2IhCoJ05848dKZqX1gAGLU1YDqgpOT71+W53JH/wI4v6ky4hm+KvSyW14JEs5A==",
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.4.1.tgz",
"integrity": "sha512-CRqZQX2ryMtrg0r3TXQPpNh76eM1HD3Wmu6zDBxIKi/d2y+4aa28Ia8weNT0bfgWpY6Vs3Oq/K8+DjfbR+tWYw==",
"dev": true,
"requires": {
"ansi-html": "0.0.7",
"bonjour": "^3.5.0",
"chokidar": "^2.1.5",
"chokidar": "^2.1.6",
"compression": "^1.7.4",
"connect-history-api-fallback": "^1.6.0",
"debug": "^4.1.1",
"del": "^4.1.0",
"express": "^4.16.4",
"del": "^4.1.1",
"express": "^4.17.0",
"html-entities": "^1.2.1",
"http-proxy-middleware": "^0.19.1",
"import-local": "^2.0.0",
"internal-ip": "^4.2.0",
"internal-ip": "^4.3.0",
"ip": "^1.1.5",
"killable": "^1.0.1",
"loglevel": "^1.6.1",
@@ -21264,7 +21600,7 @@
"strip-ansi": "^3.0.1",
"supports-color": "^6.1.0",
"url": "^0.11.0",
"webpack-dev-middleware": "^3.6.2",
"webpack-dev-middleware": "^3.7.0",
"webpack-log": "^2.0.0",
"yargs": "12.0.5"
},
@@ -21356,9 +21692,9 @@
}
},
"chokidar": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.5.tgz",
"integrity": "sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A==",
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz",
"integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==",
"dev": true,
"requires": {
"anymatch": "^2.0.0",
@@ -21890,9 +22226,9 @@
}
},
"semver": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz",
"integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==",
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.1.0.tgz",
"integrity": "sha512-kCqEOOHoBcFs/2Ccuk4Xarm/KiWRSLEX9CAZF8xkJ6ZPlIoTZ8V5f7J16vYLJqDbR7KrxTJpR2lqjIEm2Qx9cQ==",
"dev": true
},
"sockjs-client": {
@@ -22007,22 +22343,11 @@
"lodash": "^4.17.5"
}
},
"webpack-rtl-plugin": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/webpack-rtl-plugin/-/webpack-rtl-plugin-2.0.0.tgz",
"integrity": "sha512-lROgFkiPjapg9tcZ8FiLWeP5pJoG00018aEjLTxSrVldPD1ON+LPlhKPHjb7eE8Bc0+KL23pxcAjWDGOv9+UAw==",
"requires": {
"@romainberger/css-diff": "^1.0.3",
"async": "^2.0.0",
"cssnano": "4.1.10",
"rtlcss": "2.4.0",
"webpack-sources": "1.3.0"
}
},
"webpack-sources": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz",
"integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==",
"dev": true,
"requires": {
"source-list-map": "^2.0.0",
"source-map": "~0.6.1"
@@ -22177,9 +22502,9 @@
"dev": true
},
"worker-farm": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz",
"integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==",
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
"integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
"dev": true,
"requires": {
"errno": "~0.1.7"
@@ -22210,9 +22535,9 @@
}
},
"write-file-atomic": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz",
"integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==",
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz",
"integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.11",

View File

@@ -25,15 +25,16 @@
},
"dependencies": {
"@cospired/i18n-iso-languages": "^2.0.2",
"@edx/edx-bootstrap": "^2.0.1",
"@edx/frontend-analytics": "^1.0.0",
"@edx/frontend-auth": "^5.0.0",
"@edx/frontend-component-footer": "^2.0.3",
"@edx/frontend-component-site-header": "^2.1.4",
"@edx/frontend-logging": "^2.0.0",
"@edx/paragon": "^4.1.3",
"@fortawesome/fontawesome-svg-core": "^1.2.17",
"@fortawesome/free-brands-svg-icons": "^5.7.2",
"@edx/edx-bootstrap": "^2.2.1",
"@edx/frontend-analytics": "^2.0.0",
"@edx/frontend-auth": "^5.3.4",
"@edx/frontend-component-footer": "^6.0.2",
"@edx/frontend-component-site-header": "^2.4.0",
"@edx/frontend-i18n": "^2.1.0",
"@edx/frontend-logging": "^2.0.2",
"@edx/paragon": "^4.2.6",
"@fortawesome/fontawesome-svg-core": "^1.2.18",
"@fortawesome/free-brands-svg-icons": "^5.8.2",
"@fortawesome/free-regular-svg-icons": "^5.7.1",
"@fortawesome/free-solid-svg-icons": "^5.8.1",
"@fortawesome/react-fontawesome": "^0.1.4",
@@ -43,22 +44,28 @@
"email-prop-type": "^1.1.5",
"font-awesome": "^4.7.0",
"form-urlencoded": "^3.0.0",
"formdata-polyfill": "^3.0.18",
"glob": "^7.1.3",
"history": "^4.7.2",
"i18n-iso-countries": "^3.7.8",
"iso-countries-languages": "^0.2.1",
"lodash.camelcase": "^4.3.0",
"lodash.findindex": "^4.6.0",
"lodash.get": "^4.4.2",
"lodash.isempty": "^4.4.0",
"lodash.omit": "^4.5.0",
"lodash.pick": "^4.4.0",
"lodash.snakecase": "^4.1.1",
"memoize-one": "^5.0.4",
"newrelic": "^5.5.0",
"prop-types": "^15.5.10",
"react": "^16.8.3",
"react-dom": "^16.8.3",
"react-intl": "^2.8.0",
"react-redux": "^5.1.1",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-router-hash-link": "^1.2.1",
"react-scrollspy": "^3.4.0",
"react-transition-group": "^2.5.3",
"redux": "^4.0.1",
"redux-devtools-extension": "^2.13.2",
@@ -67,7 +74,7 @@
"redux-thunk": "^2.2.0",
"reselect": "^4.0.0",
"universal-cookie": "^3.1.0",
"webpack-rtl-plugin": "^2.0.0"
"url-polyfill": "^1.1.5"
},
"devDependencies": {
"@svgr/webpack": "^4.2.0",
@@ -79,6 +86,7 @@
"babel-loader": "^7.1.2",
"babel-plugin-react-intl": "^3.0.1",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-imports": "^1.5.1",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
@@ -88,7 +96,7 @@
"css-loader": "^0.28.9",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"es-check": "^2.0.2",
"es-check": "^5.0.0",
"eslint-config-edx": "^4.0.3",
"fetch-mock": "^6.3.0",
"file-loader": "^1.1.9",
@@ -100,17 +108,22 @@
"image-webpack-loader": "^4.2.0",
"jest": "^22.4.0",
"mini-css-extract-plugin": "^0.4.0",
"new-relic-source-map-webpack-plugin": "1.1.0",
"node-sass": "^4.7.2",
"postcss-loader": "^3.0.0",
"postcss-rtl": "^1.3.3",
"purgecss-webpack-plugin": "^1.5.0",
"react-dev-utils": "^5.0.0",
"react-test-renderer": "^16.8.6",
"reactifex": "1.1.1",
"redux-mock-store": "^1.5.3",
"sass-loader": "^6.0.6",
"source-map-loader": "^0.2.4",
"style-loader": "^0.20.2",
"travis-deploy-once": "^5.0.9",
"url-loader": "^1.1.2",
"webpack": "^4.25.1",
"webpack-bundle-analyzer": "^3.3.2",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.0",
"webpack-merge": "^4.1.1"
@@ -123,8 +136,7 @@
"moduleNameMapper": {
"\\.svg": "<rootDir>/__mocks__/svgrMock.js",
"\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
"\\.(css|scss)$": "identity-obj-proxy",
"@edx/frontend-i18n(.*)$": "<rootDir>/src/i18n$1"
"\\.(css|scss)$": "identity-obj-proxy"
},
"collectCoverageFrom": [
"src/**/*.{js,jsx}"

View File

@@ -1,131 +1,376 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import memoize from 'memoize-one';
import findIndex from 'lodash.findindex';
import {
injectIntl,
intlShape,
getLocale,
getCountryList,
getLanguageList,
} from '@edx/frontend-i18n'; // eslint-disable-line
FormattedMessage,
} from '@edx/frontend-i18n';
import { Hyperlink } from '@edx/paragon';
import messages from './AccountSettingsPage.messages';
import { fetchAccount, fetchThirdPartyAuthProviders } from './actions';
import { accountSettingsSelector } from './selectors';
import { PageLoading } from '../common';
import EditableField from './components/EditableField';
import PasswordReset from './components/PasswordReset';
import ThirdPartyAuth from './components/ThirdPartyAuth';
import EmailField from './components/EmailField';
import { fetchSettings, saveSettings, updateDraft } from './actions';
import { accountSettingsPageSelector } from './selectors';
import { Alert, PageLoading } from '../common';
import JumpNav from './JumpNav';
import DeleteAccount from './delete-account';
import EditableField from './EditableField';
import ResetPassword from './reset-password';
import ThirdPartyAuth from './third-party-auth';
import BetaLanguageBanner from './BetaLanguageBanner';
import EmailField from './EmailField';
import {
YEAR_OF_BIRTH_OPTIONS,
EDUCATION_LEVELS,
GENDER_OPTIONS,
} from './constants';
import { fetchSiteLanguages } from './site-language';
class AccountSettingsPage extends React.Component {
constructor(props) {
super(props);
this.countryOptions = getCountryList(getLocale())
.map(({ code, name }) => ({ value: code, label: name }));
this.languageProficiencyOptions = getLanguageList(getLocale())
.map(({ code, name }) => ({ value: code, label: name }));
this.educationLevels = EDUCATION_LEVELS.map(key => ({
value: key,
label: props.intl.formatMessage(messages[`account.settings.field.education.levels.${key}`]),
label: props.intl.formatMessage(messages[`account.settings.field.education.levels.${key || 'empty'}`]),
}));
this.genderOptions = GENDER_OPTIONS.map(key => ({
value: key,
label: props.intl.formatMessage(messages[`account.settings.field.gender.options.${key}`]),
label: props.intl.formatMessage(messages[`account.settings.field.gender.options.${key || 'empty'}`]),
}));
this.languageProficiencyOptions = [{
value: '',
label: props.intl.formatMessage(messages['account.settings.field.language_proficiencies.options.empty']),
}].concat(props.languageProficiencyOptions);
this.yearOfBirthOptions = [{
value: '',
label: props.intl.formatMessage(messages['account.settings.field.year_of_birth.options.empty']),
}].concat(YEAR_OF_BIRTH_OPTIONS);
this.countryOptions = [{
value: '',
label: props.intl.formatMessage(messages['account.settings.field.country.options.empty']),
}].concat(props.countryOptions);
}
componentDidMount() {
this.props.fetchAccount();
this.props.fetchThirdPartyAuthProviders();
this.props.fetchSettings();
this.props.fetchSiteLanguages();
}
getTimeZoneOptions = memoize((timeZoneOptions, countryTimeZoneOptions) => {
const concatTimeZoneOptions = [{
label: this.props.intl.formatMessage(messages['account.settings.field.time.zone.default']),
value: '',
}];
if (countryTimeZoneOptions.length) {
concatTimeZoneOptions.push({
label: this.props.intl.formatMessage(messages['account.settings.field.time.zone.country']),
group: countryTimeZoneOptions,
});
}
concatTimeZoneOptions.push({
label: this.props.intl.formatMessage(messages['account.settings.field.time.zone.all']),
group: timeZoneOptions,
});
return concatTimeZoneOptions;
});
isEditable(fieldName) {
return !this.props.staticFields.includes(fieldName);
}
isManagedProfile() {
// Enterprise customer profiles are managed by their organizations. We determine whether
// a profile is managed or not by the presence of the profileDataManager prop.
return Boolean(this.props.profileDataManager);
}
handleEditableFieldChange = (name, value) => {
this.props.updateDraft(name, value);
};
handleSubmit = (formId, values) => {
this.props.saveSettings(formId, values);
};
renderDuplicateTpaProviderMessage() {
if (!this.props.duplicateTpaProvider) {
return null;
}
return (
<div>
<Alert className="alert alert-danger" role="alert">
<FormattedMessage
id="account.settings.message.duplicate.tpa.provider"
defaultMessage="The {provider} account you selected is already linked to another edX account."
description="alert message informing the user that the third-party account they attempted to link is already linked to another edX account"
values={{
provider: <b>{this.props.duplicateTpaProvider}</b>,
}}
/>
</Alert>
</div>
);
}
renderManagedProfileMessage() {
if (!this.isManagedProfile()) {
return null;
}
return (
<div>
<Alert className="alert alert-primary" role="alert">
<FormattedMessage
id="account.settings.message.managed.settings"
defaultMessage="Your profile settings are managed by {managerTitle}. Contact your administrator or {support} for help."
description="alert message informing the user their account data is managed by a third party"
values={{
managerTitle: <b>{this.props.profileDataManager}</b>,
support: (
<Hyperlink destination={this.props.supportUrl} target="_blank">
<FormattedMessage
id="account.settings.message.managed.settings.support"
defaultMessage="support"
description="website support"
/>
</Hyperlink>
),
}}
/>
</Alert>
</div>
);
}
renderEmptyStaticFieldMessage() {
if (this.isManagedProfile()) {
return this.props.intl.formatMessage(messages['account.settings.static.field.empty'], {
enterprise: this.props.profileDataManager,
});
}
return this.props.intl.formatMessage(messages['account.settings.static.field.empty.no.admin']);
}
renderSecondaryEmailField(editableFieldProps) {
if (this.props.hiddenFields.includes('secondary_email')) {
return null;
}
return (
<EmailField
name="secondary_email"
label={this.props.intl.formatMessage(messages['account.settings.field.secondary.email'])}
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.secondary.email.empty'])}
value={this.props.formValues.secondary_email}
confirmationMessageDefinition={messages['account.settings.field.secondary.email.confirmation']}
{...editableFieldProps}
/>
);
}
renderContent() {
const editableFieldProps = {
onChange: this.handleEditableFieldChange,
onSubmit: this.handleSubmit,
};
const timeZoneOptions = this.getTimeZoneOptions(
this.props.timeZoneOptions,
this.props.countryTimeZoneOptions,
);
const hasLinkedTPA = findIndex(this.props.tpaProviders, provider => provider.connected) >= 0;
return (
<div>
<div className="row">
<div className="col-md-8 col-lg-6">
<h2>{this.props.intl.formatMessage(messages['account.settings.section.account.information'])}</h2>
<p>{this.props.intl.formatMessage(messages['account.settings.section.account.information.description'])}</p>
<React.Fragment>
<div className="account-section" id="basic-information">
<h2 className="section-heading">
{this.props.intl.formatMessage(messages['account.settings.section.account.information'])}
</h2>
<p>{this.props.intl.formatMessage(messages['account.settings.section.account.information.description'])}</p>
{this.renderManagedProfileMessage()}
<EditableField
name="username"
type="text"
label={this.props.intl.formatMessage(messages['account.settings.field.username'])}
isEditable={false}
/>
<EditableField
name="name"
type="text"
label={this.props.intl.formatMessage(messages['account.settings.field.full.name'])}
/>
<EmailField
name="email"
label={this.props.intl.formatMessage(messages['account.settings.field.email'])}
confirmationMessageDefinition={messages['account.settings.field.email.confirmation']}
/>
<EditableField
name="year_of_birth"
type="select"
label={this.props.intl.formatMessage(messages['account.settings.field.dob'])}
options={YEAR_OF_BIRTH_OPTIONS}
/>
<PasswordReset />
<EditableField
name="country"
type="select"
options={this.countryOptions}
label={this.props.intl.formatMessage(messages['account.settings.field.country'])}
/>
<EditableField
name="level_of_education"
type="select"
options={this.educationLevels}
label={this.props.intl.formatMessage(messages['account.settings.field.education'])}
/>
<EditableField
name="gender"
type="select"
options={this.genderOptions}
label={this.props.intl.formatMessage(messages['account.settings.field.gender'])}
/>
<EditableField
name="language_proficiencies"
type="select"
options={this.languageProficiencyOptions}
label={this.props.intl.formatMessage(messages['account.settings.field.language.proficiencies'])}
/>
<ThirdPartyAuth />
<h2>{this.props.intl.formatMessage(messages['account.settings.section.social.media'])}</h2>
<p>{this.props.intl.formatMessage(messages['account.settings.section.social.media.description'])}</p>
<EditableField
name="social_link_linkedIn"
type="text"
label={this.props.intl.formatMessage(messages['account.settings.field.social.platform.name.linkedin'])}
/>
<EditableField
name="social_link_facebook"
type="text"
label={this.props.intl.formatMessage(messages['account.settings.field.social.platform.name.facebook'])}
/>
<EditableField
name="social_link_twitter"
type="text"
label={this.props.intl.formatMessage(messages['account.settings.field.social.platform.name.twitter'])}
/>
</div>
<EditableField
name="username"
type="text"
value={this.props.formValues.username}
label={this.props.intl.formatMessage(messages['account.settings.field.username'])}
helpText={this.props.intl.formatMessage(messages['account.settings.field.username.help.text'])}
isEditable={false}
{...editableFieldProps}
/>
<EditableField
name="name"
type="text"
value={this.props.formValues.name}
label={this.props.intl.formatMessage(messages['account.settings.field.full.name'])}
emptyLabel={
this.isEditable('name') ?
this.props.intl.formatMessage(messages['account.settings.field.full.name.empty']) :
this.renderEmptyStaticFieldMessage()
}
helpText={this.props.intl.formatMessage(messages['account.settings.field.full.name.help.text'])}
isEditable={this.isEditable('name')}
{...editableFieldProps}
/>
<EmailField
name="email"
label={this.props.intl.formatMessage(messages['account.settings.field.email'])}
emptyLabel={
this.isEditable('email') ?
this.props.intl.formatMessage(messages['account.settings.field.email.empty']) :
this.renderEmptyStaticFieldMessage()
}
value={this.props.formValues.email}
confirmationMessageDefinition={messages['account.settings.field.email.confirmation']}
helpText={this.props.intl.formatMessage(messages['account.settings.field.email.help.text'])}
isEditable={this.isEditable('email')}
{...editableFieldProps}
/>
{this.renderSecondaryEmailField(editableFieldProps)}
<ResetPassword email={this.props.formValues.email} />
<EditableField
name="year_of_birth"
type="select"
label={this.props.intl.formatMessage(messages['account.settings.field.dob'])}
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.dob.empty'])}
value={this.props.formValues.year_of_birth}
options={this.yearOfBirthOptions}
{...editableFieldProps}
/>
<EditableField
name="country"
type="select"
value={this.props.formValues.country}
options={this.countryOptions}
label={this.props.intl.formatMessage(messages['account.settings.field.country'])}
emptyLabel={
this.isEditable('country') ?
this.props.intl.formatMessage(messages['account.settings.field.country.empty']) :
this.renderEmptyStaticFieldMessage()
}
isEditable={this.isEditable('country')}
{...editableFieldProps}
/>
</div>
</div>
<div className="account-section" id="profile-information">
<h2 className="section-heading">
{this.props.intl.formatMessage(messages['account.settings.section.profile.information'])}
</h2>
<EditableField
name="level_of_education"
type="select"
value={this.props.formValues.level_of_education}
options={this.educationLevels}
label={this.props.intl.formatMessage(messages['account.settings.field.education'])}
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.education.empty'])}
{...editableFieldProps}
/>
<EditableField
name="gender"
type="select"
value={this.props.formValues.gender}
options={this.genderOptions}
label={this.props.intl.formatMessage(messages['account.settings.field.gender'])}
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.gender.empty'])}
{...editableFieldProps}
/>
<EditableField
name="language_proficiencies"
type="select"
value={this.props.formValues.language_proficiencies}
options={this.languageProficiencyOptions}
label={this.props.intl.formatMessage(messages['account.settings.field.language.proficiencies'])}
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.language.proficiencies.empty'])}
{...editableFieldProps}
/>
</div>
<div className="account-section" id="social-media">
<h2 className="section-heading">
{this.props.intl.formatMessage(messages['account.settings.section.social.media'])}
</h2>
<p>{this.props.intl.formatMessage(messages['account.settings.section.social.media.description'])}</p>
<EditableField
name="social_link_linkedin"
type="text"
value={this.props.formValues.social_link_linkedin}
label={this.props.intl.formatMessage(messages['account.settings.field.social.platform.name.linkedin'])}
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.social.platform.name.linkedin.empty'])}
{...editableFieldProps}
/>
<EditableField
name="social_link_facebook"
type="text"
value={this.props.formValues.social_link_facebook}
label={this.props.intl.formatMessage(messages['account.settings.field.social.platform.name.facebook'])}
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.social.platform.name.facebook.empty'])}
{...editableFieldProps}
/>
<EditableField
name="social_link_twitter"
type="text"
value={this.props.formValues.social_link_twitter}
label={this.props.intl.formatMessage(messages['account.settings.field.social.platform.name.twitter'])}
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.social.platform.name.twitter.empty'])}
{...editableFieldProps}
/>
</div>
<div className="account-section" id="site-preferences">
<h2 className="section-heading">
{this.props.intl.formatMessage(messages['account.settings.section.site.preferences'])}
</h2>
<BetaLanguageBanner />
<EditableField
name="siteLanguage"
type="select"
options={this.props.siteLanguageOptions}
value={this.props.siteLanguage.draftOrSavedValue}
label={this.props.intl.formatMessage(messages['account.settings.field.site.language'])}
helpText={this.props.intl.formatMessage(messages['account.settings.field.site.language.help.text'])}
{...editableFieldProps}
/>
<EditableField
name="time_zone"
type="select"
value={this.props.formValues.time_zone || ''}
options={timeZoneOptions}
label={this.props.intl.formatMessage(messages['account.settings.field.time.zone'])}
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.time.zone.empty'])}
helpText={this.props.intl.formatMessage(messages['account.settings.field.time.zone.description'])}
{...editableFieldProps}
onSubmit={(formId, value) => {
// the endpoint will not accept an empty string. it must be null
this.handleSubmit(formId, value || null);
}}
/>
</div>
<div className="account-section" id="linked-accounts">
<h2 className="section-heading">{this.props.intl.formatMessage(messages['account.settings.section.linked.accounts'])}</h2>
<p>{this.props.intl.formatMessage(messages['account.settings.section.linked.accounts.description'])}</p>
<ThirdPartyAuth />
</div>
<div className="account-section" id="delete-account">
<DeleteAccount
isVerifiedAccount={this.props.isActive}
hasLinkedTPA={hasLinkedTPA}
logoutUrl={this.props.logoutUrl}
/>
</div>
</React.Fragment>
);
}
@@ -154,12 +399,22 @@ class AccountSettingsPage extends React.Component {
return (
<div className="page__account-settings container-fluid py-5">
<h1>
{this.renderDuplicateTpaProviderMessage()}
<h1 className="mb-4">
{this.props.intl.formatMessage(messages['account.settings.page.heading'])}
</h1>
{loading ? this.renderLoading() : null}
{loaded ? this.renderContent() : null}
{loadingError ? this.renderError() : null}
<div>
<div className="row">
<div className="col-md-3">
<JumpNav />
</div>
<div className="col-md-9">
{loading ? this.renderLoading() : null}
{loaded ? this.renderContent() : null}
{loadingError ? this.renderError() : null}
</div>
</div>
</div>
</div>
);
}
@@ -170,18 +425,84 @@ AccountSettingsPage.propTypes = {
loading: PropTypes.bool,
loaded: PropTypes.bool,
loadingError: PropTypes.string,
fetchAccount: PropTypes.func.isRequired,
fetchThirdPartyAuthProviders: PropTypes.func.isRequired,
// Form data
formValues: PropTypes.shape({
username: PropTypes.string,
name: PropTypes.string,
email: PropTypes.string,
secondary_email: PropTypes.string,
year_of_birth: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
country: PropTypes.string,
level_of_education: PropTypes.string,
gender: PropTypes.string,
language_proficiencies: PropTypes.string,
social_link_linkedin: PropTypes.string,
social_link_facebook: PropTypes.string,
social_link_twitter: PropTypes.string,
time_zone: PropTypes.string,
}).isRequired,
siteLanguage: PropTypes.shape({
previousValue: PropTypes.string,
draftOrSavedValue: PropTypes.string,
savedValue: PropTypes.string,
}),
siteLanguageOptions: PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
})),
countryOptions: PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
})),
languageProficiencyOptions: PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
})),
profileDataManager: PropTypes.string,
staticFields: PropTypes.arrayOf(PropTypes.string),
hiddenFields: PropTypes.arrayOf(PropTypes.string),
isActive: PropTypes.bool,
timeZoneOptions: PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
})),
countryTimeZoneOptions: PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
})),
fetchSiteLanguages: PropTypes.func.isRequired,
updateDraft: PropTypes.func.isRequired,
saveSettings: PropTypes.func.isRequired,
fetchSettings: PropTypes.func.isRequired,
duplicateTpaProvider: PropTypes.string,
tpaProviders: PropTypes.arrayOf(PropTypes.object),
supportUrl: PropTypes.string.isRequired,
logoutUrl: PropTypes.string.isRequired,
};
AccountSettingsPage.defaultProps = {
loading: false,
loaded: false,
loadingError: null,
siteLanguage: null,
siteLanguageOptions: [],
countryOptions: [],
timeZoneOptions: [],
countryTimeZoneOptions: [],
languageProficiencyOptions: [],
profileDataManager: null,
staticFields: [],
hiddenFields: ['secondary_email'],
duplicateTpaProvider: null,
tpaProviders: [],
isActive: true,
};
export default connect(accountSettingsSelector, {
fetchAccount,
fetchThirdPartyAuthProviders,
export default connect(accountSettingsPageSelector, {
fetchSettings,
saveSettings,
updateDraft,
fetchSiteLanguages,
})(injectIntl(AccountSettingsPage));

View File

@@ -1,4 +1,4 @@
import { defineMessages } from 'react-intl';
import { defineMessages } from '@edx/frontend-i18n';
const messages = defineMessages({
'account.settings.page.heading': {
@@ -8,7 +8,7 @@ const messages = defineMessages({
},
'account.settings.loading.message': {
id: 'account.settings.loading.message',
defaultMessage: 'Loading',
defaultMessage: 'Loading...',
description: 'Message when data is being loaded',
},
'account.settings.loading.error': {
@@ -16,6 +16,21 @@ const messages = defineMessages({
defaultMessage: 'Error: {error}',
description: 'Message when data failed to load',
},
'account.settings.banner.beta.language': {
id: 'account.settings.banner.beta.language',
defaultMessage: 'You have set your language to {beta_language}, which is currently not fully translated. You can help us translate this language fully by joining the Transifex community and adding translations from English for learners that speak {beta_language}.',
description: 'Message when the user selects a beta language this is not yet fully translated.',
},
'account.settings.banner.beta.language.action.switch.back': {
id: 'account.settings.banner.beta.language.action.switch.back',
defaultMessage: 'Switch Back to {previous_language}',
description: 'Button on the beta language message to switch back to the previous language.',
},
'account.settings.banner.beta.language.action.help.translate': {
id: 'account.settings.banner.beta.language.action.help.translate',
defaultMessage: 'Help Translate into {beta_language}',
description: 'Button on the beta language message to help translate the beta language.',
},
'account.settings.section.account.information': {
id: 'account.settings.section.account.information',
defaultMessage: 'Account Information',
@@ -26,26 +41,86 @@ const messages = defineMessages({
defaultMessage: 'These settings include basic information about your account.',
description: 'The basic account information section heading description.',
},
'account.settings.section.profile.information': {
id: 'account.settings.section.profile.information',
defaultMessage: 'Profile Information',
description: 'The profile information section heading.',
},
'account.settings.section.site.preferences': {
id: 'account.settings.section.site.preferences',
defaultMessage: 'Site Preferences',
description: 'The site preferences section heading.',
},
'account.settings.section.linked.accounts': {
id: 'account.settings.section.linked.accounts',
defaultMessage: 'Linked Accounts',
description: 'The linked accounts section heading.',
},
'account.settings.section.linked.accounts.description': {
id: 'account.settings.section.linked.accounts.description',
defaultMessage: 'You can link your identity accounts to simplify signing in to edX.',
description: 'The linked accounts section heading description.',
},
'account.settings.field.username': {
id: 'account.settings.field.username',
defaultMessage: 'Username',
description: 'Label for account settings username field.',
},
'account.settings.field.username.help.text': {
id: 'account.settings.field.username.help.text',
defaultMessage: 'The name that identifies you on edX. You cannot change your username.',
description: 'Help text for the account settings username field.',
},
'account.settings.field.full.name': {
id: 'account.settings.field.full.name',
defaultMessage: 'Full name',
description: 'Label for account settings name field.',
},
'account.settings.field.full.name.empty': {
id: 'account.settings.field.full.name.empty',
defaultMessage: 'Add name',
description: 'Placeholder for empty account settings name field.',
},
'account.settings.field.full.name.help.text': {
id: 'account.settings.field.full.name.help.text',
defaultMessage: 'The name that is used for ID verification and that appears on your certificates.',
description: 'Help text for the account settings name field.',
},
'account.settings.field.email': {
id: 'account.settings.field.email',
defaultMessage: 'Email address (Sign in)',
description: 'Label for account settings email field.',
},
'account.settings.field.email.empty': {
id: 'account.settings.field.email.empty',
defaultMessage: 'Add email address',
description: 'Placeholder for empty account settings email field.',
},
'account.settings.field.email.confirmation': {
id: 'account.settings.field.email.confirmation',
defaultMessage: 'Weve sent a confirmation message to {value}. Click the link in the message to update your email address.',
description: 'Confirmation message for saving the account settings email field.',
},
'account.settings.field.email.help.text': {
id: 'account.settings.field.email.help.text',
defaultMessage: 'You receive messages from edX and course teams at this address.',
description: 'Help text for the account settings email field.',
},
'account.settings.field.secondary.email': {
id: 'account.settings.field.secondary.email',
defaultMessage: 'Recovery email address',
description: 'Label for account settings recovery email field.',
},
'account.settings.field.secondary.email.empty': {
id: 'account.settings.field.secondary.email.empty',
defaultMessage: 'Add a recovery email address',
description: 'Placeholder for empty account settings recovery email field.',
},
'account.settings.field.secondary.email.confirmation': {
id: 'account.settings.field.secondary.email.confirmation',
defaultMessage: 'Weve sent a confirmation message to {value}. Click the link in the message to update your recovery email address.',
description: 'Confirmation message for saving the account settings recovery email field.',
},
'account.settings.email.field.confirmation.header': {
id: 'account.settings.email.field.confirmation.header',
defaultMessage: 'One more step!',
@@ -56,19 +131,53 @@ const messages = defineMessages({
defaultMessage: 'Year of birth',
description: 'Label for account settings year of birth field.',
},
'account.settings.field.dob.empty': {
id: 'account.settings.field.dob.empty',
defaultMessage: 'Add year of birth',
description: 'Placeholder for empty account settings year of birth field.',
},
'account.settings.field.year_of_birth.options.empty': {
id: 'account.settings.field.year_of_birth.options.empty',
defaultMessage: 'Select a year of birth',
description: 'Option for empty value on account settings year of birth field.',
},
'account.settings.field.country': {
id: 'account.settings.field.country',
defaultMessage: 'Country',
description: 'Label for account settings country field.',
},
'account.settings.field.country.empty': {
id: 'account.settings.field.country.empty',
defaultMessage: 'Add country',
description: 'Placeholder for empty account settings country field.',
},
'account.settings.field.country.options.empty': {
id: 'account.settings.field.country.options.empty',
defaultMessage: 'Select a Country',
description: 'Option for empty value on account settings country field.',
},
'account.settings.field.site.language': {
id: 'account.settings.field.site.language',
defaultMessage: 'Site language',
description: 'Label for account settings site language field.',
},
'account.settings.field.site.language.help.text': {
id: 'account.settings.field.site.language.help.text',
defaultMessage: 'The language used throughout this site. This site is currently available in a limited number of languages.',
description: 'Help text for the site language field.',
},
'account.settings.field.education': {
id: 'account.settings.field.education',
defaultMessage: 'Education',
description: 'Label for account settings education field.',
},
'account.settings.field.education.levels.null': {
id: 'account.settings.field.education.levels.null',
'account.settings.field.education.empty': {
id: 'account.settings.field.education.empty',
defaultMessage: 'Add level of education',
description: 'Placeholder for empty account settings education field.',
},
'account.settings.field.education.levels.empty': {
id: 'account.settings.field.education.levels.empty',
defaultMessage: 'Select a level of education',
description: 'Placeholder for the education levels dropdown.',
},
@@ -123,8 +232,13 @@ const messages = defineMessages({
defaultMessage: 'Gender',
description: 'Label for account settings gender field.',
},
'account.settings.field.gender.options.null': {
id: 'account.settings.field.gender.options.null',
'account.settings.field.gender.empty': {
id: 'account.settings.field.gender.empty',
defaultMessage: 'Add gender',
description: 'Placeholder for empty account settings gender field.',
},
'account.settings.field.gender.options.empty': {
id: 'account.settings.field.gender.options.empty',
defaultMessage: 'Select a gender',
description: 'Placeholder for the gender options dropdown.',
},
@@ -143,12 +257,51 @@ const messages = defineMessages({
defaultMessage: 'Other',
description: 'The label for catch-all gender option.',
},
'account.settings.field.language.proficiencies': {
id: 'account.settings.field.language.proficiencies',
defaultMessage: 'Spoken Languages',
defaultMessage: 'Spoken languages',
description: 'Label for account settings spoken languages field.',
},
'account.settings.field.language.proficiencies.empty': {
id: 'account.settings.field.language.proficiencies.empty',
defaultMessage: 'Add a spoken language',
description: 'Placeholder for empty account settings spoken languages field.',
},
'account.settings.field.language_proficiencies.options.empty': {
id: 'account.settings.field.language_proficiencies.options.empty',
defaultMessage: 'Select a Language',
description: 'Option for an empty value on account settings spoken languages field.',
},
'account.settings.field.time.zone': {
id: 'account.settings.field.time.zone',
defaultMessage: 'Time zone',
description: 'Label for time zone settings field.',
},
'account.settings.field.time.zone.empty': {
id: 'account.settings.field.time.zone.empty',
defaultMessage: 'Set time zone',
description: 'Placeholder for empty for time zone settings field.',
},
'account.settings.field.time.zone.description': {
id: 'account.settings.field.time.zone.description',
defaultMessage: 'Select the time zone for displaying course dates. If you do not specify a time zone, course dates, including assignment deadlines, will be displayed in your browsers local time zone.',
description: 'Description for time zone settings field.',
},
'account.settings.field.time.zone.default': {
id: 'account.settings.field.time.zone.default',
defaultMessage: 'Default (Local Time Zone)',
description: 'The default option for a time zone.',
},
'account.settings.field.time.zone.all': {
id: 'account.settings.field.time.zone.all',
defaultMessage: 'All time zones',
description: 'The label for the group of options for all time zones.',
},
'account.settings.field.time.zone.country': {
id: 'account.settings.field.time.zone.country',
defaultMessage: 'Country time zones',
description: 'The group of time zone options for a country.',
},
'account.settings.section.social.media': {
id: 'account.settings.section.social.media',
@@ -165,21 +318,36 @@ const messages = defineMessages({
defaultMessage: 'LinkedIn',
description: 'Label for LinkedIn',
},
'account.settings.field.social.platform.name.linkedin.empty': {
id: 'account.settings.field.social.platform.name.linkedin.empty',
defaultMessage: 'Add LinkedIn profile',
description: 'Placeholder for an empty LinkedIn field',
},
'account.settings.jump.nav.delete.account': {
id: 'account.settings.jump.nav.delete.account',
defaultMessage: 'Delete My Account',
description: 'Header for the user account deletion area',
},
'account.settings.field.social.platform.name.twitter': {
id: 'account.settings.field.social.platform.name.twitter',
defaultMessage: 'Twitter',
description: 'Label for Twitter',
},
'account.settings.field.social.platform.name.twitter.empty': {
id: 'account.settings.field.social.platform.name.twitter.empty',
defaultMessage: 'Add Twitter profile',
description: 'Placeholder for an empty Twitter field',
},
'account.settings.field.social.platform.name.facebook': {
id: 'account.settings.field.social.platform.name.facebook',
defaultMessage: 'Facebook',
description: 'Label for Facebook',
},
'account.settings.editable.field.password.reset.button': {
id: 'account.settings.editable.field.password.reset.button',
defaultMessage: 'Reset Password',
description: 'The password reset button in account settings',
'account.settings.field.social.platform.name.facebook.empty': {
id: 'account.settings.field.social.platform.name.facebook.empty',
defaultMessage: 'Add Facebook profile',
description: 'Placeholder for an empty Facebook field',
},
'account.settings.editable.field.action.save': {
id: 'account.settings.editable.field.action.save',
@@ -196,6 +364,16 @@ const messages = defineMessages({
defaultMessage: 'Edit',
description: 'The edit button on an editable field',
},
'account.settings.static.field.empty': {
id: 'account.settings.static.field.empty',
defaultMessage: 'No value set. Contact your {enterprise} administrator to make changes.',
description: 'The placeholder for an empty but uneditable field',
},
'account.settings.static.field.empty.no.admin': {
id: 'account.settings.static.field.empty.no.admin',
defaultMessage: 'No value set.',
description: 'The placeholder for an empty but uneditable field when there is no administrator',
},
});
export default messages;

View File

@@ -0,0 +1,114 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-i18n';
import { connect } from 'react-redux';
import { Button, Hyperlink } from '@edx/paragon';
import { betaLanguageBannerSelector } from './selectors';
import messages from './AccountSettingsPage.messages';
import { saveSettings } from './actions';
import { TRANSIFEX_LANGUAGE_BASE_URL } from './constants';
import { Alert } from '../common';
class BetaLanguageBanner extends React.Component {
getSiteLanguageEntry(languageCode) {
return this.props.siteLanguageList.filter(l => l.code === languageCode)[0];
}
/**
* Returns a link to the Transifex URL where contributors can provide translations.
* This code is tightly coupled to how Transifex chooses to design its URLs.
*/
getTransifexLink(languageCode) {
return TRANSIFEX_LANGUAGE_BASE_URL + this.getTransifexURLPath(languageCode);
}
/**
* Returns the URL path that Transifex chooses to use for its language sub-pages.
*
* For extended language codes, it returns the 2nd half capitalized, replacing
* hyphen (-) with underscore (_).
* example: pt-br -> pt_BR
*
* For short language codes, it returns the code as is.
* example: fr -> fr
*/
getTransifexURLPath(languageCode) {
const tokenizedCode = languageCode.split('-');
if (tokenizedCode.length > 1) {
return `${tokenizedCode[0]}_${tokenizedCode[1].toUpperCase()}`;
}
return tokenizedCode[0];
}
handleRevertLanguage = () => {
const previousSiteLanguage = this.props.siteLanguage.previousValue;
this.props.saveSettings('siteLanguage', previousSiteLanguage);
};
render() {
const savedLanguage = this.getSiteLanguageEntry(this.props.siteLanguage.savedValue);
const isSavedLanguageReleased = savedLanguage.released === true;
const noPreviousLanguageSet = this.props.siteLanguage.previousValue === null;
if (isSavedLanguageReleased || noPreviousLanguageSet) {
return null;
}
const previousLanguage = this.getSiteLanguageEntry(this.props.siteLanguage.previousValue);
return (
<div>
<Alert className="beta_language_alert alert alert-warning" role="alert">
<p>
{this.props.intl.formatMessage(messages['account.settings.banner.beta.language'], {
beta_language: savedLanguage.name,
})}
</p>
<div>
<Button onClick={this.handleRevertLanguage} className="btn btn-primary mr-2">
{this.props.intl.formatMessage(
messages['account.settings.banner.beta.language.action.switch.back'],
{ previous_language: previousLanguage.name },
)}
</Button>
<Hyperlink
destination={this.getTransifexLink(savedLanguage.code)}
className="btn btn-outline-secondary"
target="_blank"
>
{this.props.intl.formatMessage(
messages['account.settings.banner.beta.language.action.help.translate'],
{ beta_language: savedLanguage.name },
)}
</Hyperlink>
</div>
</Alert>
</div>
);
}
}
BetaLanguageBanner.propTypes = {
intl: intlShape.isRequired,
siteLanguage: PropTypes.shape({
previousValue: PropTypes.string,
draftOrSavedValue: PropTypes.string,
savedValue: PropTypes.string,
}),
siteLanguageList: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
code: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
released: PropTypes.bool,
})).isRequired,
saveSettings: PropTypes.func.isRequired,
};
BetaLanguageBanner.defaultProps = {
siteLanguage: null,
};
export default connect(
betaLanguageBannerSelector,
{
saveSettings,
},
)(injectIntl(BetaLanguageBanner));

View File

@@ -1,27 +1,26 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, intlShape } from '@edx/frontend-i18n'; // eslint-disable-line
import { Button, StatefulButton } from '@edx/paragon';
import { injectIntl, intlShape } from '@edx/frontend-i18n';
import { Button, Input, StatefulButton, ValidationFormGroup } from '@edx/paragon';
import { faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import Input from './temp/Input';
import ValidationFormGroup from './temp/ValidationFormGroup';
import SwitchContent from './temp/SwitchContent';
import messages from '../AccountSettingsPage.messages';
import { SwitchContent } from '../common';
import messages from './AccountSettingsPage.messages';
import {
openForm,
closeForm,
updateDraft,
saveAccount,
} from '../actions';
import { editableFieldSelector } from '../selectors';
} from './actions';
import { editableFieldSelector } from './selectors';
function EditableField(props) {
const {
name,
label,
emptyLabel,
type,
value,
options,
@@ -41,16 +40,6 @@ function EditableField(props) {
} = props;
const id = `field-${name}`;
const getValue = (rawValue) => {
if (options) {
// Use == instead of === to prevent issues when HTML casts numbers as strings
// eslint-disable-next-line eqeqeq
const selectedOption = options.find(option => option.value == rawValue);
if (selectedOption) return selectedOption.label;
}
return rawValue;
};
const handleSubmit = (e) => {
e.preventDefault();
onSubmit(name, new FormData(e.target).get(name));
@@ -68,6 +57,26 @@ function EditableField(props) {
onCancel(name);
};
const renderEmptyLabel = () => {
if (isEditable) {
return <Button onClick={handleEdit} className="btn-link p-0">{emptyLabel}</Button>;
}
return <span className="text-muted">{emptyLabel}</span>;
};
const renderValue = (rawValue) => {
if (!rawValue) return renderEmptyLabel();
if (options) {
// Use == instead of === to prevent issues when HTML casts numbers as strings
// eslint-disable-next-line eqeqeq
const selectedOption = options.find(option => option.value == rawValue);
if (selectedOption) return selectedOption.label;
}
return rawValue;
};
const renderConfirmationMessage = () => {
if (!confirmationMessageDefinition || !confirmationValue) return null;
return intl.formatMessage(confirmationMessageDefinition, {
@@ -129,15 +138,15 @@ function EditableField(props) {
),
default: (
<div className="form-group">
<div className="d-flex justify-content-between align-items-start">
<h6>{label}</h6>
<div className="d-flex align-items-start">
<h6 aria-level="3">{label}</h6>
{isEditable ? (
<Button onClick={handleEdit} className="btn-link">
{intl.formatMessage(messages['account.settings.editable.field.action.edit'])}
<Button onClick={handleEdit} className="ml-3 btn-link">
<FontAwesomeIcon className="mr-1" icon={faPencilAlt} />{intl.formatMessage(messages['account.settings.editable.field.action.edit'])}
</Button>
) : null}
</div>
<p>{getValue(value)}</p>
<p>{renderValue(value)}</p>
<p className="small text-muted mt-n2">{renderConfirmationMessage() || helpText}</p>
</div>
),
@@ -150,6 +159,7 @@ function EditableField(props) {
EditableField.propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
emptyLabel: PropTypes.node,
type: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
options: PropTypes.arrayOf(PropTypes.shape({
@@ -179,6 +189,7 @@ EditableField.defaultProps = {
options: undefined,
saveState: undefined,
label: undefined,
emptyLabel: undefined,
error: undefined,
confirmationMessageDefinition: undefined,
confirmationValue: undefined,
@@ -191,6 +202,4 @@ EditableField.defaultProps = {
export default connect(editableFieldSelector, {
onEdit: openForm,
onCancel: closeForm,
onChange: updateDraft,
onSubmit: saveAccount,
})(injectIntl(EditableField));

View File

@@ -1,31 +1,26 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { FormattedMessage } from 'react-intl';
import { injectIntl, intlShape } from '@edx/frontend-i18n'; // eslint-disable-line
import { Button, StatefulButton } from '@edx/paragon';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-i18n';
import { Button, StatefulButton, Input, ValidationFormGroup } from '@edx/paragon';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { faExclamationTriangle, faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import Input from './temp/Input';
import ValidationFormGroup from './temp/ValidationFormGroup';
import SwitchContent from './temp/SwitchContent';
import Alert from './Alert';
import messages from '../AccountSettingsPage.messages';
import { Alert, SwitchContent } from '../common';
import messages from './AccountSettingsPage.messages';
import {
openForm,
closeForm,
updateDraft,
saveAccount,
} from '../actions';
import { editableFieldSelector } from '../selectors';
} from './actions';
import { editableFieldSelector } from './selectors';
function EmailField(props) {
const {
name,
label,
emptyLabel,
value,
saveState,
error,
@@ -66,7 +61,7 @@ function EmailField(props) {
className="alert-warning mt-n2"
icon={<FontAwesomeIcon className="mr-2 h6" icon={faExclamationTriangle} />}
>
<h6>
<h6 aria-level="3">
{intl.formatMessage(messages['account.settings.email.field.confirmation.header'])}
</h6>
{intl.formatMessage(confirmationMessageDefinition, { value: confirmationValue })}
@@ -87,6 +82,18 @@ function EmailField(props) {
</span>
);
const renderEmptyLabel = () => {
if (isEditable) {
return <Button onClick={handleEdit} className="btn-link p-0">{emptyLabel}</Button>;
}
return <span className="text-muted">{emptyLabel}</span>;
};
const renderValue = () => {
if (confirmationValue) return renderConfirmationValue();
return value || renderEmptyLabel();
};
return (
<SwitchContent
expression={isEditing ? 'editing' : 'default'}
@@ -139,15 +146,16 @@ function EmailField(props) {
),
default: (
<div className="form-group">
<div className="d-flex justify-content-between align-items-start">
<h6>{label}</h6>
<div className="d-flex align-items-start">
<h6 aria-level="3">{label}</h6>
{isEditable ? (
<Button onClick={handleEdit} className="btn-link">
<Button onClick={handleEdit} className="ml-3 btn-link">
<FontAwesomeIcon className="mr-1" icon={faPencilAlt} />
{intl.formatMessage(messages['account.settings.editable.field.action.edit'])}
</Button>
) : null}
</div>
<p>{confirmationValue ? renderConfirmationValue() : value}</p>
<p>{renderValue()}</p>
{renderConfirmationMessage() || <p className="small text-muted mt-n2">{helpText}</p>}
</div>
),
@@ -160,6 +168,7 @@ function EmailField(props) {
EmailField.propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
emptyLabel: PropTypes.node,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
saveState: PropTypes.oneOf(['default', 'pending', 'complete', 'error']),
error: PropTypes.string,
@@ -183,6 +192,7 @@ EmailField.defaultProps = {
value: undefined,
saveState: undefined,
label: undefined,
emptyLabel: undefined,
error: undefined,
confirmationMessageDefinition: undefined,
confirmationValue: undefined,
@@ -195,6 +205,4 @@ EmailField.defaultProps = {
export default connect(editableFieldSelector, {
onEdit: openForm,
onCancel: closeForm,
onChange: updateDraft,
onSubmit: saveAccount,
})(injectIntl(EmailField));

View File

@@ -0,0 +1,65 @@
import React from 'react';
import { injectIntl, intlShape } from '@edx/frontend-i18n'; // eslint-disable-line
import { NavHashLink } from 'react-router-hash-link';
import Scrollspy from 'react-scrollspy';
import messages from './AccountSettingsPage.messages';
function JumpNav({ intl }) {
return (
<div className="jump-nav">
<Scrollspy
items={[
'basic-information',
'profile-information',
'social-media',
'site-preferences',
'linked-accounts',
'delete-account',
]}
className="list-unstyled"
currentClassName="font-weight-bold"
>
<li>
<NavHashLink to="#basic-information">
{intl.formatMessage(messages['account.settings.section.account.information'])}
</NavHashLink>
</li>
<li>
<NavHashLink to="#profile-information">
{intl.formatMessage(messages['account.settings.section.profile.information'])}
</NavHashLink>
</li>
<li>
<NavHashLink to="#social-media">
{intl.formatMessage(messages['account.settings.section.social.media'])}
</NavHashLink>
</li>
<li>
<NavHashLink to="#site-preferences">
{intl.formatMessage(messages['account.settings.section.site.preferences'])}
</NavHashLink>
</li>
<li>
<NavHashLink to="#linked-accounts">
{intl.formatMessage(messages['account.settings.section.linked.accounts'])}
</NavHashLink>
</li>
<li>
<NavHashLink to="#delete-account">
{intl.formatMessage(messages['account.settings.jump.nav.delete.account'])}
</NavHashLink>
</li>
</Scrollspy>
</div>
);
}
JumpNav.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(JumpNav);

View File

@@ -11,4 +11,25 @@
padding: 0;
display: inline-block;
}
.jump-nav {
@media (min-width: map-get($grid-breakpoints, "sm")) {
padding-top: 1rem;
position: sticky;
top: 1rem;
}
li {
margin-bottom: .5rem;
}
}
.section-heading {
@extend .h4;
margin-bottom: map-get($spacers, 3);
}
.account-section {
// These properties together will shift the hashlink position
margin-bottom: map-get($spacers, 5);
padding-top: 1rem;
}
}

View File

@@ -2,39 +2,52 @@ import { utils } from '../common';
const { AsyncActionType } = utils;
export const FETCH_ACCOUNT = new AsyncActionType('ACCOUNT_SETTINGS', 'FETCH_ACCOUNT');
export const SAVE_ACCOUNT = new AsyncActionType('ACCOUNT_SETTINGS', 'SAVE_ACCOUNT');
export const FETCH_THIRD_PARTY_AUTH_PROVIDERS = new AsyncActionType('ACCOUNT_SETTINGS', 'FETCH_THIRD_PARTY_AUTH_PROVIDERS');
export const RESET_PASSWORD = new AsyncActionType('ACCOUNT_SETTINGS', 'RESET_PASSWORD');
export const FETCH_SETTINGS = new AsyncActionType('ACCOUNT_SETTINGS', 'FETCH_SETTINGS');
export const SAVE_SETTINGS = new AsyncActionType('ACCOUNT_SETTINGS', 'SAVE_SETTINGS');
export const FETCH_TIME_ZONES = new AsyncActionType('ACCOUNT_SETTINGS', 'FETCH_TIME_ZONES');
export const SAVE_PREVIOUS_SITE_LANGUAGE = 'SAVE_PREVIOUS_SITE_LANGUAGE';
export const OPEN_FORM = 'OPEN_FORM';
export const CLOSE_FORM = 'CLOSE_FORM';
export const UPDATE_DRAFT = 'UPDATE_DRAFT';
export const RESET_DRAFTS = 'RESET_DRAFTS';
// FETCH EXAMPLE ACTIONS
// FETCH SETTINGS ACTIONS
export const fetchAccount = () => ({
type: FETCH_ACCOUNT.BASE,
export const fetchSettings = () => ({
type: FETCH_SETTINGS.BASE,
});
export const fetchAccountBegin = () => ({
type: FETCH_ACCOUNT.BEGIN,
export const fetchSettingsBegin = () => ({
type: FETCH_SETTINGS.BEGIN,
});
export const fetchAccountSuccess = values => ({
type: FETCH_ACCOUNT.SUCCESS,
payload: { values },
export const fetchSettingsSuccess = ({
values,
thirdPartyAuthProviders,
profileDataManager,
timeZones,
}) => ({
type: FETCH_SETTINGS.SUCCESS,
payload: {
values,
thirdPartyAuthProviders,
profileDataManager,
timeZones,
},
});
export const fetchAccountFailure = error => ({
type: FETCH_ACCOUNT.FAILURE,
export const fetchSettingsFailure = error => ({
type: FETCH_SETTINGS.FAILURE,
payload: { error },
});
export const fetchAccountReset = () => ({
type: FETCH_ACCOUNT.RESET,
export const fetchSettingsReset = () => ({
type: FETCH_SETTINGS.RESET,
});
// FORM STATE ACTIONS
export const openForm = formId => ({
type: OPEN_FORM,
payload: { formId },
@@ -45,9 +58,6 @@ export const closeForm = formId => ({
payload: { formId },
});
// FORM STATE ACTIONS
export const updateDraft = (name, value) => ({
type: UPDATE_DRAFT,
payload: {
@@ -60,63 +70,45 @@ export const resetDrafts = () => ({
type: RESET_DRAFTS,
});
// SAVE PROFILE ACTIONS
export const saveAccount = (formId, commitValues) => ({
type: SAVE_ACCOUNT.BASE,
// SAVE SETTINGS ACTIONS
export const saveSettings = (formId, commitValues) => ({
type: SAVE_SETTINGS.BASE,
payload: { formId, commitValues },
});
export const saveAccountBegin = () => ({
type: SAVE_ACCOUNT.BEGIN,
export const saveSettingsBegin = () => ({
type: SAVE_SETTINGS.BEGIN,
});
export const saveAccountSuccess = (values, confirmationValues) => ({
type: SAVE_ACCOUNT.SUCCESS,
export const saveSettingsSuccess = (values, confirmationValues) => ({
type: SAVE_SETTINGS.SUCCESS,
payload: { values, confirmationValues },
});
export const saveAccountReset = () => ({
type: SAVE_ACCOUNT.RESET,
export const saveSettingsReset = () => ({
type: SAVE_SETTINGS.RESET,
});
export const saveAccountFailure = ({ fieldErrors, message }) => ({
type: SAVE_ACCOUNT.FAILURE,
export const saveSettingsFailure = ({ fieldErrors, message }) => ({
type: SAVE_SETTINGS.FAILURE,
payload: { errors: fieldErrors, message },
});
// SAVE PROFILE ACTIONS
export const resetPassword = () => ({
type: RESET_PASSWORD.BASE,
export const savePreviousSiteLanguage = previousSiteLanguage => ({
type: SAVE_PREVIOUS_SITE_LANGUAGE,
payload: { previousSiteLanguage },
});
export const resetPasswordBegin = () => ({
type: RESET_PASSWORD.BEGIN,
// FETCH TIME_ZONE ACTIONS
export const fetchTimeZones = country => ({
type: FETCH_TIME_ZONES.BASE,
payload: { country },
});
export const resetPasswordSuccess = () => ({
type: RESET_PASSWORD.SUCCESS,
export const fetchTimeZonesSuccess = timeZones => ({
type: FETCH_TIME_ZONES.SUCCESS,
payload: { timeZones },
});
export const resetPasswordReset = () => ({
type: RESET_PASSWORD.RESET,
});
// fetch third party auth providers
export const fetchThirdPartyAuthProviders = () => ({
type: FETCH_THIRD_PARTY_AUTH_PROVIDERS.BASE,
});
export const fetchThirdPartyAuthProvidersBegin = () => ({
type: FETCH_THIRD_PARTY_AUTH_PROVIDERS.BEGIN,
});
export const fetchThirdPartyAuthProvidersSuccess = providers => ({
type: FETCH_THIRD_PARTY_AUTH_PROVIDERS.SUCCESS, payload: { providers },
});
export const fetchThirdPartyAuthProvidersFailure = error => ({
type: FETCH_THIRD_PARTY_AUTH_PROVIDERS.FAILURE, payload: { error },
});

View File

@@ -1,84 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, intlShape } from '@edx/frontend-i18n'; // eslint-disable-line
import { FormattedMessage } from 'react-intl';
import { StatefulButton, Hyperlink } from '@edx/paragon';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { resetPassword } from '../actions';
import { resetPasswordSelector } from '../selectors';
import messages from '../AccountSettingsPage.messages';
import Alert from './Alert';
function PasswordReset({ email, intl, ...props }) {
const renderConfirmationMessage = () => (
<Alert
className="alert-warning mt-n2"
icon={<FontAwesomeIcon className="mr-2" icon={faExclamationTriangle} />}
>
<FormattedMessage
id="account.settings.editable.field.password.reset.button"
defaultMessage="We've sent a message to {email}. Click the link in the message to reset your password. Didn't receive the message? Contact {technicalSupportLink}."
description="The password reset button in account settings"
values={{
email,
technicalSupportLink: (
<Hyperlink
destination="https://support.edx.org/hc/en-us/articles/206212088-What-if-I-did-not-receive-a-password-reset-message-"
>
<FormattedMessage
id="account.settings.editable.field.password.reset.button.support.link"
defaultMessage="technical support"
description="link text used in message: account.settings.editable.field.password.reset.button 'Contact technical support.'"
/>
</Hyperlink>
),
}}
/>
</Alert>
);
return (
<div className="form-group">
<h6>
<FormattedMessage
id="account.settings.editable.field.password.reset.label"
defaultMessage="Password"
description="The password label in account settings"
/>
</h6>
<p>
<StatefulButton
className="btn-link"
state={props.resetPasswordState}
onClick={props.resetPassword}
disabledStates={[]}
labels={{
default: intl.formatMessage(messages['account.settings.editable.field.password.reset.button']),
}}
/>
</p>
{props.resetPasswordState === 'complete' ? renderConfirmationMessage() : null}
</div>
);
}
PasswordReset.propTypes = {
resetPassword: PropTypes.func.isRequired,
email: PropTypes.string,
resetPasswordState: PropTypes.string,
intl: intlShape.isRequired,
};
PasswordReset.defaultProps = {
email: '',
resetPasswordState: undefined,
};
export default connect(resetPasswordSelector, {
resetPassword,
})(injectIntl(PasswordReset));

View File

@@ -1,151 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { FormattedMessage } from 'react-intl';
import { Hyperlink } from '@edx/paragon';
import { fetchThirdPartyAuthProviders } from '../actions';
import { thirdPartyAuthSelector } from '../selectors';
class ThirdPartyAuth extends React.Component {
componentDidMount() {
this.props.fetchThirdPartyAuthProviders();
}
renderConnectedProvider(url, name) {
return (
<React.Fragment>
<h6>{name}</h6>
<Hyperlink destination={url} className="btn btn-outline-primary">
<FormattedMessage
id="account.settings.sso.link.account"
defaultMessage="Sign in with {name}"
description="An action link to link a connected third party account.m {name} will be Google, Facebook, etc."
values={{ name }}
/>
</Hyperlink>
</React.Fragment>
);
}
renderUnconnectedProvider(url, name) {
return (
<React.Fragment>
<h6>
{name}
<span className="small font-weight-normal text-muted ml-2">
<FormattedMessage
id="account.settings.sso.account.connected"
defaultMessage="Linked"
description="A badge to show that a third party account is linked"
/>
</span>
</h6>
<Hyperlink destination={url}>
<FormattedMessage
id="account.settings.sso.unlink.account"
defaultMessage="Unlink {name} account"
description="An action link to unlink a connected third party account"
values={{ name }}
/>
</Hyperlink>
</React.Fragment>
);
}
renderProvider({
name, disconnectUrl, connectUrl, connected, id,
}) {
return (
<div className="form-group" key={id}>
{
connected ?
this.renderUnconnectedProvider(disconnectUrl, name) :
this.renderConnectedProvider(connectUrl, name)
}
</div>
);
}
renderNoProviders() {
return (
<FormattedMessage
id="account.settings.sso.no.providers"
defaultMessage="No accounts can be linked at this time."
description="Displayed when no third party accounts are available to link an edX account to"
/>
);
}
renderLoading() {
return (
<FormattedMessage
id="account.settings.sso.loading"
defaultMessage="Loading..."
description="Waiting for data to load in the third party auth provider list"
/>
);
}
renderLoadingError() {
return (
<FormattedMessage
id="account.settings.sso.loading.error"
defaultMessage="There was a problem loading linked accounts."
description="Error message for failing to load the third party auth provider list"
/>
);
}
render() {
return (
<div>
<h2>
<FormattedMessage
id="account.settings.sso.section.header"
defaultMessage="Linked Accounts"
description="Section header for the third party auth settings"
/>
</h2>
<p>
<FormattedMessage
id="account.settings.sso.section.subheader"
defaultMessage="You can link your identity accounts to simplify signing in to edX."
description="Section subheader for the third party auth settings"
/>
</p>
{this.props.providers.map(this.renderProvider, this)}
{this.props.loaded && this.props.providers.length === 0 ? this.renderNoProviders() : null}
{this.props.loading ? this.renderLoading() : null}
{this.props.loadingError ? this.renderLoadingError() : null}
</div>
);
}
}
ThirdPartyAuth.propTypes = {
fetchThirdPartyAuthProviders: PropTypes.func.isRequired,
providers: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string,
disconnectUrl: PropTypes.string,
connectUrl: PropTypes.string,
connected: PropTypes.bool,
id: PropTypes.string,
})),
loading: PropTypes.bool,
loaded: PropTypes.bool,
loadingError: PropTypes.string,
};
ThirdPartyAuth.defaultProps = {
providers: [],
loading: false,
loaded: false,
loadingError: undefined,
};
export default connect(thirdPartyAuthSelector, {
fetchThirdPartyAuthProviders,
})(ThirdPartyAuth);

View File

@@ -1,106 +0,0 @@
import React from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
class Input extends React.Component {
componentDidMount() {
if (process.env.NODE_ENV === 'development') {
this.checkHasLabel();
}
}
getHTMLTagForType() {
const { type } = this.props;
if (type === 'select' || type === 'textarea') return type;
return 'input';
}
getClassNameForType() {
switch (this.props.type) {
case 'file':
return 'form-control-file';
case 'checkbox':
case 'radio':
return 'form-check-input';
default:
return 'form-control';
}
}
getRef(forwardedRef) {
if (process.env.NODE_ENV !== 'development') return forwardedRef;
if (forwardedRef) return forwardedRef;
if (!this.innerRef) this.innerRef = React.createRef();
return this.innerRef;
}
checkHasLabel() {
const htmlNode = this.getRef().current;
if (htmlNode.labels.length > 0) return;
if (htmlNode.getAttribute('aria-label') !== null) return;
// eslint-disable-next-line no-console
if (console) console.warn('Input[a11y]: There is no associated label for this Input');
}
renderOptions(options) {
return options.map((option) => {
const {
value, label, group, ...attributes
} = option;
if (group) {
return (
<optgroup key={`optgroup-${label}`} label={label} {...attributes}>
{this.renderOptions(group)}
</optgroup>
);
}
return <option key={value} value={value} {...attributes}>{label}</option>;
}, this);
}
render() {
const {
type, className, options, innerRef, ...attributes // eslint-disable-line react/prop-types
} = this.props;
const htmlTag = this.getHTMLTagForType();
const htmlProps = {
className: classNames(this.getClassNameForType(), className),
type: htmlTag === 'input' ? type : undefined,
...attributes,
ref: this.getRef(innerRef),
};
const htmlChildren = type === 'select' ? this.renderOptions(options) : null;
return React.createElement(htmlTag, htmlProps, htmlChildren);
}
}
Input.propTypes = {
type: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
className: PropTypes.string,
options: PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
disabled: PropTypes.bool,
group: PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
disabled: PropTypes.bool,
})),
})),
};
Input.defaultProps = {
className: undefined,
options: [],
};
// eslint-disable-next-line react/no-multi-comp
export default React.forwardRef((props, ref) => <Input innerRef={ref} {...props} />);

View File

@@ -1,92 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import Input from './Input';
const propTypes = {
for: PropTypes.string.isRequired,
className: PropTypes.string,
invalid: PropTypes.bool,
valid: PropTypes.bool,
validMessage: PropTypes.node,
invalidMessage: PropTypes.node,
helpText: PropTypes.node,
children: PropTypes.node,
};
const defaultProps = {
invalid: undefined,
valid: undefined,
validMessage: undefined,
invalidMessage: undefined,
helpText: undefined,
children: undefined,
className: undefined,
};
function ValidationFormGroup(props) {
const {
className,
invalidMessage,
invalid,
valid,
validMessage,
helpText,
for: id,
children,
} = props;
const renderChildren = () => React.Children.map(children, (child) => {
// Any non-user input element should pass through unmodified
if (['input', 'textarea', 'select', Input].indexOf(child.type) === -1) return child;
// Add validation class names and describedby values to input element
return React.cloneElement(child, {
className: classNames(child.props.className, {
'is-invalid': invalid,
'is-valid': valid,
}),
// This is a non-standard use of the classNames package, but it's exactly the same use case.
'aria-describedby': classNames(child.props['aria-describedby'], {
[`${id}-help-text`]: Boolean(helpText),
[`${id}-invalid-feedback`]: invalid && invalidMessage,
[`${id}-valid-feedback`]: valid && validMessage,
}),
});
});
const renderHelpText = (text) => {
if (!text) return null;
return <small id={`${id}-help-text`} className="form-text text-muted">{text}</small>;
};
const renderFeedback = (message, state) => {
if (!message) return null;
return (
<div
className={`${state}-feedback`}
id={`${id}-${state}-feedback`}
>
{message}
</div>
);
};
return (
<div className={classNames('form-group', className)}>
{renderChildren()}
{renderHelpText(helpText)}
{renderFeedback(invalidMessage, 'invalid')}
{renderFeedback(validMessage, 'valid')}
</div>
);
}
ValidationFormGroup.propTypes = propTypes;
ValidationFormGroup.defaultProps = defaultProps;
export default ValidationFormGroup;

View File

@@ -12,7 +12,7 @@ export const YEAR_OF_BIRTH_OPTIONS = (() => {
})();
export const EDUCATION_LEVELS = [
null,
'',
'p',
'm',
'b',
@@ -25,8 +25,10 @@ export const EDUCATION_LEVELS = [
];
export const GENDER_OPTIONS = [
null,
'',
'f',
'm',
'o',
];
export const TRANSIFEX_LANGUAGE_BASE_URL = 'https://www.transifex.com/open-edx/edx-platform/language/';

View File

@@ -0,0 +1,44 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-i18n';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Hyperlink } from '@edx/paragon';
// Messages
import messages from './messages';
// Components
import { Alert } from '../../common';
const BeforeProceedingBanner = (props) => {
const { instructionMessageId, intl, supportUrl } = props;
return (
<Alert
className="alert-warning mt-n2"
icon={<FontAwesomeIcon className="mr-2" icon={faExclamationTriangle} />}
>
<FormattedMessage
id="account.settings.delete.account.before.proceeding"
defaultMessage="Before proceeding, please {actionLink}."
description="Error that appears if you are trying to delete your edX account, but something about your account needs attention first. The actionLink will be instructions, such as 'unlink your Facebook account'."
values={{
actionLink: (
<Hyperlink destination={supportUrl}>
{intl.formatMessage(messages[instructionMessageId])}
</Hyperlink>
),
}}
/>
</Alert>
);
};
BeforeProceedingBanner.propTypes = {
instructionMessageId: PropTypes.string.isRequired,
intl: intlShape.isRequired,
supportUrl: PropTypes.string.isRequired,
};
export default injectIntl(BeforeProceedingBanner);

View File

@@ -0,0 +1,129 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Button, Input, Modal, ValidationFormGroup } from '@edx/paragon';
import { injectIntl, intlShape } from '@edx/frontend-i18n';
import { faExclamationCircle, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import messages from './messages';
import { Alert } from '../../common';
import PrintingInstructions from './PrintingInstructions';
export class ConfirmationModal extends Component {
/**
* @returns String The message id for a short description of the error, suitable for a header or
* as the error message under an input field.
*/
getShortErrorMessageId(reason) {
switch (reason) {
case 'empty-password':
return 'account.settings.delete.account.error.no.password';
default:
return 'account.settings.delete.account.error.unable.to.delete';
}
}
renderError(reason) {
const { errorType, intl } = this.props;
if (errorType === null) {
return null;
}
const headerMessageId = this.getShortErrorMessageId(errorType);
const detailsMessageId =
reason === 'empty-password'
? null
: 'account.settings.delete.account.error.unable.to.delete.details';
return (
<Alert
className="alert-danger mt-n2"
icon={<FontAwesomeIcon className="mr-2" icon={faExclamationCircle} />}
>
<h6>{intl.formatMessage(messages[headerMessageId])}</h6>
{detailsMessageId ? (
<p className="text-danger">{intl.formatMessage(messages[detailsMessageId])}</p>
) : null}
</Alert>
);
}
render() {
const {
open,
errorType,
intl,
onCancel,
onChange,
onSubmit,
password,
} = this.props;
const passwordFieldId = 'passwordFieldId';
const invalidMessage = messages[this.getShortErrorMessageId(errorType)];
return (
<Modal
open={open}
title={intl.formatMessage(messages['account.settings.delete.account.modal.header'])}
body={
<div>
{this.renderError()}
<Alert
className="alert-warning mt-n2"
icon={<FontAwesomeIcon className="mr-2" icon={faExclamationTriangle} />}
>
<h6>
{intl.formatMessage(messages['account.settings.delete.account.modal.text.1'])}
</h6>
<p>{intl.formatMessage(messages['account.settings.delete.account.modal.text.2'])}</p>
<p>
<PrintingInstructions />
</p>
</Alert>
<ValidationFormGroup
for={passwordFieldId}
invalid={errorType !== null}
invalidMessage={intl.formatMessage(invalidMessage)}
>
<label className="d-block" htmlFor={passwordFieldId}>
{intl.formatMessage(messages['account.settings.delete.account.modal.enter.password'])}
</label>
<Input
name="password"
id={passwordFieldId}
type="password"
value={password}
onChange={onChange}
/>
</ValidationFormGroup>
</div>
}
buttons={[
<Button className="btn-danger" onClick={onSubmit}>
{intl.formatMessage(messages['account.settings.delete.account.modal.confirm.delete'])}
</Button>,
]}
closeText={intl.formatMessage(messages['account.settings.delete.account.modal.confirm.cancel'])}
renderHeaderCloseButton={false}
onClose={onCancel}
/>
);
}
}
ConfirmationModal.propTypes = {
open: PropTypes.bool,
errorType: PropTypes.oneOf(['empty-password', 'server']),
intl: intlShape.isRequired,
onCancel: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
password: PropTypes.string.isRequired,
};
ConfirmationModal.defaultProps = {
open: false,
errorType: null,
};
export default injectIntl(ConfirmationModal);

View File

@@ -0,0 +1,71 @@
import React from 'react';
import ReactDOM from 'react-dom';
import renderer from 'react-test-renderer';
import { IntlProvider, injectIntl } from '@edx/frontend-i18n';
// Modal creates a portal. Overriding ReactDOM.createPortal allows portals to be tested in jest.
ReactDOM.createPortal = node => node;
import { ConfirmationModal } from './ConfirmationModal'; // eslint-disable-line import/first
const IntlConfirmationModal = injectIntl(ConfirmationModal);
describe('ConfirmationModal', () => {
let props = {};
beforeEach(() => {
props = {
onCancel: jest.fn(),
onChange: jest.fn(),
onSubmit: jest.fn(),
open: false,
errorType: null,
password: 'fluffy bunnies',
logoutUrl: 'http://localhost/logout',
};
});
it('should match default closed confirmation modal snapshot', () => {
const tree = renderer
.create((
<IntlProvider locale="en">
<IntlConfirmationModal
{...props}
/>
</IntlProvider>
))
.toJSON();
expect(tree).toMatchSnapshot();
});
it('should match open confirmation modal snapshot', () => {
const tree = renderer
.create((
<IntlProvider locale="en">
<IntlConfirmationModal
{...props}
// This will cause 'modal-backdrop' and 'show' to appear on the modal as CSS classes.
open
/>
</IntlProvider>
))
.toJSON();
expect(tree).toMatchSnapshot();
});
it('should match empty password confirmation modal snapshot', () => {
const tree = renderer
.create((
<IntlProvider locale="en">
<IntlConfirmationModal
{...props}
errorType="empty-password"
// This will cause 'modal-backdrop' and 'show' to appear on the modal as CSS classes.
open
/>
</IntlProvider>
))
.toJSON();
expect(tree).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,135 @@
import React, { useState, useCallback } from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-i18n';
import { Button, Hyperlink } from '@edx/paragon';
// Messages
import messages from './messages';
// Components
import ConnectedConfirmationModal from './ConfirmationModal';
import PrintingInstructions from './PrintingInstructions';
import ConnectedSuccessModal from './SuccessModal';
import BeforeProceedingBanner from './BeforeProceedingBanner';
import { postDeleteAccount } from './data/service';
import useAction from '../../common/hooks';
function DeleteAccount(props) {
const {
logoutUrl, hasLinkedTPA, isVerifiedAccount, intl,
} = props;
const [password, setPassword] = useState('');
const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false);
const [validationError, setValidationError] = useState(null);
const [
deleteAccountState,
performDeleteAccount,
resetDeleteAccount,
] = useAction(postDeleteAccount);
const canDelete = isVerifiedAccount && !hasLinkedTPA;
const successDialogOpen = deleteAccountState.loaded;
const error = deleteAccountState.error !== null ? 'server' : validationError;
// Event handlers
const handleDeleteAccountClick = useCallback(() => {
if (canDelete) {
setConfirmationDialogOpen(true);
}
});
const handleSubmit = useCallback(() => {
if (password === '') {
setValidationError('empty-password');
} else {
performDeleteAccount(password);
}
});
const handleCancel = useCallback(() => {
setPassword('');
setConfirmationDialogOpen(false);
resetDeleteAccount();
});
const handlePasswordChange = useCallback((e) => {
setPassword(e.target.value.trim());
setValidationError(null);
});
const handleFinalClose = useCallback(() => {
global.location = logoutUrl;
});
return (
<div>
<h2 className="section-heading">
{intl.formatMessage(messages['account.settings.delete.account.header'])}
</h2>
<p>{intl.formatMessage(messages['account.settings.delete.account.subheader'])}</p>
<p>{intl.formatMessage(messages['account.settings.delete.account.text.1'])}</p>
<p>{intl.formatMessage(messages['account.settings.delete.account.text.2'])}</p>
<p>
<PrintingInstructions />
</p>
<p className="text-danger h6">
{intl.formatMessage(messages['account.settings.delete.account.text.warning'])}
</p>
<p>
<Hyperlink destination="https://support.edx.org/hc/en-us/sections/115004139268-Manage-Your-Account-Settings">
{intl.formatMessage(messages['account.settings.delete.account.text.change.instead'])}
</Hyperlink>
</p>
<p>
<Button
className="btn-outline-danger"
onClick={handleDeleteAccountClick}
disabled={!canDelete}
>
{intl.formatMessage(messages['account.settings.delete.account.button'])}
</Button>
</p>
{isVerifiedAccount ? null : (
<BeforeProceedingBanner
instructionMessageId="account.settings.delete.account.please.activate"
supportUrl="https://support.edx.org/hc/en-us/articles/115000940568-How-do-I-activate-my-account-"
/>
)}
{hasLinkedTPA ? (
<BeforeProceedingBanner
instructionMessageId="account.settings.delete.account.please.unlink"
supportUrl="https://support.edx.org/hc/en-us/articles/207206067"
/>
) : null}
<ConnectedConfirmationModal
open={confirmationDialogOpen}
errorType={error}
onSubmit={handleSubmit}
onCancel={handleCancel}
onChange={handlePasswordChange}
password={password}
/>
<ConnectedSuccessModal open={successDialogOpen} onClose={handleFinalClose} />
</div>
);
}
DeleteAccount.propTypes = {
hasLinkedTPA: PropTypes.bool,
isVerifiedAccount: PropTypes.bool,
logoutUrl: PropTypes.string.isRequired,
intl: intlShape.isRequired,
};
DeleteAccount.defaultProps = {
hasLinkedTPA: false,
isVerifiedAccount: true,
};
export default injectIntl(DeleteAccount);

View File

@@ -0,0 +1,62 @@
import React from 'react';
import renderer from 'react-test-renderer';
import { IntlProvider } from '@edx/frontend-i18n';
// Testing the modals separately, they just clutter up the snapshots if included here.
jest.mock('./ConfirmationModal');
jest.mock('./SuccessModal');
import DeleteAccount from './DeleteAccount'; // eslint-disable-line import/first
describe('DeleteAccount', () => {
let props = {};
beforeEach(() => {
props = {
hasLinkedTPA: false,
isVerifiedAccount: true,
logoutUrl: 'http://localhost/logout',
};
});
it('should match default section snapshot', () => {
const tree = renderer
.create((
<IntlProvider locale="en">
<DeleteAccount
{...props}
/>
</IntlProvider>
))
.toJSON();
expect(tree).toMatchSnapshot();
});
it('should match unverified account section snapshot', () => {
const tree = renderer
.create((
<IntlProvider locale="en">
<DeleteAccount
{...props}
isVerifiedAccount={false}
/>
</IntlProvider>
))
.toJSON();
expect(tree).toMatchSnapshot();
});
it('should match unverified account section snapshot', () => {
const tree = renderer
.create((
<IntlProvider locale="en">
<DeleteAccount
{...props}
hasLinkedTPA
/>
</IntlProvider>
))
.toJSON();
expect(tree).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,30 @@
import React from 'react';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-i18n';
import { Hyperlink } from '@edx/paragon';
import messages from './messages';
const PrintingInstructions = (props) => {
const actionLink = (
<Hyperlink
destination="https://edx.readthedocs.io/projects/edx-guide-for-students/en/latest/SFD_certificates.html#printing-a-certificate"
>
{props.intl.formatMessage(messages['account.settings.delete.account.text.3.link'])}
</Hyperlink>
);
return (
<FormattedMessage
id="account.settings.delete.account.text.3"
defaultMessage="You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion, {actionLink}."
description="A message in the user account deletion area"
values={{ actionLink }}
/>
);
};
PrintingInstructions.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(PrintingInstructions);

View File

@@ -0,0 +1,38 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-i18n';
import { Modal } from '@edx/paragon';
import messages from './messages';
export const SuccessModal = (props) => {
const { open, intl, onClose } = props;
return (
<Modal
open={open}
title={intl.formatMessage(messages['account.settings.delete.account.modal.after.header'])}
body={
<div>
<p className="h6">
{intl.formatMessage(messages['account.settings.delete.account.modal.after.text'])}
</p>
</div>
}
closeText={intl.formatMessage(messages['account.settings.delete.account.modal.after.button'])}
renderHeaderCloseButton={false}
onClose={onClose}
/>
);
};
SuccessModal.propTypes = {
open: PropTypes.bool,
intl: intlShape.isRequired,
onClose: PropTypes.func.isRequired,
};
SuccessModal.defaultProps = {
open: false,
};
export default injectIntl(SuccessModal);

View File

@@ -0,0 +1,44 @@
import React from 'react';
import ReactDOM from 'react-dom';
import renderer from 'react-test-renderer';
import { IntlProvider, injectIntl } from '@edx/frontend-i18n';
// Modal creates a portal. Overriding ReactDOM.createPortal allows portals to be tested in jest.
ReactDOM.createPortal = node => node;
import { SuccessModal } from './SuccessModal'; // eslint-disable-line import/first
const IntlSuccessModal = injectIntl(SuccessModal);
describe('SuccessModal', () => {
let props = {};
beforeEach(() => {
props = {
onClose: jest.fn(),
open: false,
};
});
it('should match default closed success modal snapshot', () => {
const tree = renderer.create((
<IntlProvider locale="en"><IntlSuccessModal {...props} /></IntlProvider>))
.toJSON();
expect(tree).toMatchSnapshot();
});
it('should match open success modal snapshot', () => {
const tree = renderer
.create((
<IntlProvider locale="en">
<IntlSuccessModal
{...props}
// This will cause 'modal-backdrop' and 'show' to appear on the modal as CSS classes.
open
/>
</IntlProvider>
))
.toJSON();
expect(tree).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,436 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ConfirmationModal should match default closed confirmation modal snapshot 1`] = `
<div>
<div
className="fade"
role="presentation"
/>
<div
className="modal js-close-modal-on-click fade"
onClick={[Function]}
role="presentation"
>
<div
aria-labelledby="id3"
aria-modal={true}
className=""
role="dialog"
tabIndex="-1"
>
<div
className="modal-content"
>
<div
className="modal-header"
>
<h2
className="modal-title"
id="id3"
>
Are you sure?
</h2>
</div>
<div
className="modal-body"
>
<div>
<div
className="alert d-flex align-items-start alert-warning mt-n2"
>
<div>
<svg
aria-hidden="true"
className="svg-inline--fa fa-exclamation-triangle fa-w-18 mr-2"
data-icon="exclamation-triangle"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
fill="currentColor"
style={Object {}}
/>
</svg>
</div>
<div>
<h6>
You have selected "Delete My Account". Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.
</h6>
<p>
If you proceed, you will be unable to use this account to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employer's or university's system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.
</p>
<p>
<span>
You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion,
<a
href="https://edx.readthedocs.io/projects/edx-guide-for-students/en/latest/SFD_certificates.html#printing-a-certificate"
onClick={[Function]}
target="_self"
>
follow the instructions for printing or downloading a certificate
</a>
.
</span>
</p>
</div>
</div>
<div
className="form-group"
>
<label
className="d-block"
htmlFor="passwordFieldId"
>
If you still wish to continue and delete your account, please enter your account password:
</label>
<input
aria-describedby=""
className="form-control"
id="passwordFieldId"
name="password"
onChange={[MockFunction]}
type="password"
value="fluffy bunnies"
/>
<strong
className="invalid-feedback"
id="passwordFieldId-invalid-feedback"
>
Unable to delete account
</strong>
</div>
</div>
</div>
<div
className="modal-footer"
>
<button
className="btn btn-danger"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Yes, Delete
</button>
<button
className="btn js-close-modal-on-click btn-secondary"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Cancel
</button>
</div>
</div>
</div>
</div>
</div>
`;
exports[`ConfirmationModal should match empty password confirmation modal snapshot 1`] = `
<div>
<div
className="modal-backdrop show"
role="presentation"
/>
<div
className="modal js-close-modal-on-click show d-block"
onClick={[Function]}
role="presentation"
>
<div
aria-labelledby="id5"
aria-modal={true}
className="modal-dialog"
role="dialog"
tabIndex="-1"
>
<div
className="modal-content"
>
<div
className="modal-header"
>
<h2
className="modal-title"
id="id5"
>
Are you sure?
</h2>
</div>
<div
className="modal-body"
>
<div>
<div
className="alert d-flex align-items-start alert-danger mt-n2"
>
<div>
<svg
aria-hidden="true"
className="svg-inline--fa fa-exclamation-circle fa-w-16 mr-2"
data-icon="exclamation-circle"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
fill="currentColor"
style={Object {}}
/>
</svg>
</div>
<div>
<h6>
A password is required
</h6>
<p
className="text-danger"
>
Sorry, there was an error trying to process your request. Please try again later.
</p>
</div>
</div>
<div
className="alert d-flex align-items-start alert-warning mt-n2"
>
<div>
<svg
aria-hidden="true"
className="svg-inline--fa fa-exclamation-triangle fa-w-18 mr-2"
data-icon="exclamation-triangle"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
fill="currentColor"
style={Object {}}
/>
</svg>
</div>
<div>
<h6>
You have selected "Delete My Account". Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.
</h6>
<p>
If you proceed, you will be unable to use this account to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employer's or university's system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.
</p>
<p>
<span>
You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion,
<a
href="https://edx.readthedocs.io/projects/edx-guide-for-students/en/latest/SFD_certificates.html#printing-a-certificate"
onClick={[Function]}
target="_self"
>
follow the instructions for printing or downloading a certificate
</a>
.
</span>
</p>
</div>
</div>
<div
className="form-group"
>
<label
className="d-block"
htmlFor="passwordFieldId"
>
If you still wish to continue and delete your account, please enter your account password:
</label>
<input
aria-describedby="passwordFieldId-invalid-feedback"
className="form-control is-invalid"
id="passwordFieldId"
name="password"
onChange={[MockFunction]}
type="password"
value="fluffy bunnies"
/>
<strong
className="invalid-feedback"
id="passwordFieldId-invalid-feedback"
>
A password is required
</strong>
</div>
</div>
</div>
<div
className="modal-footer"
>
<button
className="btn btn-danger"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Yes, Delete
</button>
<button
className="btn js-close-modal-on-click btn-secondary"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Cancel
</button>
</div>
</div>
</div>
</div>
</div>
`;
exports[`ConfirmationModal should match open confirmation modal snapshot 1`] = `
<div>
<div
className="modal-backdrop show"
role="presentation"
/>
<div
className="modal js-close-modal-on-click show d-block"
onClick={[Function]}
role="presentation"
>
<div
aria-labelledby="id4"
aria-modal={true}
className="modal-dialog"
role="dialog"
tabIndex="-1"
>
<div
className="modal-content"
>
<div
className="modal-header"
>
<h2
className="modal-title"
id="id4"
>
Are you sure?
</h2>
</div>
<div
className="modal-body"
>
<div>
<div
className="alert d-flex align-items-start alert-warning mt-n2"
>
<div>
<svg
aria-hidden="true"
className="svg-inline--fa fa-exclamation-triangle fa-w-18 mr-2"
data-icon="exclamation-triangle"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
fill="currentColor"
style={Object {}}
/>
</svg>
</div>
<div>
<h6>
You have selected "Delete My Account". Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.
</h6>
<p>
If you proceed, you will be unable to use this account to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employer's or university's system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.
</p>
<p>
<span>
You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion,
<a
href="https://edx.readthedocs.io/projects/edx-guide-for-students/en/latest/SFD_certificates.html#printing-a-certificate"
onClick={[Function]}
target="_self"
>
follow the instructions for printing or downloading a certificate
</a>
.
</span>
</p>
</div>
</div>
<div
className="form-group"
>
<label
className="d-block"
htmlFor="passwordFieldId"
>
If you still wish to continue and delete your account, please enter your account password:
</label>
<input
aria-describedby=""
className="form-control"
id="passwordFieldId"
name="password"
onChange={[MockFunction]}
type="password"
value="fluffy bunnies"
/>
<strong
className="invalid-feedback"
id="passwordFieldId-invalid-feedback"
>
Unable to delete account
</strong>
</div>
</div>
</div>
<div
className="modal-footer"
>
<button
className="btn btn-danger"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Yes, Delete
</button>
<button
className="btn js-close-modal-on-click btn-secondary"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Cancel
</button>
</div>
</div>
</div>
</div>
</div>
`;

View File

@@ -0,0 +1,247 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DeleteAccount should match default section snapshot 1`] = `
<div>
<h2
className="section-heading"
>
Delete My Account
</h2>
<p>
We're sorry to see you go!
</p>
<p>
Please note: Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.
</p>
<p>
Once your account is deleted, you cannot use it to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employers or universitys system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.
</p>
<p>
<span>
You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion,
<a
href="https://edx.readthedocs.io/projects/edx-guide-for-students/en/latest/SFD_certificates.html#printing-a-certificate"
onClick={[Function]}
target="_self"
>
follow the instructions for printing or downloading a certificate
</a>
.
</span>
</p>
<p
className="text-danger h6"
>
Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on edX.
</p>
<p>
<a
href="https://support.edx.org/hc/en-us/sections/115004139268-Manage-Your-Account-Settings"
onClick={[Function]}
target="_self"
>
Want to change your email, name, or password instead?
</a>
</p>
<p>
<button
className="btn btn-outline-danger"
disabled={false}
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Delete My Account
</button>
</p>
</div>
`;
exports[`DeleteAccount should match unverified account section snapshot 1`] = `
<div>
<h2
className="section-heading"
>
Delete My Account
</h2>
<p>
We're sorry to see you go!
</p>
<p>
Please note: Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.
</p>
<p>
Once your account is deleted, you cannot use it to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employers or universitys system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.
</p>
<p>
<span>
You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion,
<a
href="https://edx.readthedocs.io/projects/edx-guide-for-students/en/latest/SFD_certificates.html#printing-a-certificate"
onClick={[Function]}
target="_self"
>
follow the instructions for printing or downloading a certificate
</a>
.
</span>
</p>
<p
className="text-danger h6"
>
Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on edX.
</p>
<p>
<a
href="https://support.edx.org/hc/en-us/sections/115004139268-Manage-Your-Account-Settings"
onClick={[Function]}
target="_self"
>
Want to change your email, name, or password instead?
</a>
</p>
<p>
<button
className="btn btn-outline-danger"
disabled={true}
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Delete My Account
</button>
</p>
<div
className="alert d-flex align-items-start alert-warning mt-n2"
>
<div>
<svg
aria-hidden="true"
className="svg-inline--fa fa-exclamation-triangle fa-w-18 mr-2"
data-icon="exclamation-triangle"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
fill="currentColor"
style={Object {}}
/>
</svg>
</div>
<div>
<span>
Before proceeding, please
<a
href="https://support.edx.org/hc/en-us/articles/115000940568-How-do-I-activate-my-account-"
onClick={[Function]}
target="_self"
>
activate your account
</a>
.
</span>
</div>
</div>
</div>
`;
exports[`DeleteAccount should match unverified account section snapshot 2`] = `
<div>
<h2
className="section-heading"
>
Delete My Account
</h2>
<p>
We're sorry to see you go!
</p>
<p>
Please note: Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.
</p>
<p>
Once your account is deleted, you cannot use it to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employers or universitys system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.
</p>
<p>
<span>
You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion,
<a
href="https://edx.readthedocs.io/projects/edx-guide-for-students/en/latest/SFD_certificates.html#printing-a-certificate"
onClick={[Function]}
target="_self"
>
follow the instructions for printing or downloading a certificate
</a>
.
</span>
</p>
<p
className="text-danger h6"
>
Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on edX.
</p>
<p>
<a
href="https://support.edx.org/hc/en-us/sections/115004139268-Manage-Your-Account-Settings"
onClick={[Function]}
target="_self"
>
Want to change your email, name, or password instead?
</a>
</p>
<p>
<button
className="btn btn-outline-danger"
disabled={true}
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Delete My Account
</button>
</p>
<div
className="alert d-flex align-items-start alert-warning mt-n2"
>
<div>
<svg
aria-hidden="true"
className="svg-inline--fa fa-exclamation-triangle fa-w-18 mr-2"
data-icon="exclamation-triangle"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
fill="currentColor"
style={Object {}}
/>
</svg>
</div>
<div>
<span>
Before proceeding, please
<a
href="https://support.edx.org/hc/en-us/articles/207206067"
onClick={[Function]}
target="_self"
>
unlink all social media accounts
</a>
.
</span>
</div>
</div>
</div>
`;

View File

@@ -0,0 +1,123 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SuccessModal should match default closed success modal snapshot 1`] = `
<div>
<div
className="fade"
role="presentation"
/>
<div
className="modal js-close-modal-on-click fade"
onClick={[Function]}
role="presentation"
>
<div
aria-labelledby="id3"
aria-modal={true}
className=""
role="dialog"
tabIndex="-1"
>
<div
className="modal-content"
>
<div
className="modal-header"
>
<h2
className="modal-title"
id="id3"
>
We're sorry to see you go! Your account will be deleted shortly.
</h2>
</div>
<div
className="modal-body"
>
<div>
<p
className="h6"
>
Account deletion, including removal from email lists, may take a few weeks to fully process through our system. If you want to opt-out of emails before then, please unsubscribe from the footer of any email.
</p>
</div>
</div>
<div
className="modal-footer"
>
<button
className="btn js-close-modal-on-click btn-secondary"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Close
</button>
</div>
</div>
</div>
</div>
</div>
`;
exports[`SuccessModal should match open success modal snapshot 1`] = `
<div>
<div
className="modal-backdrop show"
role="presentation"
/>
<div
className="modal js-close-modal-on-click show d-block"
onClick={[Function]}
role="presentation"
>
<div
aria-labelledby="id4"
aria-modal={true}
className="modal-dialog"
role="dialog"
tabIndex="-1"
>
<div
className="modal-content"
>
<div
className="modal-header"
>
<h2
className="modal-title"
id="id4"
>
We're sorry to see you go! Your account will be deleted shortly.
</h2>
</div>
<div
className="modal-body"
>
<div>
<p
className="h6"
>
Account deletion, including removal from email lists, may take a few weeks to fully process through our system. If you want to opt-out of emails before then, please unsubscribe from the footer of any email.
</p>
</div>
</div>
<div
className="modal-footer"
>
<button
className="btn js-close-modal-on-click btn-secondary"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Close
</button>
</div>
</div>
</div>
</div>
</div>
`;

View File

@@ -0,0 +1,31 @@
import formurlencoded from 'form-urlencoded';
import { applyConfiguration, handleRequestError } from '../../../common/serviceUtils';
let config = {
DELETE_ACCOUNT_URL: null,
};
let apiClient = null;
export function configureService(newConfig, newApiClient) {
config = applyConfiguration(config, newConfig);
apiClient = newApiClient;
}
/**
* Request deletion of the user's account.
*/
export async function postDeleteAccount(password) {
const { data } = await apiClient
.post(
config.DELETE_ACCOUNT_URL,
formurlencoded({ password }),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
},
)
.catch(handleRequestError);
return data;
}

View File

@@ -0,0 +1,2 @@
export { default } from './DeleteAccount';
export { configureService } from './data/service';

View File

@@ -0,0 +1,116 @@
import { defineMessages } from '@edx/frontend-i18n';
const messages = defineMessages({
'account.settings.delete.account.header': {
id: 'account.settings.delete.account.header',
defaultMessage: 'Delete My Account',
description: 'Header for the user account deletion area',
},
'account.settings.delete.account.subheader': {
id: 'account.settings.delete.account.subheader',
defaultMessage: 'We\'re sorry to see you go!',
description: 'A message in the user account deletion area',
},
'account.settings.delete.account.text.1': {
id: 'account.settings.delete.account.text.1',
defaultMessage: 'Please note: Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.',
description: 'A message in the user account deletion area',
},
'account.settings.delete.account.text.2': {
id: 'account.settings.delete.account.text.2',
defaultMessage: 'Once your account is deleted, you cannot use it to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employers or universitys system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.',
description: 'A message in the user account deletion area',
},
'account.settings.delete.account.text.3.link': {
id: 'account.settings.delete.account.text.3.link',
defaultMessage: 'follow the instructions for printing or downloading a certificate',
description: 'This text will be a link to a technical support page; it will go in the phrase If you want to make a copy of these for your records, ______ .',
},
'account.settings.delete.account.text.warning': {
id: 'account.settings.delete.account.text.warning',
defaultMessage: 'Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on edX.',
description: 'A message in the user account deletion area',
},
'account.settings.delete.account.text.change.instead': {
id: 'account.settings.delete.account.text.change.instead',
defaultMessage: 'Want to change your email, name, or password instead?',
description: 'A message in the user account deletion area',
},
'account.settings.delete.account.button': {
id: 'account.settings.delete.account.button',
defaultMessage: 'Delete My Account',
description: 'Button label to permanently delete your edX account',
},
'account.settings.delete.account.please.activate': {
id: 'account.settings.delete.account.please.activate',
defaultMessage: 'activate your account',
description: 'This is the text on a link that goes to the support page. It is part of this sentence: Before proceeding, please activate your account.',
},
'account.settings.delete.account.please.unlink': {
id: 'account.settings.delete.account.please.unlink',
defaultMessage: 'unlink all social media accounts',
description: 'This is the text on a link that goes to the support page. It is part of this sentence: Before proceeding, please unlink all social media accounts.',
},
'account.settings.delete.account.modal.header': {
id: 'account.settings.delete.account.modal.header',
defaultMessage: 'Are you sure?',
description: 'Title of the dialog asking user to confirm that they want to delete their entire account',
},
'account.settings.delete.account.modal.text.1': {
id: 'account.settings.delete.account.modal.text.1',
defaultMessage: 'You have selected "Delete My Account". Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.',
description: 'Messaging in the dialog asking user to confirm that they want to delete their entire account',
},
'account.settings.delete.account.modal.text.2': {
id: 'account.settings.delete.account.modal.text.2',
defaultMessage: 'If you proceed, you will be unable to use this account to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employer\'s or university\'s system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.',
description: 'Messaging in the dialog asking user to confirm that they want to delete their entire account',
},
'account.settings.delete.account.modal.enter.password': {
id: 'account.settings.delete.account.modal.enter.password',
defaultMessage: 'If you still wish to continue and delete your account, please enter your account password:',
description: 'Asking for the user\'s account password',
},
'account.settings.delete.account.modal.confirm.delete': {
id: 'account.settings.delete.account.modal.confirm.delete',
defaultMessage: 'Yes, Delete',
description: 'Button label for user to confirm it is okay to delete their account',
},
'account.settings.delete.account.modal.confirm.cancel': {
id: 'account.settings.delete.account.modal.confirm.cancel',
defaultMessage: 'Cancel',
description: 'The cancel button on the delete my account modal confirmation',
},
'account.settings.delete.account.error.unable.to.delete': {
id: 'account.settings.delete.account.error.unable.to.delete',
defaultMessage: 'Unable to delete account',
description: 'Error message when account deletion failed',
},
'account.settings.delete.account.error.no.password': {
id: 'account.settings.delete.account.error.no.password',
defaultMessage: 'A password is required',
description: 'Error message when user has not entered their password',
},
'account.settings.delete.account.error.unable.to.delete.details': {
id: 'account.settings.delete.account.error.unable.to.delete.details',
defaultMessage: 'Sorry, there was an error trying to process your request. Please try again later.',
description: 'Error message when account deletion failed',
},
'account.settings.delete.account.modal.after.header': {
id: 'account.settings.delete.account.modal.after.header',
defaultMessage: 'We\'re sorry to see you go! Your account will be deleted shortly.',
description: 'Title displayed after user account is deleted',
},
'account.settings.delete.account.modal.after.text': {
id: 'account.settings.delete.account.modal.after.text',
defaultMessage: 'Account deletion, including removal from email lists, may take a few weeks to fully process through our system. If you want to opt-out of emails before then, please unsubscribe from the footer of any email.',
description: 'Text displayed after user account is deleted',
},
'account.settings.delete.account.modal.after.button': {
id: 'account.settings.delete.account.modal.after.button',
defaultMessage: 'Close',
description: 'Label on button to close a dialog',
},
});
export default messages;

View File

@@ -1,13 +1,13 @@
import ConnectedAccountSettingsPage from './AccountSettingsPage';
import reducer from './reducers';
import saga from './sagas';
import { configureApiService } from './service';
import { configureService } from './service';
import { storeName } from './selectors';
export {
configureService,
ConnectedAccountSettingsPage,
reducer,
saga,
configureApiService,
storeName,
};

View File

@@ -1,14 +1,18 @@
import {
FETCH_ACCOUNT,
FETCH_SETTINGS,
OPEN_FORM,
CLOSE_FORM,
SAVE_ACCOUNT,
RESET_PASSWORD,
SAVE_SETTINGS,
FETCH_TIME_ZONES,
SAVE_PREVIOUS_SITE_LANGUAGE,
UPDATE_DRAFT,
RESET_DRAFTS,
FETCH_THIRD_PARTY_AUTH_PROVIDERS,
} from './actions';
import { reducer as siteLanguageReducer, FETCH_SITE_LANGUAGES } from './site-language';
import { reducer as resetPasswordReducer, RESET_PASSWORD } from './reset-password';
import { reducer as thirdPartyAuthReducer, DISCONNECT_AUTH } from './third-party-auth';
export const defaultState = {
loading: false,
loaded: false,
@@ -19,36 +23,47 @@ export const defaultState = {
confirmationValues: {},
drafts: {},
saveState: null,
resetPasswordState: null,
timeZones: [],
countryTimeZones: [],
previousSiteLanguage: null,
siteLanguage: siteLanguageReducer(),
resetPassword: resetPasswordReducer(),
thirdPartyAuth: thirdPartyAuthReducer(),
};
const accountSettingsReducer = (state = defaultState, action) => {
const reducer = (state = defaultState, action) => {
let dispatcherIsOpenForm;
switch (action.type) {
case FETCH_ACCOUNT.BEGIN:
case FETCH_SETTINGS.BEGIN:
return {
...state,
loading: true,
loaded: false,
loadingError: null,
};
case FETCH_ACCOUNT.SUCCESS:
case FETCH_SETTINGS.SUCCESS:
return {
...state,
values: Object.assign({}, state.values, action.payload.values),
// Dump the providers into thirdPartyAuth.
thirdPartyAuth: Object.assign({}, state.thirdPartyAuth, {
providers: action.payload.thirdPartyAuthProviders,
}),
profileDataManager: action.payload.profileDataManager,
timeZones: action.payload.timeZones,
loading: false,
loaded: true,
loadingError: null,
};
case FETCH_ACCOUNT.FAILURE:
case FETCH_SETTINGS.FAILURE:
return {
...state,
loading: false,
loaded: false,
loadingError: action.payload.error,
};
case FETCH_ACCOUNT.RESET:
case FETCH_SETTINGS.RESET:
return {
...state,
loading: false,
@@ -92,13 +107,13 @@ const accountSettingsReducer = (state = defaultState, action) => {
drafts: {},
};
case SAVE_ACCOUNT.BEGIN:
case SAVE_SETTINGS.BEGIN:
return {
...state,
saveState: 'pending',
errors: {},
};
case SAVE_ACCOUNT.SUCCESS:
case SAVE_SETTINGS.SUCCESS:
return {
...state,
saveState: 'complete',
@@ -110,58 +125,56 @@ const accountSettingsReducer = (state = defaultState, action) => {
action.payload.confirmationValues,
),
};
case SAVE_ACCOUNT.FAILURE:
case SAVE_SETTINGS.FAILURE:
return {
...state,
saveState: 'error',
errors: Object.assign({}, state.errors, action.payload.errors),
};
case SAVE_ACCOUNT.RESET:
case SAVE_SETTINGS.RESET:
return {
...state,
saveState: null,
errors: {},
};
case RESET_PASSWORD.BEGIN:
case SAVE_PREVIOUS_SITE_LANGUAGE:
return {
...state,
resetPasswordState: 'pending',
previousSiteLanguage: action.payload.previousSiteLanguage,
};
case FETCH_TIME_ZONES.SUCCESS:
return {
...state,
countryTimeZones: action.payload.timeZones,
};
// TODO: Once all the above cases have been converted into sub-reducers, we can use
// combineReducers in this file to greatly simplify it.
case FETCH_SITE_LANGUAGES.BEGIN:
case FETCH_SITE_LANGUAGES.SUCCESS:
case FETCH_SITE_LANGUAGES.FAILURE:
case FETCH_SITE_LANGUAGES.RESET:
return {
...state,
siteLanguage: siteLanguageReducer(state.siteLanguage, action),
};
case RESET_PASSWORD.BEGIN:
case RESET_PASSWORD.SUCCESS:
return {
...state,
resetPasswordState: 'complete',
resetPassword: resetPasswordReducer(state.resetPassword, action),
};
case FETCH_THIRD_PARTY_AUTH_PROVIDERS.BEGIN:
case DISCONNECT_AUTH.BEGIN:
case DISCONNECT_AUTH.SUCCESS:
case DISCONNECT_AUTH.FAILURE:
case DISCONNECT_AUTH.RESET:
return {
...state,
thirdPartyAuthLoading: true,
thirdPartyAuthLoaded: false,
thirdPartyAuthLoadingError: null,
};
case FETCH_THIRD_PARTY_AUTH_PROVIDERS.SUCCESS:
return {
...state,
authProviders: action.payload.providers,
thirdPartyAuthLoading: false,
thirdPartyAuthLoaded: true,
thirdPartyAuthLoadingError: null,
};
case FETCH_THIRD_PARTY_AUTH_PROVIDERS.FAILURE:
return {
...state,
thirdPartyAuthLoading: false,
thirdPartyAuthLoaded: false,
thirdPartyAuthLoadingError: action.payload.error,
};
case FETCH_THIRD_PARTY_AUTH_PROVIDERS.RESET:
return {
...state,
thirdPartyAuthLoading: false,
thirdPartyAuthLoaded: false,
thirdPartyAuthLoadingError: null,
thirdPartyAuth: thirdPartyAuthReducer(state.thirdPartyAuth, action),
};
default:
@@ -169,4 +182,4 @@ const accountSettingsReducer = (state = defaultState, action) => {
}
};
export default accountSettingsReducer;
export default reducer;

View File

@@ -0,0 +1,47 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from '@edx/frontend-i18n';
import { Hyperlink } from '@edx/paragon';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { Alert } from '../../common';
const ConfirmationAlert = (props) => {
const { email } = props;
const technicalSupportLink = (
<Hyperlink
destination="https://support.edx.org/hc/en-us/articles/206212088-What-if-I-did-not-receive-a-password-reset-message-"
>
<FormattedMessage
id="account.settings.editable.field.password.reset.button.confirmation.support.link"
defaultMessage="technical support"
description="link text used in message: account.settings.editable.field.password.reset.button.confirmation 'Contact technical support.'"
/>
</Hyperlink>
);
return (
<Alert
className="alert-warning mt-n2"
icon={<FontAwesomeIcon className="mr-2" icon={faExclamationTriangle} />}
>
<FormattedMessage
id="account.settings.editable.field.password.reset.button.confirmation"
defaultMessage="We've sent a message to {email}. Click the link in the message to reset your password. Didn't receive the message? Contact {technicalSupportLink}."
description="The password reset button in account settings"
values={{
email,
technicalSupportLink,
}}
/>
</Alert>
);
};
ConfirmationAlert.propTypes = {
email: PropTypes.string.isRequired,
};
export default ConfirmationAlert;

View File

@@ -0,0 +1,69 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-i18n';
import { StatefulButton } from '@edx/paragon';
import { resetPassword } from './data/actions';
import messages from './messages';
import ConfirmationAlert from './ConfirmationAlert';
const ResetPassword = (props) => {
const { email, intl, status } = props;
return (
<div className="form-group">
<h6 aria-level="3">
<FormattedMessage
id="account.settings.editable.field.password.reset.label"
defaultMessage="Password"
description="The password label in account settings"
/>
</h6>
<p>
<StatefulButton
className="btn-link"
state={status}
onClick={(e) => {
// Swallow clicks if the state is pending.
// We do this instead of disabling the button to prevent
// it from losing focus (disabled elements cannot have focus).
// Disabling it would causes upstream issues in focus management.
// Swallowing the onSubmit event on the form would be better, but
// we would have to add that logic for every field given our
// current structure of the application.
if (status === 'pending') {
e.preventDefault();
}
props.resetPassword(email);
}}
disabledStates={[]}
labels={{
default: intl.formatMessage(messages['account.settings.editable.field.password.reset.button']),
}}
/>
</p>
{status === 'complete' ? <ConfirmationAlert email={email} /> : null}
</div>
);
};
ResetPassword.propTypes = {
email: PropTypes.string,
intl: intlShape.isRequired,
resetPassword: PropTypes.func.isRequired,
status: PropTypes.string,
};
ResetPassword.defaultProps = {
email: '',
status: null,
};
const mapStateToProps = state => state.accountSettings.resetPassword;
export default connect(
mapStateToProps,
{
resetPassword,
},
)(injectIntl(ResetPassword));

View File

@@ -0,0 +1,22 @@
import { utils } from '../../../common';
const { AsyncActionType } = utils;
export const RESET_PASSWORD = new AsyncActionType('ACCOUNT_SETTINGS', 'RESET_PASSWORD');
export const resetPassword = email => ({
type: RESET_PASSWORD.BASE,
payload: { email },
});
export const resetPasswordBegin = () => ({
type: RESET_PASSWORD.BEGIN,
});
export const resetPasswordSuccess = () => ({
type: RESET_PASSWORD.SUCCESS,
});
export const resetPasswordReset = () => ({
type: RESET_PASSWORD.RESET,
});

View File

@@ -0,0 +1,27 @@
import { RESET_PASSWORD } from './actions';
export const defaultState = {
status: null,
};
const reducer = (state = defaultState, action = null) => {
if (action !== null) {
switch (action.type) {
case RESET_PASSWORD.BEGIN:
return {
...state,
status: 'pending',
};
case RESET_PASSWORD.SUCCESS:
return {
...state,
status: 'complete',
};
default:
}
}
return state;
};
export default reducer;

View File

@@ -0,0 +1,14 @@
import { put, call, takeEvery } from 'redux-saga/effects';
import { resetPasswordBegin, resetPasswordSuccess, RESET_PASSWORD } from './actions';
import { postResetPassword } from './service';
function* handleResetPassword(action) {
yield put(resetPasswordBegin());
const response = yield call(postResetPassword, action.payload.email);
yield put(resetPasswordSuccess(response));
}
export default function* saga() {
yield takeEvery(RESET_PASSWORD.BASE, handleResetPassword);
}

View File

@@ -0,0 +1,29 @@
import formurlencoded from 'form-urlencoded';
import { applyConfiguration, handleRequestError } from '../../../common/serviceUtils';
let config = {
PASSWORD_RESET_URL: null,
};
let apiClient = null;
export function configureService(newConfig, newApiClient) {
config = applyConfiguration(config, newConfig);
apiClient = newApiClient;
}
export async function postResetPassword(email) {
const { data } = await apiClient
.post(
config.PASSWORD_RESET_URL,
formurlencoded({ email }),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
},
)
.catch(handleRequestError);
return data;
}

View File

@@ -0,0 +1,5 @@
export { default } from './ResetPassword';
export { default as reducer } from './data/reducers';
export { RESET_PASSWORD } from './data/actions';
export { default as saga } from './data/sagas';
export { configureService } from './data/service';

View File

@@ -0,0 +1,11 @@
import { defineMessages } from '@edx/frontend-i18n';
const messages = defineMessages({
'account.settings.editable.field.password.reset.button': {
id: 'account.settings.editable.field.password.reset.button',
defaultMessage: 'Reset Password',
description: 'The password reset button in account settings',
},
});
export default messages;

View File

@@ -1,94 +1,109 @@
import { call, put, delay, takeEvery, select } from 'redux-saga/effects';
import { push } from 'connected-react-router';
import { logAPIErrorResponse } from '@edx/frontend-logging';
import { call, put, delay, takeEvery, select, all } from 'redux-saga/effects';
// Actions
import {
FETCH_ACCOUNT,
fetchAccountBegin,
fetchAccountSuccess,
fetchAccountFailure,
FETCH_SETTINGS,
fetchSettingsBegin,
fetchSettingsSuccess,
fetchSettingsFailure,
closeForm,
SAVE_ACCOUNT,
saveAccountBegin,
saveAccountSuccess,
saveAccountFailure,
RESET_PASSWORD,
resetPasswordBegin,
resetPasswordSuccess,
FETCH_THIRD_PARTY_AUTH_PROVIDERS,
fetchThirdPartyAuthProvidersBegin,
fetchThirdPartyAuthProvidersSuccess,
fetchThirdPartyAuthProvidersFailure,
SAVE_SETTINGS,
saveSettingsBegin,
saveSettingsSuccess,
saveSettingsFailure,
savePreviousSiteLanguage,
FETCH_TIME_ZONES,
fetchTimeZones,
fetchTimeZonesSuccess,
} from './actions';
import { usernameSelector } from './selectors';
import { usernameSelector, userRolesSelector, siteLanguageSelector } from './selectors';
// Sub-modules
import { saga as resetPasswordSaga } from './reset-password';
import { saga as siteLanguageSaga, ApiService as SiteLanguageApiService } from './site-language';
import { saga as thirdPartyAuthSaga } from './third-party-auth';
// Services
import * as ApiService from './service';
export function* handleFetchAccount() {
try {
yield put(fetchAccountBegin());
import { setLocale, handleRtl } from '@edx/frontend-i18n'; // eslint-disable-line
export function* handleFetchSettings() {
try {
yield put(fetchSettingsBegin());
const username = yield select(usernameSelector);
const values = yield call(ApiService.getAccount, username);
yield put(fetchAccountSuccess(values));
const userRoles = yield select(userRolesSelector);
const {
thirdPartyAuthProviders, profileDataManager, timeZones, ...values
} = yield call(
ApiService.getSettings,
username,
userRoles,
);
if (values.country) yield put(fetchTimeZones(values.country));
yield put(fetchSettingsSuccess({
values,
thirdPartyAuthProviders,
profileDataManager,
timeZones,
}));
} catch (e) {
logAPIErrorResponse(e);
yield put(fetchAccountFailure(e.message));
yield put(push('/error'));
yield put(fetchSettingsFailure(e.message));
throw e;
}
}
export function* handleSaveAccount(action) {
export function* handleSaveSettings(action) {
try {
yield put(saveAccountBegin());
yield put(saveSettingsBegin());
const username = yield select(usernameSelector);
const { commitValues, formId } = action.payload;
const commitData = { [formId]: commitValues };
const savedValues = yield call(ApiService.patchAccount, username, commitData);
yield put(saveAccountSuccess(savedValues, commitData));
let savedValues = null;
if (formId === 'siteLanguage') {
const previousSiteLanguage = yield select(siteLanguageSelector);
yield all([
call(SiteLanguageApiService.patchPreferences, username, { prefLang: commitValues }),
call(SiteLanguageApiService.postSetLang, commitValues),
]);
yield put(setLocale(commitValues));
yield put(savePreviousSiteLanguage(previousSiteLanguage.savedValue));
handleRtl();
savedValues = commitData;
} else {
savedValues = yield call(ApiService.patchSettings, username, commitData);
}
yield put(saveSettingsSuccess(savedValues, commitData));
if (savedValues.country) yield put(fetchTimeZones(savedValues.country));
yield delay(1000);
yield put(closeForm(action.payload.formId));
} catch (e) {
if (e.fieldErrors) {
yield put(saveAccountFailure({ fieldErrors: e.fieldErrors }));
yield put(saveSettingsFailure({ fieldErrors: e.fieldErrors }));
} else {
logAPIErrorResponse(e);
yield put(saveAccountFailure(e.message));
yield put(push('/error'));
yield put(saveSettingsFailure(e.message));
throw e;
}
}
}
export function* handleResetPassword() {
try {
yield put(resetPasswordBegin());
const response = yield call(ApiService.postResetPassword);
yield put(resetPasswordSuccess(response));
} catch (e) {
logAPIErrorResponse(e);
yield put(push('/error'));
}
export function* handleFetchTimeZones(action) {
const response = yield call(ApiService.getTimeZones, action.payload.country);
yield put(fetchTimeZonesSuccess(response, action.payload.country));
}
export function* handleFetchThirdPartyAuthProviders() {
try {
yield put(fetchThirdPartyAuthProvidersBegin());
const authProviders = yield call(ApiService.getThirdPartyAuthProviders);
yield put(fetchThirdPartyAuthProvidersSuccess(authProviders));
} catch (e) {
logAPIErrorResponse(e);
yield put(fetchThirdPartyAuthProvidersFailure(e.message));
yield put(push('/error'));
}
}
export default function* saga() {
yield takeEvery(FETCH_ACCOUNT.BASE, handleFetchAccount);
yield takeEvery(SAVE_ACCOUNT.BASE, handleSaveAccount);
yield takeEvery(RESET_PASSWORD.BASE, handleResetPassword);
yield takeEvery(FETCH_THIRD_PARTY_AUTH_PROVIDERS.BASE, handleFetchThirdPartyAuthProviders);
yield takeEvery(FETCH_SETTINGS.BASE, handleFetchSettings);
yield takeEvery(SAVE_SETTINGS.BASE, handleSaveSettings);
yield takeEvery(FETCH_TIME_ZONES.BASE, handleFetchTimeZones);
yield all([
siteLanguageSaga(),
resetPasswordSaga(),
thirdPartyAuthSaga(),
]);
}

View File

@@ -1,11 +1,24 @@
import { createSelector, createStructuredSelector } from 'reselect';
import {
localeSelector,
getCountryList,
getLanguageList,
} from '@edx/frontend-i18n'; // eslint-disable-line
export const storeName = 'account-settings';
import { siteLanguageOptionsSelector, siteLanguageListSelector } from './site-language';
export const storeName = 'accountSettings';
export const usernameSelector = state => state.authentication.username;
export const userRolesSelector = state => state.authentication.roles || [];
export const accountSettingsSelector = state => ({ ...state[storeName] });
const duplicateTpaProviderSelector = state => state.errors.duplicateTpaProvider;
const configurationSelector = state => state.configuration;
const editableFieldNameSelector = (state, props) => props.name;
const valuesSelector = createSelector(
@@ -18,11 +31,9 @@ const draftsSelector = createSelector(
accountSettings => accountSettings.drafts,
);
const editableFieldValueSelector = createSelector(
editableFieldNameSelector,
valuesSelector,
draftsSelector,
(name, values, drafts) => (drafts[name] !== undefined ? drafts[name] : values[name]),
const previousSiteLanguageSelector = createSelector(
accountSettingsSelector,
accountSettings => accountSettings.previousSiteLanguage,
);
const editableFieldErrorSelector = createSelector(
@@ -49,27 +60,153 @@ const saveStateSelector = createSelector(
);
export const editableFieldSelector = createStructuredSelector({
value: editableFieldValueSelector,
error: editableFieldErrorSelector,
confirmationValue: editableFieldConfirmationValuesSelector,
saveState: saveStateSelector,
isEditing: isEditingSelector,
});
export const resetPasswordSelector = createSelector(
export const profileDataManagerSelector = createSelector(
accountSettingsSelector,
accountSettings => ({
resetPasswordState: accountSettings.resetPasswordState,
email: accountSettings.values.email,
accountSettings => accountSettings.profileDataManager,
);
export const staticFieldsSelector = createSelector(
accountSettingsSelector,
accountSettings => (accountSettings.profileDataManager ? ['name', 'email', 'country'] : []),
);
export const hiddenFieldsSelector = createSelector(
accountSettingsSelector,
accountSettings => (accountSettings.profileDataManager ? [] : ['secondary_email']),
);
/**
* If there's no draft present at all (undefined), use the original committed value.
*/
function chooseFormValue(draft, committed) {
return draft !== undefined ? draft : committed;
}
const formValuesSelector = createSelector(
valuesSelector,
draftsSelector,
(values, drafts) => {
const formValues = {};
Object.entries(values).forEach(([name, value]) => {
formValues[name] = chooseFormValue(drafts[name], value) || '';
});
return formValues;
},
);
const countryOptionsSelector = createSelector(
localeSelector,
locale => getCountryList(locale).map(({ code, name }) => ({ value: code, label: name })),
);
const languageProficiencyOptionsSelector = createSelector(
localeSelector,
locale => getLanguageList(locale).map(({ code, name }) => ({ value: code, label: name })),
);
const transformTimeZonesToOptions = timeZoneArr => timeZoneArr
.map(({ time_zone, description }) => ({ // eslint-disable-line camelcase
value: time_zone, label: description,
}));
const timeZonesSelector = createSelector(
accountSettingsSelector,
accountSettings => transformTimeZonesToOptions(accountSettings.timeZones),
);
const countryTimeZonesSelector = createSelector(
accountSettingsSelector,
accountSettings => transformTimeZonesToOptions(accountSettings.countryTimeZones),
);
const activeAccountSelector = createSelector(
accountSettingsSelector,
accountSettings => accountSettings.values.is_active,
);
/**
* This selector converts the site language code back to the server version so that it can match up
* with one of the options in the site language dropdown. The drafts version will already be the
* server version, but if it's from localeSelector, it will be our client (two character) version.
*/
export const siteLanguageSelector = createSelector(
previousSiteLanguageSelector,
draftsSelector,
localeSelector,
(previousValue, drafts, locale) => ({
previousValue,
draftOrSavedValue: (drafts.siteLanguage !== undefined ? drafts.siteLanguage : locale),
savedValue: locale,
}),
);
export const thirdPartyAuthSelector = createSelector(
accountSettingsSelector,
accountSettings => ({
providers: accountSettings.authProviders,
loading: accountSettings.thirdPartyAuthLoading,
loaded: accountSettings.thirdPartyAuthLoaded,
loadingError: accountSettings.thirdPartyAuthLoadingError,
export const betaLanguageBannerSelector = createSelector(
siteLanguageListSelector,
siteLanguageSelector,
(
siteLanguageList,
siteLanguage,
) => ({
siteLanguageList,
siteLanguage,
}),
);
export const accountSettingsPageSelector = createSelector(
accountSettingsSelector,
siteLanguageOptionsSelector,
siteLanguageSelector,
countryOptionsSelector,
languageProficiencyOptionsSelector,
formValuesSelector,
profileDataManagerSelector,
staticFieldsSelector,
hiddenFieldsSelector,
timeZonesSelector,
countryTimeZonesSelector,
activeAccountSelector,
duplicateTpaProviderSelector,
configurationSelector,
(
accountSettings,
siteLanguageOptions,
siteLanguage,
countryOptions,
languageProficiencyOptions,
formValues,
profileDataManager,
staticFields,
hiddenFields,
timeZoneOptions,
countryTimeZoneOptions,
activeAccount,
duplicateTpaProvider,
configuration,
) => ({
siteLanguageOptions,
siteLanguage,
countryOptions,
languageProficiencyOptions,
loading: accountSettings.loading,
loaded: accountSettings.loaded,
loadingError: accountSettings.loadingError,
timeZoneOptions,
countryTimeZoneOptions,
isActive: activeAccount,
formValues,
profileDataManager,
staticFields,
hiddenFields,
duplicateTpaProvider,
tpaProviders: accountSettings.thirdPartyAuth.providers,
supportUrl: configuration.SUPPORT_URL,
logoutUrl: configuration.LOGOUT_URL,
}),
);

View File

@@ -1,9 +1,20 @@
import pick from 'lodash.pick';
import omit from 'lodash.omit';
import isEmpty from 'lodash.isempty';
import { applyConfiguration, handleRequestError, unpackFieldErrors } from '../common/serviceUtils';
import { configureService as configureDeleteAccountApiService } from './delete-account';
import { configureService as configureResetPasswordApiService } from './reset-password';
import { configureService as configureSiteLanguageApiService } from './site-language';
import { configureService as configureThirdPartyAuthApiService, getThirdPartyAuthProviders } from './third-party-auth';
let config = {
BASE_URL: null,
ACCOUNTS_API_BASE_URL: null,
PREFERENCES_API_BASE_URL: null,
ECOMMERCE_API_BASE_URL: null,
LMS_BASE_URL: null,
DELETE_ACCOUNT_URL: null,
PASSWORD_RESET_URL: null,
};
@@ -15,38 +26,22 @@ const SOCIAL_PLATFORMS = [
let apiClient = null;
function validateConfiguration(newConfig) {
Object.keys(config).forEach((key) => {
if (newConfig[key] === undefined) {
throw new Error(`Service configuration error: ${key} is required.`);
}
});
}
export function configureApiService(newConfig, newApiClient) {
validateConfiguration(newConfig);
config = pick(newConfig, Object.keys(config));
export function configureService(newConfig, newApiClient) {
config = applyConfiguration(config, newConfig);
apiClient = newApiClient;
}
function unpackFieldErrors(fieldErrors) {
const unpackedFieldErrors = fieldErrors;
if (fieldErrors.social_links) {
SOCIAL_PLATFORMS.forEach(({ key }) => {
unpackedFieldErrors[key] = fieldErrors.social_links;
});
}
return Object.entries(unpackedFieldErrors)
.reduce((acc, [k, v]) => {
acc[k] = v.user_message;
return acc;
}, {});
configureDeleteAccountApiService(config, apiClient);
configureResetPasswordApiService(config, apiClient);
configureSiteLanguageApiService(config, apiClient);
configureThirdPartyAuthApiService(config, apiClient);
}
function unpackAccountResponseData(data) {
const unpackedData = data;
// This is handled by preferences
delete unpackedData.time_zone;
SOCIAL_PLATFORMS.forEach(({ id, key }) => {
const platformData = data.social_links.find(({ platform }) => platform === id);
unpackedData[key] = typeof platformData === 'object' ? platformData.social_link : '';
@@ -62,33 +57,38 @@ function unpackAccountResponseData(data) {
return unpackedData;
}
function packAccountCommitData(commitData) {
const packedData = commitData;
SOCIAL_PLATFORMS.forEach(({ id, key }) => {
if (commitData[key]) {
packedData.social_links = [{ platform: id, social_link: commitData[key] }];
}
// Skip missing values. Empty strings are valid values and should be preserved.
if (commitData[key] === undefined) return;
packedData.social_links = [{ platform: id, social_link: commitData[key] }];
delete packedData[key];
});
if (commitData.language_proficiencies) {
packedData.language_proficiencies = [{ code: commitData.language_proficiencies }];
if (commitData.language_proficiencies !== undefined) {
if (commitData.language_proficiencies) {
packedData.language_proficiencies = [{ code: commitData.language_proficiencies }];
} else {
// An empty string should be sent as an array.
packedData.language_proficiencies = [];
}
}
if (commitData.year_of_birth !== undefined) {
if (commitData.year_of_birth) {
packedData.year_of_birth = commitData.year_of_birth;
} else {
// An empty string should be sent as null.
packedData.year_of_birth = null;
}
}
return packedData;
}
function handleRequestError(error) {
if (error.response && error.response.data.field_errors) {
const apiError = Object.create(error);
apiError.fieldErrors = unpackFieldErrors(error.response.data.field_errors);
throw apiError;
}
throw error;
}
export async function getAccount(username) {
const { data } = await apiClient.get(`${config.ACCOUNTS_API_BASE_URL}/${username}`);
return unpackAccountResponseData(data);
@@ -99,30 +99,122 @@ export async function patchAccount(username, commitValues) {
headers: { 'Content-Type': 'application/merge-patch+json' },
};
const { data } = await apiClient.patch(
`${config.ACCOUNTS_API_BASE_URL}/${username}`,
packAccountCommitData(commitValues),
requestConfig,
).catch(handleRequestError);
const { data } = await apiClient
.patch(
`${config.ACCOUNTS_API_BASE_URL}/${username}`,
packAccountCommitData(commitValues),
requestConfig,
)
.catch((error) => {
const unpackFunction = (fieldErrors) => {
const unpackedFieldErrors = fieldErrors;
if (fieldErrors.social_links) {
SOCIAL_PLATFORMS.forEach(({ key }) => {
unpackedFieldErrors[key] = fieldErrors.social_links;
});
}
return unpackFieldErrors(unpackedFieldErrors);
};
handleRequestError(error, unpackFunction);
});
return unpackAccountResponseData(data);
}
export async function postResetPassword() {
export async function getPreferences(username) {
const { data } = await apiClient.get(`${config.PREFERENCES_API_BASE_URL}/${username}`);
return data;
}
export async function patchPreferences(username, commitValues) {
const requestConfig = { headers: { 'Content-Type': 'application/merge-patch+json' } };
const requestUrl = `${config.PREFERENCES_API_BASE_URL}/${username}`;
// Ignore the success response, the API does not currently return any data.
await apiClient.patch(requestUrl, commitValues, requestConfig).catch(handleRequestError);
return commitValues;
}
export async function getTimeZones(forCountry) {
const { data } = await apiClient
.post(config.PASSWORD_RESET_URL)
.get(`${config.LMS_BASE_URL}/user_api/v1/preferences/time_zones/`, {
params: { country_code: forCountry },
})
.catch(handleRequestError);
return data;
}
export async function getThirdPartyAuthProviders() {
const { data } = await apiClient.get(`${config.LMS_BASE_URL}/api/third_party_auth/v0/providers/user_status`)
.catch(handleRequestError);
/**
* Determine if the user's profile data is managed by a third-party identity provider.
*/
export async function getProfileDataManager(username, userRoles) {
const userRoleNames = userRoles.map(role => role.split(':')[0]);
return data.map(({ connect_url: connectUrl, disconnect_url: disconnectUrl, ...provider }) => ({
...provider,
connectUrl: `${config.LMS_BASE_URL}${connectUrl}`,
disconnectUrl: `${config.LMS_BASE_URL}${disconnectUrl}`,
}));
if (userRoleNames.includes('enterprise_learner')) {
const url = `${config.LMS_BASE_URL}/enterprise/api/v1/enterprise-learner/?username=${username}`;
const { data } = await apiClient.get(url).catch(handleRequestError);
if ('results' in data) {
for (let i = 0; i < data.results.length; i += 1) {
const enterprise = data.results[i].enterprise_customer;
if (enterprise.sync_learner_profile_data) {
return enterprise.name;
}
}
}
}
return null;
}
/**
* A single function to GET everything considered a setting.
* Currently encapsulates Account, Preferences, and ThirdPartyAuth
*/
export async function getSettings(username, userRoles) {
const results = await Promise.all([
getAccount(username),
getPreferences(username),
getThirdPartyAuthProviders(),
getProfileDataManager(username, userRoles),
getTimeZones(),
]);
return {
...results[0],
...results[1],
thirdPartyAuthProviders: results[2],
profileDataManager: results[3],
timeZones: results[4],
};
}
/**
* A single function to PATCH everything considered a setting.
* Currently encapsulates Account, Preferences, and ThirdPartyAuth
*/
export async function patchSettings(username, commitValues) {
// Note: time_zone exists in the return value from user/v1/accounts
// but it is always null and won't update. It also exists in
// user/v1/preferences where it does update. This is the one we use.
const preferenceKeys = ['time_zone'];
const accountCommitValues = omit(commitValues, preferenceKeys);
const preferenceCommitValues = pick(commitValues, preferenceKeys);
const patchRequests = [];
if (!isEmpty(accountCommitValues)) {
patchRequests.push(patchAccount(username, accountCommitValues));
}
if (!isEmpty(preferenceCommitValues)) {
patchRequests.push(patchPreferences(username, preferenceCommitValues));
}
const results = await Promise.all(patchRequests);
// Assigns in order of requests. Preference keys
// will override account keys. Notably time_zone.
const combinedResults = Object.assign({}, ...results);
return combinedResults;
}

View File

@@ -0,0 +1,25 @@
import { AsyncActionType } from '../../common/utils';
export const FETCH_SITE_LANGUAGES = new AsyncActionType('SITE_LANGUAGE', 'FETCH_SITE_LANGUAGES');
export const fetchSiteLanguages = () => ({
type: FETCH_SITE_LANGUAGES.BASE,
});
export const fetchSiteLanguagesBegin = () => ({
type: FETCH_SITE_LANGUAGES.BEGIN,
});
export const fetchSiteLanguagesSuccess = siteLanguageList => ({
type: FETCH_SITE_LANGUAGES.SUCCESS,
payload: { siteLanguageList },
});
export const fetchSiteLanguagesFailure = error => ({
type: FETCH_SITE_LANGUAGES.FAILURE,
payload: { error },
});
export const fetchSiteLanguagesReset = () => ({
type: FETCH_SITE_LANGUAGES.RESET,
});

View File

@@ -0,0 +1,74 @@
const siteLanguageList = [
{
code: 'en',
name: 'English',
released: true,
},
{
code: 'ar',
name: 'العربية',
released: true,
},
{
code: 'ca',
name: 'Català',
released: false,
},
{
code: 'es-419',
name: 'Español (Latinoamérica)',
released: true,
},
{
code: 'fr',
name: 'Français',
released: true,
},
{
code: 'he',
name: 'עברית',
released: false,
},
{
code: 'id',
name: 'Bahasa Indonesia',
released: false,
},
{
code: 'ko-kr',
name: '한국어 (대한민국)',
released: false,
},
{
code: 'pl',
name: 'Polski',
released: false,
},
{
code: 'pt-br',
name: 'Português (Brasil)',
released: false,
},
{
code: 'ru',
name: 'Русский',
released: false,
},
{
code: 'th',
name: 'ไทย',
released: false,
},
{
code: 'uk',
name: 'Українська',
released: false,
},
{
code: 'zh-cn',
name: '中文 (简体)',
released: true,
},
];
export default siteLanguageList;

View File

@@ -0,0 +1,16 @@
import reducer from './reducers';
import saga from './sagas';
import { configureService, ApiService } from './service';
import { siteLanguageOptionsSelector, siteLanguageListSelector } from './selectors';
import { fetchSiteLanguages, FETCH_SITE_LANGUAGES } from './actions';
export {
ApiService,
configureService,
fetchSiteLanguages,
FETCH_SITE_LANGUAGES,
reducer,
saga,
siteLanguageListSelector,
siteLanguageOptionsSelector,
};

View File

@@ -0,0 +1,48 @@
import { FETCH_SITE_LANGUAGES } from './actions';
export const defaultState = {
loading: false,
loaded: false,
loadingError: null,
siteLanguageList: [],
};
const reducer = (state = defaultState, action = null) => {
if (action !== null) {
switch (action.type) {
case FETCH_SITE_LANGUAGES.BEGIN:
return {
...state,
loading: true,
loaded: false,
loadingError: null,
};
case FETCH_SITE_LANGUAGES.SUCCESS:
return {
...state,
siteLanguageList: action.payload.siteLanguageList,
loading: false,
loaded: true,
loadingError: null,
};
case FETCH_SITE_LANGUAGES.FAILURE:
return {
...state,
loading: false,
loaded: false,
loadingError: action.payload.error,
};
case FETCH_SITE_LANGUAGES.RESET:
return {
...state,
loading: false,
loaded: false,
loadingError: null,
};
default:
}
}
return state;
};
export default reducer;

View File

@@ -0,0 +1,25 @@
import { call, put, takeEvery } from 'redux-saga/effects';
import {
fetchSiteLanguagesBegin,
fetchSiteLanguagesSuccess,
fetchSiteLanguagesFailure,
FETCH_SITE_LANGUAGES,
} from './actions';
import { ApiService } from './service';
import handleFailure from '../../common/sagaUtils';
function* handleFetchSiteLanguages() {
try {
yield put(fetchSiteLanguagesBegin());
const siteLanguageList = yield call(ApiService.getSiteLanguageList);
yield put(fetchSiteLanguagesSuccess(siteLanguageList));
} catch (e) {
yield call(handleFailure, e, fetchSiteLanguagesFailure);
}
}
export default function* saga() {
yield takeEvery(FETCH_SITE_LANGUAGES.BASE, handleFetchSiteLanguages);
}

View File

@@ -0,0 +1,20 @@
import { createSelector } from 'reselect';
import { getModuleState } from '../../common/utils';
export const storePath = ['accountSettings', 'siteLanguage'];
const siteLanguageSelector = state => getModuleState(state, storePath);
export const siteLanguageListSelector = createSelector(
siteLanguageSelector,
siteLanguage => siteLanguage.siteLanguageList,
);
export const siteLanguageOptionsSelector = createSelector(
siteLanguageSelector,
siteLanguage =>
siteLanguage.siteLanguageList.map(({ code, name }) => ({
value: code,
label: name,
})),
);

View File

@@ -0,0 +1,48 @@
import siteLanguageList from './constants';
import { snakeCaseObject, convertKeyNames } from '../../common/utils';
import { applyConfiguration } from '../../common/serviceUtils';
let config = {
BASE_URL: null,
PREFERENCES_API_BASE_URL: null,
LMS_BASE_URL: null,
};
let apiClient = null;
export function configureService(newConfig, newApiClient) {
config = applyConfiguration(config, newConfig);
apiClient = newApiClient;
}
async function getSiteLanguageList() {
return siteLanguageList;
}
async function patchPreferences(username, params) {
let processedParams = snakeCaseObject(params);
processedParams = convertKeyNames(processedParams, {
pref_lang: 'pref-lang',
});
await apiClient.patch(`${config.PREFERENCES_API_BASE_URL}/${username}`, processedParams, {
headers: { 'Content-Type': 'application/merge-patch+json' },
});
return params; // TODO: Once the server returns the updated preferences object, return that.
}
async function postSetLang(code) {
const formData = new FormData();
formData.append('language', code);
await apiClient.post(`${config.LMS_BASE_URL}/i18n/setlang/`, formData, {
headers: { 'X-Requested-With': 'XMLHttpRequest' },
});
}
export const ApiService = {
getSiteLanguageList,
patchPreferences,
postSetLang,
};

View File

@@ -0,0 +1,144 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { FormattedMessage } from '@edx/frontend-i18n';
import { Hyperlink, StatefulButton } from '@edx/paragon';
import { Alert } from '../../common';
import { disconnectAuth } from './data/actions';
class ThirdPartyAuth extends Component {
onClickDisconnect = (e) => {
e.preventDefault();
const providerId = e.currentTarget.getAttribute('data-provider-id');
if (this.props.disconnectionStatuses[providerId] === 'pending') return;
const disconnectUrl = e.currentTarget.getAttribute('data-disconnect-url');
this.props.disconnectAuth(disconnectUrl, providerId);
}
renderUnconnectedProvider(url, name) {
return (
<React.Fragment>
<h6 aria-level="3">{name}</h6>
<Hyperlink destination={url} className="btn btn-outline-primary">
<FormattedMessage
id="account.settings.sso.link.account"
defaultMessage="Sign in with {name}"
description="An action link to link a connected third party account.m {name} will be Google, Facebook, etc."
values={{ name }}
/>
</Hyperlink>
</React.Fragment>
);
}
renderConnectedProvider(url, name, id) {
const hasError = this.props.errors[id];
return (
<React.Fragment>
<h6 aria-level="3">
{name}
<span className="small font-weight-normal text-muted ml-2">
<FormattedMessage
id="account.settings.sso.account.connected"
defaultMessage="Linked"
description="A badge to show that a third party account is linked"
/>
</span>
</h6>
{hasError ? (
<Alert className="alert-danger">
<FormattedMessage
id="account.settings.sso.account.disconnect.error"
defaultMessage="There was a problem disconnecting this account. Contact support if the problem persists."
description="A message displayed when an error occurred while disconnecting a third party account"
/>
</Alert>
) : null}
<StatefulButton
className="btn-link"
state={this.props.disconnectionStatuses[id]}
labels={{
default: (
<FormattedMessage
id="account.settings.sso.unlink.account"
defaultMessage="Unlink {name} account"
description="An action link to unlink a connected third party account"
values={{ name }}
/>
),
}}
onClick={this.onClickDisconnect}
disabledStates={[]}
data-disconnect-url={url}
data-provider-id={id}
/>
</React.Fragment>
);
}
renderProvider({
name, disconnectUrl, connectUrl, connected, id,
}) {
return (
<div className="form-group" key={id}>
{
connected ?
this.renderConnectedProvider(disconnectUrl, name, id) :
this.renderUnconnectedProvider(connectUrl, name)
}
</div>
);
}
renderNoProviders() {
return (
<FormattedMessage
id="account.settings.sso.no.providers"
defaultMessage="No accounts can be linked at this time."
description="Displayed when no third party accounts are available to link an edX account to"
/>
);
}
render() {
if (this.props.providers === undefined) return null;
if (this.props.providers.length === 0) {
return this.renderNoProviders();
}
return this.props.providers.map(this.renderProvider, this);
}
}
ThirdPartyAuth.propTypes = {
providers: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string,
disconnectUrl: PropTypes.string,
connectUrl: PropTypes.string,
connected: PropTypes.bool,
id: PropTypes.string,
})),
disconnectionStatuses: PropTypes.objectOf(PropTypes.oneOf([null, 'pending', 'complete', 'error'])),
errors: PropTypes.objectOf(PropTypes.bool),
disconnectAuth: PropTypes.func.isRequired,
};
ThirdPartyAuth.defaultProps = {
providers: undefined,
disconnectionStatuses: {},
errors: {},
};
const mapStateToProps = state => state.accountSettings.thirdPartyAuth;
export default connect(
mapStateToProps,
{
disconnectAuth,
},
)(ThirdPartyAuth);

View File

@@ -0,0 +1,22 @@
import { utils } from '../../../common';
const { AsyncActionType } = utils;
export const DISCONNECT_AUTH = new AsyncActionType('ACCOUNT_SETTINGS', 'DISCONNECT_AUTH');
export const disconnectAuth = (url, providerId) => ({
type: DISCONNECT_AUTH.BASE, payload: { url, providerId },
});
export const disconnectAuthBegin = providerId => ({
type: DISCONNECT_AUTH.BEGIN, payload: { providerId },
});
export const disconnectAuthSuccess = (providerId, thirdPartyAuthProviders) => ({
type: DISCONNECT_AUTH.SUCCESS,
payload: { providerId, thirdPartyAuthProviders },
});
export const disconnectAuthFailure = providerId => ({
type: DISCONNECT_AUTH.FAILURE, payload: { providerId },
});
export const disconnectAuthReset = providerId => ({
type: DISCONNECT_AUTH.RESET, payload: { providerId },
});

View File

@@ -0,0 +1,59 @@
import { DISCONNECT_AUTH } from './actions';
export const defaultState = {
providers: [],
disconnectionStatuses: {},
errors: {},
};
const reducer = (state = defaultState, action = null) => {
if (action !== null) {
switch (action.type) {
case DISCONNECT_AUTH.BEGIN:
return {
...state,
disconnectionStatuses: {
...state.disconnectionStatuses,
[action.payload.providerId]: 'pending',
},
};
case DISCONNECT_AUTH.SUCCESS:
return {
...state,
disconnectionStatuses: {
...state.disconnectionStatuses,
[action.payload.providerId]: 'complete',
},
providers: action.payload.thirdPartyAuthProviders,
};
case DISCONNECT_AUTH.FAILURE:
return {
...state,
disconnectionStatuses: {
...state.disconnectionStatuses,
[action.payload.providerId]: 'error',
},
errors: {
...state.errors,
[action.payload.providerId]: true,
},
};
case DISCONNECT_AUTH.RESET:
return {
...state,
disconnectionStatuses: {
...state.disconnectionStatuses,
[action.payload.providerId]: null,
},
errors: {
...state.errors,
[action.payload.providerId]: null,
},
};
default:
}
}
return state;
};
export default reducer;

View File

@@ -0,0 +1,33 @@
import { call, put, takeEvery } from 'redux-saga/effects';
import { logAPIErrorResponse } from '@edx/frontend-logging';
import {
disconnectAuthReset,
disconnectAuthBegin,
disconnectAuthSuccess,
disconnectAuthFailure,
DISCONNECT_AUTH,
} from './actions';
import {
getThirdPartyAuthProviders,
postDisconnectAuth,
} from './service';
function* handleDisconnectAuth(action) {
const { providerId } = action.payload;
try {
yield put(disconnectAuthReset(providerId));
yield put(disconnectAuthBegin(providerId));
yield call(postDisconnectAuth, action.payload.url);
const thirdPartyAuthProviders = yield call(getThirdPartyAuthProviders);
yield put(disconnectAuthSuccess(providerId, thirdPartyAuthProviders));
} catch (e) {
logAPIErrorResponse(e);
yield put(disconnectAuthFailure(providerId));
}
}
export default function* saga() {
yield takeEvery(DISCONNECT_AUTH.BASE, handleDisconnectAuth);
}

View File

@@ -0,0 +1,29 @@
import { applyConfiguration, handleRequestError } from '../../../common/serviceUtils';
let config = {
LMS_BASE_URL: null,
};
let apiClient = null;
export function configureService(newConfig, newApiClient) {
config = applyConfiguration(config, newConfig);
apiClient = newApiClient;
}
export async function getThirdPartyAuthProviders() {
const { data } = await apiClient
.get(`${config.LMS_BASE_URL}/api/third_party_auth/v0/providers/user_status`)
.catch(handleRequestError);
return data.map(({ connect_url: connectUrl, disconnect_url: disconnectUrl, ...provider }) => ({
...provider,
connectUrl: `${config.LMS_BASE_URL}${connectUrl}`,
disconnectUrl: `${config.LMS_BASE_URL}${disconnectUrl}`,
}));
}
export async function postDisconnectAuth(url) {
const { data } = await apiClient.post(url).catch(handleRequestError);
return data;
}

View File

@@ -0,0 +1,5 @@
export { default } from './ThirdPartyAuth';
export { default as reducer } from './data/reducers';
export { default as saga } from './data/sagas';
export { configureService, getThirdPartyAuthProviders } from './data/service';
export { DISCONNECT_AUTH } from './data/actions';

View File

@@ -0,0 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`keepKeys getModuleState should throw an exception on a bad path 1`] = `"Unexpected state key uhoh given to getModuleState. Is your state path set up correctly?"`;

View File

@@ -0,0 +1,41 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { logAPIErrorResponse } from '@edx/frontend-logging';
import ErrorPage from './ErrorPage';
/*
Error boundary component used to log caught errors and display the error page.
*/
export default class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError() {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, info) {
logAPIErrorResponse(`${error} ${info}`);
}
render() {
if (this.state.hasError) {
return <ErrorPage />;
}
return this.props.children;
}
}
ErrorBoundary.propTypes = {
children: PropTypes.node,
};
ErrorBoundary.defaultProps = {
children: null,
};

View File

@@ -0,0 +1,42 @@
import React, { Component } from 'react';
import { FormattedMessage } from '@edx/frontend-i18n';
import { Button } from '@edx/paragon';
export default class ErrorPage extends Component {
reload() {
window.location.reload();
}
render() {
return (
<div className="container-fluid py-5 justify-content-center align-items-start text-center">
<div className="row">
<div className="col">
<p className="my-0 py-5 text-muted">
<FormattedMessage
id="unexpected.error.message.text"
defaultMessage="An unexpected error occurred. Please click the button below to return to refresh the page."
description="error message when an unexpected error occurs"
/>
</p>
</div>
</div>
<div className="row">
<div className="col">
<Button
buttonType="primary"
onClick={this.reload}
label={
<FormattedMessage
id="unexpected.error.button.text"
defaultMessage="Try Again"
description="text for button that tries to reload the app by refreshing the page"
/>
}
/>
</div>
</div>
</div>
);
}
}

42
src/common/hooks.js Normal file
View File

@@ -0,0 +1,42 @@
import { useState } from 'react';
const useAction = (action) => {
const [loaded, setLoaded] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [data, setData] = useState(null);
const performAction = async (body = null) => {
try {
setLoading(true);
setLoaded(false);
setData(null);
setError(null);
const result = await action(body);
setData(result);
setLoaded(true);
} catch (e) {
if (e.response.data) {
setError(e.response.data);
} else {
throw e;
}
setLoaded(false);
} finally {
setLoading(false);
}
};
const resetAction = () => {
setLoading(false);
setLoaded(false);
setData(null);
setError(null);
};
return [{
loaded, loading, data, error,
}, performAction, resetAction];
};
export default useAction;

View File

@@ -1,9 +1,15 @@
import * as utils from './utils';
import Alert from './components/Alert';
import PageLoading from './components/PageLoading';
import ErrorBoundary from './components/ErrorBoundary';
import SwitchContent from './components/SwitchContent';
import { configureUserAccountApiService, fetchUserAccount } from './actions';
export {
Alert,
ErrorBoundary,
PageLoading,
SwitchContent,
utils,
configureUserAccountApiService,
fetchUserAccount,

16
src/common/sagaUtils.js Normal file
View File

@@ -0,0 +1,16 @@
import { put } from 'redux-saga/effects';
import { push } from 'connected-react-router';
import { logAPIErrorResponse } from '@edx/frontend-logging';
export default function* handleFailure(error, failureAction = null, failureRedirectPath = null) {
if (error.fieldErrors && failureAction !== null) {
yield put(failureAction({ fieldErrors: error.fieldErrors }));
}
logAPIErrorResponse(error);
if (failureAction !== null) {
yield put(failureAction(error.message));
}
if (failureRedirectPath !== null) {
yield put(push(failureRedirectPath));
}
}

View File

@@ -0,0 +1,59 @@
import pick from 'lodash.pick';
export function applyConfiguration(expected, actual) {
Object.keys(expected).forEach((key) => {
if (actual[key] === undefined) {
throw new Error(`Service configuration error: ${key} is required.`);
}
});
return pick(actual, Object.keys(expected));
}
/**
* Turns field errors of the form:
*
* {
* "name":{
* "developer_message": "Nerdy message here",
* "user_message": "This value is invalid."
* },
* "other_field": {
* "developer_message": "Other Nerdy message here",
* "user_message": "This other value is invalid."
* }
* }
*
* Into:
*
* {
* "name": "This value is invalid.",
* "other_field": "This other value is invalid"
* }
*/
export function unpackFieldErrors(fieldErrors) {
return Object.entries(fieldErrors).reduce((acc, [k, v]) => {
acc[k] = v.user_message;
return acc;
}, {});
}
/**
* Processes and re-throws request errors. If the response contains a field_errors field, will
* massage the data into a form expected by the client.
*
* Field errors will be packaged as an api error with a fieldErrors field usable by the client.
* Takes an optional unpack function which is used to process the field errors,
* otherwise uses the default unpackFieldErrors function.
*
* @param error The original error object.
* @param unpackFunction (Optional) A function to use to unpack the field errors as a replacement
* for the default.
*/
export function handleRequestError(error, unpackFunction = unpackFieldErrors) {
if (error.response && error.response.data.field_errors) {
const apiError = Object.create(error);
apiError.fieldErrors = unpackFunction(error.response.data.field_errors);
throw apiError;
}
throw error;
}

View File

@@ -47,6 +47,38 @@ export function keepKeys(data, whitelist) {
return result;
}
/**
* Given a state tree and an array representing a set of keys to traverse in that tree, returns
* the portion of the tree at that key path.
*
* Example:
*
* const result = getModuleState(
* {
* first: { red: { awesome: 'sauce' }, blue: { weak: 'sauce' } },
* second: { other: 'data', }
* },
* ['first', 'red']
* );
*
* result will be:
*
* {
* awesome: 'sauce'
* }
*/
export function getModuleState(state, originalPath) {
const path = [...originalPath]; // don't modify your argument
if (path.length < 1) {
return state;
}
const key = path.shift();
if (state[key] === undefined) {
throw new Error(`Unexpected state key ${key} given to getModuleState. Is your state path set up correctly?`);
}
return getModuleState(state[key], path);
}
/**
* Helper class to save time when writing out action types for asynchronous methods. Also helps
* ensure that actions are namespaced.

View File

@@ -1,4 +1,12 @@
import { AsyncActionType, modifyObjectKeys, camelCaseObject, snakeCaseObject, convertKeyNames, keepKeys } from './utils';
import {
AsyncActionType,
modifyObjectKeys,
camelCaseObject,
snakeCaseObject,
convertKeyNames,
keepKeys,
getModuleState,
} from './utils';
describe('modifyObjectKeys', () => {
it('should use the provided modify function to change all keys in and object and its children', () => {
@@ -86,15 +94,22 @@ describe('convertKeyNames', () => {
describe('keepKeys', () => {
it('should keep the specified keys only', () => {
const result = keepKeys({
one: 123,
two: { three: 'skip me' },
four: 'five',
six: null,
8: 'sneaky',
}, [
'one', 'three', 'six', 'seven', '8', // yup, the 8 integer will be converted to a string.
]);
const result = keepKeys(
{
one: 123,
two: { three: 'skip me' },
four: 'five',
six: null,
8: 'sneaky',
},
[
'one',
'three',
'six',
'seven',
'8', // yup, the 8 integer will be converted to a string.
],
);
expect(result).toEqual({
one: 123,
@@ -103,7 +118,6 @@ describe('keepKeys', () => {
});
});
describe('AsyncActionType', () => {
it('should return well formatted action strings', () => {
const actionType = new AsyncActionType('HOUSE_CATS', 'START_THE_RACE');
@@ -115,4 +129,39 @@ describe('keepKeys', () => {
expect(actionType.RESET).toBe('HOUSE_CATS__START_THE_RACE__RESET');
});
});
describe('getModuleState', () => {
const state = {
first: { red: { awesome: 'sauce' }, blue: { weak: 'sauce' } },
second: { other: 'data' },
};
it('should return everything if given an empty path', () => {
expect(getModuleState(state, [])).toEqual(state);
});
it('should resolve paths correctly', () => {
expect(getModuleState(
state,
['first'],
)).toEqual({ red: { awesome: 'sauce' }, blue: { weak: 'sauce' } });
expect(getModuleState(
state,
['first', 'red'],
)).toEqual({ awesome: 'sauce' });
expect(getModuleState(state, ['second'])).toEqual({ other: 'data' });
});
it('should throw an exception on a bad path', () => {
expect(() => {
getModuleState(state, ['uhoh']);
}).toThrowErrorMatchingSnapshot();
});
it('should return non-objects correctly', () => {
expect(getModuleState(state, ['first', 'red', 'awesome'])).toEqual('sauce');
});
});
});

View File

@@ -1,93 +1,131 @@
import React, { Component } from 'react';
import { connect, Provider } from 'react-redux';
import PropTypes from 'prop-types';
import { IntlProvider, injectIntl, intlShape } from 'react-intl';
import { Route, Switch } from 'react-router-dom';
import { ConnectedRouter } from 'connected-react-router';
import { sendTrackEvent } from '@edx/frontend-analytics';
import { IntlProvider, injectIntl, intlShape, getMessages } from '@edx/frontend-i18n';
import SiteHeader from '@edx/frontend-component-site-header';
import SiteFooter from '@edx/frontend-component-footer';
import { getLocale, getMessages } from '@edx/frontend-i18n'; // eslint-disable-line
import { PageLoading, fetchUserAccount } from '../common';
import {
faFacebookSquare,
faTwitterSquare,
faYoutubeSquare,
faLinkedin,
faRedditSquare,
} from '@fortawesome/free-brands-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { ErrorBoundary, fetchUserAccount } from '../common';
import { ConnectedAccountSettingsPage } from '../account-settings';
import FooterLogo from '../assets/edx-footer.png';
import HeaderLogo from '../assets/logo.svg';
import ErrorPage from './ErrorPage';
import NotFoundPage from './NotFoundPage';
import messages from './App.messages';
import WelcomePage from './WelcomePage';
function PageContent({
ready,
configuration,
username,
avatar,
intl,
}) {
if (!ready) {
return <PageLoading srMessage={intl.formatMessage(messages['app.loading.message'])} />;
}
const mainMenu = [
{
type: 'item',
href: `${process.env.MARKETING_SITE_BASE_URL}/course`,
href: `${configuration.LMS_BASE_URL}/dashboard`,
content: intl.formatMessage(messages['siteheader.links.courses']),
},
{
type: 'item',
href: `${process.env.MARKETING_SITE_BASE_URL}/course?program=all`,
href: `${configuration.LMS_BASE_URL}/dashboard/programs`,
content: intl.formatMessage(messages['siteheader.links.programs']),
},
{
type: 'item',
href: `${process.env.MARKETING_SITE_BASE_URL}/schools-partners`,
content: intl.formatMessage(messages['siteheader.links.schools']),
href: `${configuration.MARKETING_SITE_BASE_URL}/course`,
content: intl.formatMessage(messages['siteheader.links.content.search']),
onClick: () => {
sendTrackEvent(
'edx.bi.dashboard.find_courses_button.clicked',
{ category: 'account', label: 'header' },
);
},
},
];
const userMenu = [
{
type: 'item',
href: `${process.env.LMS_BASE_URL}`,
href: `${configuration.LMS_BASE_URL}`,
content: intl.formatMessage(messages['siteheader.user.menu.dashboard']),
},
{
type: 'item',
href: `${process.env.LMS_BASE_URL}/u/${username}`,
href: `${configuration.LMS_BASE_URL}/u/${username}`,
content: intl.formatMessage(messages['siteheader.user.menu.profile']),
},
{
type: 'item',
href: `${process.env.LMS_BASE_URL}/account/settings`,
href: `${configuration.LMS_BASE_URL}/account/settings`,
content: intl.formatMessage(messages['siteheader.user.menu.account.settings']),
},
{
type: 'item',
href: process.env.ORDER_HISTORY_URL,
href: configuration.ORDER_HISTORY_URL,
content: intl.formatMessage(messages['siteheader.user.menu.order.history']),
},
{
type: 'item',
href: process.env.LOGOUT_URL,
href: configuration.LOGOUT_URL,
content: intl.formatMessage(messages['siteheader.user.menu.logout']),
},
];
const loggedOutItems = [
{
type: 'item',
href: `${process.env.LMS_BASE_URL}/login`,
href: `${configuration.LMS_BASE_URL}/login`,
content: intl.formatMessage(messages['siteheader.user.menu.login']),
},
{
type: 'item',
href: `${process.env.LMS_BASE_URL}/register`,
href: `${configuration.LMS_BASE_URL}/register`,
content: intl.formatMessage(messages['siteheader.user.menu.register']),
},
];
const socialLinks = [
{
title: 'Facebook',
url: configuration.FACEBOOK_URL,
icon: <FontAwesomeIcon icon={faFacebookSquare} className="social-icon" size="2x" />,
screenReaderText: 'Like edX on Facebook',
},
{
title: 'Twitter',
url: configuration.TWITTER_URL,
icon: <FontAwesomeIcon icon={faTwitterSquare} className="social-icon" size="2x" />,
screenReaderText: 'Follow edX on Twitter',
},
{
title: 'Youtube',
url: configuration.YOU_TUBE_URL,
icon: <FontAwesomeIcon icon={faYoutubeSquare} className="social-icon" size="2x" />,
screenReaderText: 'Subscribe to the edX YouTube channel',
},
{
title: 'LinkedIn',
url: configuration.LINKED_IN_URL,
icon: <FontAwesomeIcon icon={faLinkedin} className="social-icon" size="2x" />,
screenReaderText: 'Follow edX on LinkedIn',
},
{
title: 'Reddit',
url: configuration.REDDIT_URL,
icon: <FontAwesomeIcon icon={faRedditSquare} className="social-icon" size="2x" />,
screenReaderText: 'Subscribe to the edX subreddit',
},
];
return (
<div id="app">
@@ -97,17 +135,15 @@ function PageContent({
username={username}
avatar={avatar}
logoAltText={configuration.SITE_NAME}
logoDestination={configuration.MARKETING_SITE_BASE_URL}
logoDestination={`${configuration.LMS_BASE_URL}/dashboard`}
mainMenu={mainMenu}
userMenu={userMenu}
loggedOutItems={loggedOutItems}
/>
<main>
<Switch>
<Route path="/account-settings" component={ConnectedAccountSettingsPage} />
<Route path="/error" component={ErrorPage} />
<Route exact path="/" component={ConnectedAccountSettingsPage} />
<Route path="/notfound" component={NotFoundPage} />
<Route path="/" component={WelcomePage} />
<Route path="*" component={NotFoundPage} />
</Switch>
</main>
@@ -120,49 +156,23 @@ function PageContent({
openSourceUrl={configuration.OPEN_SOURCE_URL}
termsOfServiceUrl={configuration.TERMS_OF_SERVICE_URL}
privacyPolicyUrl={configuration.PRIVACY_POLICY_URL}
facebookUrl={configuration.FACEBOOK_URL}
twitterUrl={configuration.TWITTER_URL}
youTubeUrl={configuration.YOU_TUBE_URL}
linkedInUrl={configuration.LINKED_IN_URL}
googlePlusUrl={configuration.GOOGLE_PLUS_URL}
redditUrl={configuration.REDDIT_URL}
appleAppStoreUrl={configuration.APPLE_APP_STORE_URL}
googlePlayUrl={configuration.GOOGLE_PLAY_URL}
socialLinks={socialLinks}
enterpriseMarketingLink={{
url: configuration.ENTERPRISE_MARKETING_URL,
queryParams: {
utm_source: configuration.ENTERPRISE_MARKETING_UTM_SOURCE,
utm_campaign: configuration.ENTERPRISE_MARKETING_UTM_CAMPAIGN,
utm_medium: configuration.ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM,
},
}}
handleAllTrackEvents={sendTrackEvent}
/>
</div>
);
}
PageContent.propTypes = {
username: PropTypes.string.isRequired,
avatar: PropTypes.string,
ready: PropTypes.bool,
configuration: PropTypes.shape({
SITE_NAME: PropTypes.string.isRequired,
MARKETING_SITE_BASE_URL: PropTypes.string.isRequired,
SUPPORT_URL: PropTypes.string.isRequired,
CONTACT_URL: PropTypes.string.isRequired,
OPEN_SOURCE_URL: PropTypes.string.isRequired,
TERMS_OF_SERVICE_URL: PropTypes.string.isRequired,
PRIVACY_POLICY_URL: PropTypes.string.isRequired,
FACEBOOK_URL: PropTypes.string.isRequired,
TWITTER_URL: PropTypes.string.isRequired,
YOU_TUBE_URL: PropTypes.string.isRequired,
LINKED_IN_URL: PropTypes.string.isRequired,
GOOGLE_PLUS_URL: PropTypes.string.isRequired,
REDDIT_URL: PropTypes.string.isRequired,
APPLE_APP_STORE_URL: PropTypes.string.isRequired,
GOOGLE_PLAY_URL: PropTypes.string.isRequired,
}).isRequired,
intl: intlShape.isRequired,
};
PageContent.defaultProps = {
ready: false,
avatar: null,
};
const IntlPageContent = injectIntl(PageContent);
class App extends Component {
@@ -173,59 +183,76 @@ class App extends Component {
render() {
return (
<IntlProvider locale={getLocale()} messages={getMessages()}>
<Provider store={this.props.store}>
<ConnectedRouter history={this.props.history}>
<IntlPageContent
ready={this.props.ready}
configuration={this.props.configuration}
username={this.props.username}
avatar={this.props.avatar}
/>
</ConnectedRouter>
</Provider>
</IntlProvider>
<ErrorBoundary>
<IntlProvider locale={this.props.locale} messages={getMessages()}>
<Provider store={this.props.store}>
<ConnectedRouter history={this.props.history}>
<IntlPageContent
configuration={this.props.configuration}
username={this.props.username}
avatar={this.props.avatar}
/>
</ConnectedRouter>
</Provider>
</IntlProvider>
</ErrorBoundary>
);
}
}
const configurationPropTypes = {
SITE_NAME: PropTypes.string.isRequired,
LMS_BASE_URL: PropTypes.string.isRequired,
LOGOUT_URL: PropTypes.string.isRequired,
MARKETING_SITE_BASE_URL: PropTypes.string.isRequired,
SUPPORT_URL: PropTypes.string.isRequired,
CONTACT_URL: PropTypes.string.isRequired,
OPEN_SOURCE_URL: PropTypes.string.isRequired,
TERMS_OF_SERVICE_URL: PropTypes.string.isRequired,
PRIVACY_POLICY_URL: PropTypes.string.isRequired,
FACEBOOK_URL: PropTypes.string.isRequired,
TWITTER_URL: PropTypes.string.isRequired,
YOU_TUBE_URL: PropTypes.string.isRequired,
LINKED_IN_URL: PropTypes.string.isRequired,
REDDIT_URL: PropTypes.string.isRequired,
APPLE_APP_STORE_URL: PropTypes.string.isRequired,
GOOGLE_PLAY_URL: PropTypes.string.isRequired,
ORDER_HISTORY_URL: PropTypes.string.isRequired,
ENTERPRISE_MARKETING_URL: PropTypes.string.isRequired,
ENTERPRISE_MARKETING_UTM_SOURCE: PropTypes.string.isRequired,
ENTERPRISE_MARKETING_UTM_CAMPAIGN: PropTypes.string.isRequired,
ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM: PropTypes.string.isRequired,
};
PageContent.propTypes = {
username: PropTypes.string.isRequired,
avatar: PropTypes.string,
configuration: PropTypes.shape(configurationPropTypes).isRequired,
intl: intlShape.isRequired,
};
PageContent.defaultProps = {
avatar: null,
};
App.propTypes = {
fetchUserAccount: PropTypes.func.isRequired,
username: PropTypes.string.isRequired,
avatar: PropTypes.string,
store: PropTypes.object.isRequired, // eslint-disable-line
history: PropTypes.object.isRequired, // eslint-disable-line
ready: PropTypes.bool,
configuration: PropTypes.shape({
SITE_NAME: PropTypes.string.isRequired,
MARKETING_SITE_BASE_URL: PropTypes.string.isRequired,
SUPPORT_URL: PropTypes.string.isRequired,
CONTACT_URL: PropTypes.string.isRequired,
OPEN_SOURCE_URL: PropTypes.string.isRequired,
TERMS_OF_SERVICE_URL: PropTypes.string.isRequired,
PRIVACY_POLICY_URL: PropTypes.string.isRequired,
FACEBOOK_URL: PropTypes.string.isRequired,
TWITTER_URL: PropTypes.string.isRequired,
YOU_TUBE_URL: PropTypes.string.isRequired,
LINKED_IN_URL: PropTypes.string.isRequired,
GOOGLE_PLUS_URL: PropTypes.string.isRequired,
REDDIT_URL: PropTypes.string.isRequired,
APPLE_APP_STORE_URL: PropTypes.string.isRequired,
GOOGLE_PLAY_URL: PropTypes.string.isRequired,
}).isRequired,
locale: PropTypes.string.isRequired,
configuration: PropTypes.shape(configurationPropTypes).isRequired,
};
App.defaultProps = {
ready: false,
avatar: null,
};
const mapStateToProps = state => ({
username: state.authentication.username,
// An error means that we tried to load the user account and failed,
// which also means we're ready to display something.
ready: state.userAccount.loaded || state.userAccount.error != null,
configuration: state.configuration,
locale: state.i18n.locale,
avatar: state.userAccount.profileImage.hasImage
? state.userAccount.profileImage.imageUrlMedium
: null,

View File

@@ -1,20 +1,20 @@
import { defineMessages } from 'react-intl';
import { defineMessages } from '@edx/frontend-i18n';
const messages = defineMessages({
'siteheader.links.courses': {
id: 'siteheader.links.courses',
defaultMessage: 'Courses',
description: 'Link to the course catalog',
description: 'Link to the learner course dashboard',
},
'siteheader.links.programs': {
id: 'siteheader.links.programs',
defaultMessage: 'Programs & Degrees',
description: 'Link to the programs catalog',
defaultMessage: 'Programs',
description: 'Link to the learner program dashboard',
},
'siteheader.links.schools': {
id: 'siteheader.links.schools',
defaultMessage: 'Schools & Partners',
description: 'Link to the schools and partners landing page',
'siteheader.links.content.search': {
id: 'siteheader.links.content.search',
defaultMessage: 'Discover New',
description: 'Link to the content search page',
},
'siteheader.user.menu.dashboard': {
id: 'siteheader.user.menu.dashboard',
@@ -53,7 +53,7 @@ const messages = defineMessages({
},
'app.loading.message': {
id: 'app.loading.message',
defaultMessage: 'Loading',
defaultMessage: 'Loading...',
description: 'Message shown when page content is loading.',
},
});

View File

@@ -1,20 +0,0 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
export default function ErrorPage() {
return (
<div className="container-fluid py-5 justify-content-center align-items-start text-center">
<div className="row">
<div className="col">
<p className="my-0 py-5 text-muted">
<FormattedMessage
id="error.unexpected.message"
defaultMessage="An unexpected error occurred."
description="error message when an unexpected error occurs"
/>
</p>
</div>
</div>
</div>
);
}

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { FormattedMessage } from '@edx/frontend-i18n';
export default function NotFoundPage() {
return (

View File

@@ -1,16 +0,0 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
export default function WelcomePage() {
return (
<div className="py-5 justify-content-center align-items-start text-center">
<p className="my-0 pt-5 text-muted">
<FormattedMessage
id="app.page"
defaultMessage="Congratulations! You have a new micro-frontend."
description="Default page content for a new frontend application"
/>
</p>
</div>
);
}

View File

@@ -23,7 +23,6 @@ export const configuration = {
TWITTER_URL: process.env.TWITTER_URL,
YOU_TUBE_URL: process.env.YOU_TUBE_URL,
LINKED_IN_URL: process.env.LINKED_IN_URL,
GOOGLE_PLUS_URL: process.env.GOOGLE_PLUS_URL,
REDDIT_URL: process.env.REDDIT_URL,
APPLE_APP_STORE_URL: process.env.APPLE_APP_STORE_URL,
GOOGLE_PLAY_URL: process.env.GOOGLE_PLAY_URL,
@@ -36,7 +35,13 @@ export const configuration = {
CERTIFICATES_API_BASE_URL: `${process.env.LMS_BASE_URL}/api/certificates/v0/certificates`,
VIEW_MY_RECORDS_URL: `${process.env.CREDENTIALS_BASE_URL}/records`,
ECOMMERCE_API_BASE_URL: `${process.env.ECOMMERCE_BASE_URL}/api/v2`,
ORDER_HISTORY_URL: process.env.ORDER_HISTORY_URL,
DELETE_ACCOUNT_URL: `${process.env.LMS_BASE_URL}/api/user/v1/accounts/deactivate_logout/`,
PASSWORD_RESET_URL: `${process.env.LMS_BASE_URL}/password_reset/`,
ENTERPRISE_MARKETING_URL: process.env.ENTERPRISE_MARKETING_URL,
ENTERPRISE_MARKETING_UTM_SOURCE: process.env.ENTERPRISE_MARKETING_UTM_SOURCE,
ENTERPRISE_MARKETING_UTM_CAMPAIGN: process.env.ENTERPRISE_MARKETING_UTM_CAMPAIGN,
ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM: process.env.ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM,
};
export const features = {};

View File

@@ -1,63 +0,0 @@
#!/usr/bin/env node
/**
* See the Makefile for how the required hash file is downloaded from Transifex.
*/
const fs = require('fs');
const glob = require('glob');
const path = require('path');
// Expected input: a directory, possibly containing subdirectories, with .json files. Each .json
// file is an array of translation triplets (id, description, defaultMessage).
function gatherJson(dir) {
const ret = [];
const files = glob.sync(`${dir}/**/*.json`);
files.forEach((filename) => {
const messages = JSON.parse(fs.readFileSync(filename));
ret.push(...messages);
});
return ret;
}
// the hash file returns ids whose periods are "escaped" (sort of), like this:
// "key": "profile\\.sociallinks\\.social\\.links"
// so our regular messageIds won't match them out of the box
function escapeDots(messageId) {
return messageId.replace(/\./g, '\\.');
}
const jsonDir = process.argv[2];
const messageObjects = gatherJson(jsonDir);
if (process.argv[3] === '--comments') { // prepare to handle the translator notes
const loggingPrefix = path.basename(`${__filename}`); // the name of this JS file
const bashScriptsPath = './node_modules/reactifex/bash_scripts';
const hashFile = `${bashScriptsPath}/hashmap.json`;
process.stdout.write(`${loggingPrefix}: reading hash file ${hashFile}\n`);
const messageInfo = JSON.parse(fs.readFileSync(hashFile));
const outputFile = `${bashScriptsPath}/hashed_data.txt`;
process.stdout.write(`${loggingPrefix}: writing to output file ${outputFile}\n`);
fs.writeFileSync(outputFile, '');
messageObjects.forEach((message) => {
const transifexFormatId = escapeDots(message.id);
const info = messageInfo.find(mi => mi.key === transifexFormatId);
if (info) {
fs.appendFileSync(outputFile, `${info.string_hash}|${message.description}\n`);
} else {
process.stdout.write(`${loggingPrefix}: string ${message.id} does not yet exist on transifex!\n`);
}
});
} else {
const output = {};
messageObjects.forEach((message) => {
output[message.id] = message.defaultMessage;
});
fs.writeFileSync(process.argv[3], JSON.stringify(output, null, 2));
}

View File

@@ -1,165 +0,0 @@
/**
* For each locale we want to support, react-intl needs 1) the locale-data, which includes
* information about how to format numbers, handle plurals, etc., and 2) the translations, as an
* object holding message id / translated string pairs. A locale string and the messages object are
* passed into the IntlProvider element that wraps your element hierarchy.
*
* Note that react-intl has no way of checking if the translations you give it actually have
* anything to do with the locale you pass it; it will happily use whatever messages object you pass
* in. However, if the locale data for the locale you passed into the IntlProvider was not
* correctly installed with addLocaleData, all of your translations will fall back to the default
* (in our case English), *even if you gave IntlProvider the correct messages object for that
* locale*.
*/
import { addLocaleData } from 'react-intl';
import Cookies from 'universal-cookie';
import arLocale from 'react-intl/locale-data/ar';
import enLocale from 'react-intl/locale-data/en';
import es419Locale from 'react-intl/locale-data/es';
import frLocale from 'react-intl/locale-data/fr';
import zhcnLocale from 'react-intl/locale-data/zh';
import COUNTRIES, { langs as countryLangs } from 'i18n-iso-countries';
import LANGUAGES, { langs as languageLangs } from '@cospired/i18n-iso-languages';
import arMessages from './messages/ar.json';
// no need to import en messages-- they are in the defaultMessage field
import es419Messages from './messages/es_419.json';
import frMessages from './messages/fr.json';
import zhcnMessages from './messages/zh_CN.json';
addLocaleData([...arLocale, ...enLocale, ...es419Locale, ...frLocale, ...zhcnLocale]);
// TODO: When we start dynamically loading translations only for the current locale, change this.
COUNTRIES.registerLocale(require('i18n-iso-countries/langs/ar.json'));
COUNTRIES.registerLocale(require('i18n-iso-countries/langs/en.json'));
COUNTRIES.registerLocale(require('i18n-iso-countries/langs/es.json'));
COUNTRIES.registerLocale(require('i18n-iso-countries/langs/fr.json'));
COUNTRIES.registerLocale(require('i18n-iso-countries/langs/zh.json'));
// TODO: When we start dynamically loading translations only for the current locale, change this.
// TODO: Also note that Arabic (ar) and Chinese (zh) are missing here. That's because they're
// not implemented in this library. If you read this and it's been a while, go check and see
// if that's changed!
LANGUAGES.registerLocale(require('@cospired/i18n-iso-languages/langs/en.json'));
LANGUAGES.registerLocale(require('@cospired/i18n-iso-languages/langs/es.json'));
LANGUAGES.registerLocale(require('@cospired/i18n-iso-languages/langs/fr.json'));
const messages = { // current fallback strategy is to use the first two letters of the locale code
ar: arMessages,
es: es419Messages,
fr: frMessages,
zh: zhcnMessages,
};
const cookies = new Cookies();
const getTwoLetterLanguageCode = code => code.substr(0, 2);
// Get the locale by setting priority. Skip if we don't support that language.
const getLocale = (localeStr) => {
// 1. Explicit application request
if (localeStr && messages[localeStr] !== undefined) {
return localeStr;
}
// 2. User setting in cookie
const cookieLangPref = cookies.get(process.env.LANGUAGE_PREFERENCE_COOKIE_NAME);
if (cookieLangPref && messages[getTwoLetterLanguageCode(cookieLangPref)] !== undefined) {
return getTwoLetterLanguageCode(cookieLangPref);
}
// 3. Browser language (default)
return getTwoLetterLanguageCode(window.navigator.language);
};
const getMessages = (locale = getLocale()) => messages[locale];
const rtlLocales = ['ar', 'he', 'fa', 'ur'];
const isRtl = locale => rtlLocales.includes(locale);
const handleRtl = () => {
if (isRtl(getLocale())) {
document.getElementsByTagName('html')[0].setAttribute('dir', 'rtl');
document.styleSheets[0].disabled = true;
} else {
document.styleSheets[1].disabled = true;
}
};
/**
* Provides a lookup table of country IDs to country names for the current locale.
*/
const getCountryMessages = (locale) => {
const finalLocale = countryLangs().includes(locale) ? locale : 'en';
return COUNTRIES.getNames(finalLocale);
};
/**
* Provides a lookup table of language IDs to language names for the current locale.
*/
const getLanguageMessages = (locale) => {
const finalLocale = languageLangs().includes(locale) ? locale : 'en';
return LANGUAGES.getNames(finalLocale);
};
const sortFunction = (a, b) => {
// If localeCompare exists, use that. (Not supported in some older browsers)
if (typeof String.prototype.localeCompare === 'function') {
return a[1].localeCompare(b[1], getLocale());
}
if (a[1] === b[1]) {
return 0;
}
// Otherwise make a best effort.
return a[1] > b[1] ? 1 : -1;
};
/**
* Provides a list of countries represented as objects of the following shape:
*
* {
* key, // The ID of the country
* name // The localized name of the country
* }
*
* The list is sorted alphabetically in the current locale.
* This is useful for select dropdowns primarily.
*/
const getCountryList = (locale) => {
const countryMessages = getCountryMessages(locale);
return Object.entries(countryMessages)
.sort(sortFunction)
.map(([code, name]) => ({ code, name }));
};
/**
* Provides a list of languages represented as objects of the following shape:
*
* {
* key, // The ID of the language
* name // The localized name of the language
* }
*
* The list is sorted alphabetically in the current locale.
* This is useful for select dropdowns primarily.
*/
const getLanguageList = (locale) => {
const languageMessages = getLanguageMessages(locale);
return Object.entries(languageMessages)
.sort(sortFunction)
.map(([code, name]) => ({ code, name }));
};
export {
getCountryList,
getCountryMessages,
getLanguageList,
getLanguageMessages,
getLocale,
getMessages,
handleRtl,
isRtl,
};

32
src/i18n/index.js Normal file
View File

@@ -0,0 +1,32 @@
import arMessages from './messages/ar.json';
import caMessages from './messages/ca.json';
// no need to import en messages-- they are in the defaultMessage field
import es419Messages from './messages/es_419.json';
import frMessages from './messages/fr.json';
import zhcnMessages from './messages/zh_CN.json';
import heMessages from './messages/he.json';
import idMessages from './messages/id.json';
import kokrMessages from './messages/ko_kr.json';
import plMessages from './messages/pl.json';
import ptbrMessages from './messages/pt_br.json';
import ruMessages from './messages/ru.json';
import thMessages from './messages/th.json';
import ukMessages from './messages/uk.json';
const messages = {
ar: arMessages,
'es-419': es419Messages,
fr: frMessages,
'zh-cn': zhcnMessages,
ca: caMessages,
he: heMessages,
id: idMessages,
'ko-kr': kokrMessages,
pl: plMessages,
'pt-br': ptbrMessages,
ru: ruMessages,
th: thMessages,
uk: ukMessages,
};
export default messages;

View File

@@ -1,25 +0,0 @@
import { intlShape } from 'react-intl';
import injectIntlWithShim from './injectIntlWithShim';
import {
getCountryList,
getCountryMessages,
getLanguageList,
getLanguageMessages,
getLocale,
getMessages,
handleRtl,
isRtl,
} from './i18n-loader';
export {
injectIntlWithShim as injectIntl,
getCountryList,
getCountryMessages,
getLanguageList,
getLanguageMessages,
getLocale,
getMessages,
handleRtl,
isRtl,
intlShape,
};

View File

@@ -1,41 +0,0 @@
import React from 'react';
import { injectIntl, intlShape } from 'react-intl';
import LoggingService from '@edx/frontend-logging';
const injectIntlWithShim = (WrappedComponent) => {
class ShimmedIntlComponent extends React.Component {
static propTypes = {
intl: intlShape.isRequired,
};
constructor(props) {
super(props);
this.shimmedIntl = Object.create(this.props.intl, {
formatMessage: {
value: (definition, ...args) => {
if (definition === undefined || definition.id === undefined) {
const error = new Error('i18n error: An undefined message was supplied to intl.formatMessage.');
if (process.env.NODE_ENV !== 'production') {
console.error(error); // eslint-disable-line no-console
return '!!! Missing message supplied to intl.formatMessage !!!';
}
LoggingService.logError(error);
return ''; // Fail silent in production
}
return this.props.intl.formatMessage(definition, ...args);
},
},
});
}
render() {
return <WrappedComponent {...this.props} intl={this.shimmedIntl} />;
}
}
return injectIntl(ShimmedIntlComponent);
};
export default injectIntlWithShim;

View File

@@ -1,11 +1,126 @@
{
"account.settings.message.duplicate.tpa.provider": "The {provider} account you selected is already linked to another edX account.",
"account.settings.message.managed.settings": "Your profile settings are managed by {managerTitle}. Contact your administrator or {support} for help.",
"account.settings.message.managed.settings.support": "support",
"account.settings.page.heading": "Account Settings",
"account.settings.loading.message": "Loading...",
"account.settings.loading.error": "Error: {error}",
"account.settings.banner.beta.language": "You have set your language to {beta_language}, which is currently not fully translated. You can help us translate this language fully by joining the Transifex community and adding translations from English for learners that speak {beta_language}.",
"account.settings.banner.beta.language.action.switch.back": "Switch Back to {previous_language}",
"account.settings.banner.beta.language.action.help.translate": "Help Translate into {beta_language}",
"account.settings.section.account.information": "Account Information",
"account.settings.section.account.information.description": "These settings include basic information about your account.",
"account.settings.section.profile.information": "Profile Information",
"account.settings.section.site.preferences": "Site Preferences",
"account.settings.section.linked.accounts": "Linked Accounts",
"account.settings.section.linked.accounts.description": "You can link your identity accounts to simplify signing in to edX.",
"account.settings.field.username": "Username",
"account.settings.field.username.help.text": "The name that identifies you on edX. You cannot change your username.",
"account.settings.field.full.name": "Full name",
"account.settings.field.full.name.empty": "Add name",
"account.settings.field.full.name.help.text": "The name that is used for ID verification and that appears on your certificates.",
"account.settings.field.email": "Email address (Sign in)",
"account.settings.field.email.empty": "Add email address",
"account.settings.field.email.confirmation": "Weve sent a confirmation message to {value}. Click the link in the message to update your email address.",
"account.settings.field.email.help.text": "You receive messages from edX and course teams at this address.",
"account.settings.field.secondary.email": "Recovery email address",
"account.settings.field.secondary.email.empty": "Add a recovery email address",
"account.settings.field.secondary.email.confirmation": "Weve sent a confirmation message to {value}. Click the link in the message to update your recovery email address.",
"account.settings.email.field.confirmation.header": "Pending confirmation",
"account.settings.field.dob": "Year of birth",
"account.settings.field.dob.empty": "Add year of birth",
"account.settings.field.year_of_birth.options.empty": "Select a year of birth",
"account.settings.field.country": "Country",
"account.settings.field.country.empty": "Add country",
"account.settings.field.country.options.empty": "Select a Country",
"account.settings.field.site.language": "Site language",
"account.settings.field.site.language.help.text": "The language used throughout this site. This site is currently available in a limited number of languages.",
"account.settings.field.education": "Education",
"account.settings.field.education.empty": "Add level of education",
"account.settings.field.education.levels.empty": "Select a level of education",
"account.settings.field.education.levels.p": "Doctorate",
"account.settings.field.education.levels.m": "Master's or professional degree",
"account.settings.field.education.levels.b": "Bachelor's Degree",
"account.settings.field.education.levels.a": "Associate's degree",
"account.settings.field.education.levels.hs": "Secondary/high school",
"account.settings.field.education.levels.jhs": "Junior secondary/junior high/middle school",
"account.settings.field.education.levels.el": "Elementary/primary school",
"account.settings.field.education.levels.none": "No formal education",
"account.settings.field.education.levels.o": "Other education",
"account.settings.field.gender": "Gender",
"account.settings.field.gender.empty": "Add gender",
"account.settings.field.gender.options.empty": "Select a gender",
"account.settings.field.gender.options.f": "Female",
"account.settings.field.gender.options.m": "Male",
"account.settings.field.gender.options.o": "Other",
"account.settings.field.language.proficiencies": "Spoken languages",
"account.settings.field.language.proficiencies.empty": "Add a spoken language",
"account.settings.field.language_proficiencies.options.empty": "Select a Language",
"account.settings.field.time.zone": "Time zone",
"account.settings.field.time.zone.empty": "Set time zone",
"account.settings.field.time.zone.description": "Select the time zone for displaying course dates. If you do not specify a time zone, course dates, including assignment deadlines, will be displayed in your browsers local time zone.",
"account.settings.field.time.zone.default": "Default (Local Time Zone)",
"account.settings.field.time.zone.all": "All time zones",
"account.settings.field.time.zone.country": "Country time zones",
"account.settings.section.social.media": "Social Media Links",
"account.settings.section.social.media.description": "Optionally, link your personal accounts to the social media icons on your edX profile.",
"account.settings.field.social.platform.name.linkedin": "LinkedIn",
"account.settings.field.social.platform.name.linkedin.empty": "Add LinkedIn profile",
"account.settings.jump.nav.delete.account": "Delete My Account",
"account.settings.field.social.platform.name.twitter": "Twitter",
"account.settings.field.social.platform.name.twitter.empty": "Add Twitter profile",
"account.settings.field.social.platform.name.facebook": "Facebook",
"account.settings.field.social.platform.name.facebook.empty": "Add Facebook profile",
"account.settings.editable.field.action.save": "Save",
"account.settings.editable.field.action.cancel": "Cancel",
"account.settings.editable.field.action.edit": "Edit",
"account.settings.static.field.empty": "No value set. Contact your {enterprise} administrator to make changes.",
"account.settings.static.field.empty.no.admin": "No value set.",
"account.settings.delete.account.before.proceeding": "Before proceeding, please {actionLink}.",
"account.settings.delete.account.header": "Delete My Account",
"account.settings.delete.account.subheader": "We're sorry to see you go!",
"account.settings.delete.account.text.1": "Please note: Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.",
"account.settings.delete.account.text.2": "Once your account is deleted, you cannot use it to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employers or universitys system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.",
"account.settings.delete.account.text.3.link": "follow the instructions for printing or downloading a certificate",
"account.settings.delete.account.text.warning": "Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on edX.",
"account.settings.delete.account.text.change.instead": "Want to change your email, name, or password instead?",
"account.settings.delete.account.button": "Delete My Account",
"account.settings.delete.account.please.activate": "activate your account",
"account.settings.delete.account.please.unlink": "unlink all social media accounts",
"account.settings.delete.account.modal.header": "Are you sure?",
"account.settings.delete.account.modal.text.1": "You have selected \"Delete My Account\". Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.",
"account.settings.delete.account.modal.text.2": "If you proceed, you will be unable to use this account to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employer's or university's system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.",
"account.settings.delete.account.modal.enter.password": "If you still wish to continue and delete your account, please enter your account password:",
"account.settings.delete.account.modal.confirm.delete": "Yes, Delete",
"account.settings.delete.account.modal.confirm.cancel": "Cancel",
"account.settings.delete.account.error.unable.to.delete": "Unable to delete account",
"account.settings.delete.account.error.no.password": "A password is required",
"account.settings.delete.account.error.unable.to.delete.details": "Sorry, there was an error trying to process your request. Please try again later.",
"account.settings.delete.account.modal.after.header": "We're sorry to see you go! Your account will be deleted shortly.",
"account.settings.delete.account.modal.after.text": "Account deletion, including removal from email lists, may take a few weeks to fully process through our system. If you want to opt-out of emails before then, please unsubscribe from the footer of any email.",
"account.settings.delete.account.modal.after.button": "Close",
"account.settings.delete.account.text.3": "You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion, {actionLink}.",
"account.settings.editable.field.password.reset.button.confirmation.support.link": "technical support",
"account.settings.editable.field.password.reset.button.confirmation": "We've sent a message to {email}. Click the link in the message to reset your password. Didn't receive the message? Contact {technicalSupportLink}.",
"account.settings.editable.field.password.reset.button": "Reset Password",
"account.settings.editable.field.password.reset.label": "Password",
"account.settings.sso.link.account": "Sign in with {name}",
"account.settings.sso.account.connected": "Linked",
"account.settings.sso.account.disconnect.error": "There was a problem disconnecting this account. Contact support if the problem persists.",
"account.settings.sso.unlink.account": "Unlink {name} account",
"account.settings.sso.no.providers": "No accounts can be linked at this time.",
"unexpected.error.message.text": "An unexpected error occurred. Please click the button below to return to refresh the page.",
"unexpected.error.button.text": "Try Again",
"siteheader.links.courses": "Courses",
"siteheader.links.programs": "Programs & Degrees",
"siteheader.links.schools": "Schools & Partners",
"siteheader.links.programs": "Programs",
"siteheader.links.content.search": "Discover New",
"siteheader.user.menu.dashboard": "Dashboard",
"siteheader.user.menu.profile": "Profile",
"siteheader.user.menu.account.settings": "Account",
"siteheader.user.menu.order.history": "Order History",
"siteheader.user.menu.logout": "Logout",
"siteheader.user.menu.login": "Login",
"siteheader.user.menu.register": "Sign Up"
"siteheader.user.menu.register": "Sign Up",
"app.loading.message": "Loading...",
"error.notfound.message": "The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again."
}

11
src/i18n/messages/ca.json Normal file
View File

@@ -0,0 +1,11 @@
{
"siteheader.links.courses": "Courses",
"siteheader.links.programs": "Programs & Degrees",
"siteheader.links.schools": "Schools & Partners",
"siteheader.user.menu.dashboard": "Dashboard",
"siteheader.user.menu.profile": "Profile",
"siteheader.user.menu.account.settings": "Account",
"siteheader.user.menu.logout": "Logout",
"siteheader.user.menu.login": "Login",
"siteheader.user.menu.register": "Sign Up"
}

View File

@@ -1,11 +1,126 @@
{
"account.settings.message.duplicate.tpa.provider": "La cuenta de {provider} seleccionada ya está vinculada con otra cuenta de edX. ",
"account.settings.message.managed.settings": "Your profile settings are managed by {managerTitle}. Contact your administrator or {support} for help.",
"account.settings.message.managed.settings.support": "support",
"account.settings.page.heading": "Configuración de cuenta",
"account.settings.loading.message": "Cargando...",
"account.settings.loading.error": "Error: {error}",
"account.settings.banner.beta.language": "Ha establecido su idioma en {beta_language}, el cual no está traducido completamente. Puede ayudarnos a traducir este idioma totalmente uniéndose la comunidad Transifex y adicionando traducciones desde el Inglés para los estudiantes que hablan {beta_language}.",
"account.settings.banner.beta.language.action.switch.back": "Switch Back to {previous_language}",
"account.settings.banner.beta.language.action.help.translate": "Ayude a traducir a {beta_language}",
"account.settings.section.account.information": "Información de la cuenta",
"account.settings.section.account.information.description": "Estas configuraciones incluyen información básica sobre tu cuenta.",
"account.settings.section.profile.information": "Profile Information",
"account.settings.section.site.preferences": "Site Preferences",
"account.settings.section.linked.accounts": "Cuentas vinculadas",
"account.settings.section.linked.accounts.description": "You can link your identity accounts to simplify signing in to edX.",
"account.settings.field.username": "Nombre de usuario",
"account.settings.field.username.help.text": "The name that identifies you on edX. You cannot change your username.",
"account.settings.field.full.name": "Nombre completo",
"account.settings.field.full.name.empty": "Añade nombre",
"account.settings.field.full.name.help.text": "El nombre que es usado para la verificación de identidad y aparece en sus certificados.",
"account.settings.field.email": "Email address (Sign in)",
"account.settings.field.email.empty": "Add email address",
"account.settings.field.email.confirmation": "Weve sent a confirmation message to {value}. Click the link in the message to update your email address.",
"account.settings.field.email.help.text": "You receive messages from edX and course teams at this address.",
"account.settings.field.secondary.email": "Recovery email address",
"account.settings.field.secondary.email.empty": "Add a recovery email address",
"account.settings.field.secondary.email.confirmation": "Weve sent a confirmation message to {value}. Click the link in the message to update your recovery email address.",
"account.settings.email.field.confirmation.header": "Pending confirmation",
"account.settings.field.dob": "Año de nacimiento",
"account.settings.field.dob.empty": "Add year of birth",
"account.settings.field.year_of_birth.options.empty": "Select a year of birth",
"account.settings.field.country": "País",
"account.settings.field.country.empty": "Add country",
"account.settings.field.country.options.empty": "Select a Country",
"account.settings.field.site.language": "Site language",
"account.settings.field.site.language.help.text": "El idioma que se usará para el sitio. Actualmente solo hay disponibilidad de usar un número limitado de idiomas.",
"account.settings.field.education": "Educación",
"account.settings.field.education.empty": "Add level of education",
"account.settings.field.education.levels.empty": "Select a level of education",
"account.settings.field.education.levels.p": "Doctorado",
"account.settings.field.education.levels.m": "Master o magíster",
"account.settings.field.education.levels.b": "Bachelor's Degree",
"account.settings.field.education.levels.a": "Grado técnico - tecnológico",
"account.settings.field.education.levels.hs": "Enseñanza secundaria",
"account.settings.field.education.levels.jhs": "Formación media",
"account.settings.field.education.levels.el": "Enseñanza primaria",
"account.settings.field.education.levels.none": "Ninguna educación formal",
"account.settings.field.education.levels.o": "Otra educación",
"account.settings.field.gender": "Género",
"account.settings.field.gender.empty": "Add gender",
"account.settings.field.gender.options.empty": "Select a gender",
"account.settings.field.gender.options.f": "Femenino",
"account.settings.field.gender.options.m": "Masculino",
"account.settings.field.gender.options.o": "Otro",
"account.settings.field.language.proficiencies": "Spoken languages",
"account.settings.field.language.proficiencies.empty": "Add a spoken language",
"account.settings.field.language_proficiencies.options.empty": "Select a Language",
"account.settings.field.time.zone": "Time zone",
"account.settings.field.time.zone.empty": "Set time zone",
"account.settings.field.time.zone.description": "Select the time zone for displaying course dates. If you do not specify a time zone, course dates, including assignment deadlines, will be displayed in your browsers local time zone.",
"account.settings.field.time.zone.default": "Por defecto (Zona horaria local)",
"account.settings.field.time.zone.all": "All time zones",
"account.settings.field.time.zone.country": "Country time zones",
"account.settings.section.social.media": "Enlaces de redes sociales",
"account.settings.section.social.media.description": "Opcionalmente, conecte sus cuentas personales a los iconos de redes sociales en su perfil de edX.",
"account.settings.field.social.platform.name.linkedin": "LinkedIn",
"account.settings.field.social.platform.name.linkedin.empty": "Add LinkedIn profile",
"account.settings.jump.nav.delete.account": "Eliminar mi cuenta",
"account.settings.field.social.platform.name.twitter": "Twitter",
"account.settings.field.social.platform.name.twitter.empty": "Add Twitter profile",
"account.settings.field.social.platform.name.facebook": "Facebook",
"account.settings.field.social.platform.name.facebook.empty": "Add Facebook profile",
"account.settings.editable.field.action.save": "Guardar",
"account.settings.editable.field.action.cancel": "Cancelar",
"account.settings.editable.field.action.edit": "Editar",
"account.settings.static.field.empty": "No value set. Contact your {enterprise} administrator to make changes.",
"account.settings.static.field.empty.no.admin": "No value set.",
"account.settings.delete.account.before.proceeding": "Before proceeding, please {actionLink}.",
"account.settings.delete.account.header": "Eliminar mi cuenta",
"account.settings.delete.account.subheader": "We're sorry to see you go!",
"account.settings.delete.account.text.1": "Please note: Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.",
"account.settings.delete.account.text.2": "Una vez tu cuenta haya sido eliminada, no la podrás usar para tomar cursos en la app de edX, edx.org o en cualquier otro sitio administrado por edX. Esto incluye el acceso a edx.org desde el sistema de tu empleador o de tu universidad y el acceso a páginas privadas ofrecidas por MIT Open Learning, Wharton Online y Harvard Medical School.",
"account.settings.delete.account.text.3.link": "follow the instructions for printing or downloading a certificate",
"account.settings.delete.account.text.warning": "Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on edX.",
"account.settings.delete.account.text.change.instead": "Want to change your email, name, or password instead?",
"account.settings.delete.account.button": "Eliminar mi cuenta",
"account.settings.delete.account.please.activate": "activate your account",
"account.settings.delete.account.please.unlink": "unlink all social media accounts",
"account.settings.delete.account.modal.header": "¿Está seguro?",
"account.settings.delete.account.modal.text.1": "You have selected \"Delete My Account\". Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.",
"account.settings.delete.account.modal.text.2": "If you proceed, you will be unable to use this account to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employer's or university's system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.",
"account.settings.delete.account.modal.enter.password": "Si deseas continuar y eliminar tu cuenta, por favor introduce la contraseña de tu cuenta:",
"account.settings.delete.account.modal.confirm.delete": "Si, Eliminar",
"account.settings.delete.account.modal.confirm.cancel": "Cancelar",
"account.settings.delete.account.error.unable.to.delete": "No es posible eliminar esta cuenta",
"account.settings.delete.account.error.no.password": "A password is required",
"account.settings.delete.account.error.unable.to.delete.details": "Ocurrió un error al procesar tu solicitud. Por favor, intente nuevamente más tarde.",
"account.settings.delete.account.modal.after.header": "We're sorry to see you go! Your account will be deleted shortly.",
"account.settings.delete.account.modal.after.text": "La eliminación de cuenta, incluyendo la eliminación de las listas de correo electrónico, puede tardar unas semanas en procesarse totalmente en nuestro sistema. Si quieres renunciar a recibir correos antes de que la eliminación se haya completado, por favor date de baja mediante el enlace que aparece al final de los correos.",
"account.settings.delete.account.modal.after.button": "Cerrar",
"account.settings.delete.account.text.3": "You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion, {actionLink}.",
"account.settings.editable.field.password.reset.button.confirmation.support.link": "technical support",
"account.settings.editable.field.password.reset.button.confirmation": "We've sent a message to {email}. Click the link in the message to reset your password. Didn't receive the message? Contact {technicalSupportLink}.",
"account.settings.editable.field.password.reset.button": "Restablecer contraseña",
"account.settings.editable.field.password.reset.label": "Contraseña",
"account.settings.sso.link.account": "Sign in with {name}",
"account.settings.sso.account.connected": "Vinculado",
"account.settings.sso.account.disconnect.error": "There was a problem disconnecting this account. Contact support if the problem persists.",
"account.settings.sso.unlink.account": "Unlink {name} account",
"account.settings.sso.no.providers": "No accounts can be linked at this time.",
"unexpected.error.message.text": "An unexpected error occurred. Please click the button below to return to refresh the page.",
"unexpected.error.button.text": "Try Again",
"siteheader.links.courses": "Cursos",
"siteheader.links.programs": "Programas y Titulos",
"siteheader.links.schools": "Escuelas y Socios",
"siteheader.links.programs": "Programs",
"siteheader.links.content.search": "Discover New",
"siteheader.user.menu.dashboard": "Panel de Control",
"siteheader.user.menu.profile": "Perfil",
"siteheader.user.menu.account.settings": "Cuenta",
"siteheader.user.menu.order.history": "Historial de órdenes",
"siteheader.user.menu.logout": "Salir",
"siteheader.user.menu.login": "Iniciar Sesión",
"siteheader.user.menu.register": "Registrarse"
"siteheader.user.menu.register": "Registrarse",
"app.loading.message": "Cargando...",
"error.notfound.message": "La página que estas buscando no está disponible o hay un error en la URL. Por favor, comprueba la URL y vuelve a intentarlo."
}

View File

@@ -1,11 +1,126 @@
{
"siteheader.links.courses": "Cours",
"siteheader.links.programs": "Programs & Degrees",
"siteheader.links.schools": "Écoles et Partenaires",
"siteheader.user.menu.dashboard": "Tableau de bord",
"siteheader.user.menu.profile": "Profil",
"siteheader.user.menu.account.settings": "Compte",
"siteheader.user.menu.logout": "Déconnexion",
"siteheader.user.menu.login": "Connexion",
"siteheader.user.menu.register": "S'inscrire"
"account.settings.message.duplicate.tpa.provider": "The {provider} account you selected is already linked to another edX account.",
"account.settings.message.managed.settings": "Your profile settings are managed by {managerTitle}. Contact your administrator or {support} for help.",
"account.settings.message.managed.settings.support": "support",
"account.settings.page.heading": "Account Settings",
"account.settings.loading.message": "Loading...",
"account.settings.loading.error": "Error: {error}",
"account.settings.banner.beta.language": "You have set your language to {beta_language}, which is currently not fully translated. You can help us translate this language fully by joining the Transifex community and adding translations from English for learners that speak {beta_language}.",
"account.settings.banner.beta.language.action.switch.back": "Switch Back to {previous_language}",
"account.settings.banner.beta.language.action.help.translate": "Help Translate into {beta_language}",
"account.settings.section.account.information": "Account Information",
"account.settings.section.account.information.description": "These settings include basic information about your account.",
"account.settings.section.profile.information": "Profile Information",
"account.settings.section.site.preferences": "Site Preferences",
"account.settings.section.linked.accounts": "Linked Accounts",
"account.settings.section.linked.accounts.description": "You can link your identity accounts to simplify signing in to edX.",
"account.settings.field.username": "Username",
"account.settings.field.username.help.text": "The name that identifies you on edX. You cannot change your username.",
"account.settings.field.full.name": "Full name",
"account.settings.field.full.name.empty": "Add name",
"account.settings.field.full.name.help.text": "The name that is used for ID verification and that appears on your certificates.",
"account.settings.field.email": "Email address (Sign in)",
"account.settings.field.email.empty": "Add email address",
"account.settings.field.email.confirmation": "Weve sent a confirmation message to {value}. Click the link in the message to update your email address.",
"account.settings.field.email.help.text": "You receive messages from edX and course teams at this address.",
"account.settings.field.secondary.email": "Recovery email address",
"account.settings.field.secondary.email.empty": "Add a recovery email address",
"account.settings.field.secondary.email.confirmation": "Weve sent a confirmation message to {value}. Click the link in the message to update your recovery email address.",
"account.settings.email.field.confirmation.header": "Pending confirmation",
"account.settings.field.dob": "Year of birth",
"account.settings.field.dob.empty": "Add year of birth",
"account.settings.field.year_of_birth.options.empty": "Select a year of birth",
"account.settings.field.country": "Country",
"account.settings.field.country.empty": "Add country",
"account.settings.field.country.options.empty": "Select a Country",
"account.settings.field.site.language": "Site language",
"account.settings.field.site.language.help.text": "The language used throughout this site. This site is currently available in a limited number of languages.",
"account.settings.field.education": "Education",
"account.settings.field.education.empty": "Add level of education",
"account.settings.field.education.levels.empty": "Select a level of education",
"account.settings.field.education.levels.p": "Doctorate",
"account.settings.field.education.levels.m": "Master's or professional degree",
"account.settings.field.education.levels.b": "Bachelor's Degree",
"account.settings.field.education.levels.a": "Associate's degree",
"account.settings.field.education.levels.hs": "Secondary/high school",
"account.settings.field.education.levels.jhs": "Junior secondary/junior high/middle school",
"account.settings.field.education.levels.el": "Elementary/primary school",
"account.settings.field.education.levels.none": "No formal education",
"account.settings.field.education.levels.o": "Other education",
"account.settings.field.gender": "Gender",
"account.settings.field.gender.empty": "Add gender",
"account.settings.field.gender.options.empty": "Select a gender",
"account.settings.field.gender.options.f": "Female",
"account.settings.field.gender.options.m": "Male",
"account.settings.field.gender.options.o": "Other",
"account.settings.field.language.proficiencies": "Spoken languages",
"account.settings.field.language.proficiencies.empty": "Add a spoken language",
"account.settings.field.language_proficiencies.options.empty": "Select a Language",
"account.settings.field.time.zone": "Time zone",
"account.settings.field.time.zone.empty": "Set time zone",
"account.settings.field.time.zone.description": "Select the time zone for displaying course dates. If you do not specify a time zone, course dates, including assignment deadlines, will be displayed in your browsers local time zone.",
"account.settings.field.time.zone.default": "Default (Local Time Zone)",
"account.settings.field.time.zone.all": "All time zones",
"account.settings.field.time.zone.country": "Country time zones",
"account.settings.section.social.media": "Social Media Links",
"account.settings.section.social.media.description": "Optionally, link your personal accounts to the social media icons on your edX profile.",
"account.settings.field.social.platform.name.linkedin": "LinkedIn",
"account.settings.field.social.platform.name.linkedin.empty": "Add LinkedIn profile",
"account.settings.jump.nav.delete.account": "Delete My Account",
"account.settings.field.social.platform.name.twitter": "Twitter",
"account.settings.field.social.platform.name.twitter.empty": "Add Twitter profile",
"account.settings.field.social.platform.name.facebook": "Facebook",
"account.settings.field.social.platform.name.facebook.empty": "Add Facebook profile",
"account.settings.editable.field.action.save": "Save",
"account.settings.editable.field.action.cancel": "Cancel",
"account.settings.editable.field.action.edit": "Edit",
"account.settings.static.field.empty": "No value set. Contact your {enterprise} administrator to make changes.",
"account.settings.static.field.empty.no.admin": "No value set.",
"account.settings.delete.account.before.proceeding": "Before proceeding, please {actionLink}.",
"account.settings.delete.account.header": "Delete My Account",
"account.settings.delete.account.subheader": "We're sorry to see you go!",
"account.settings.delete.account.text.1": "Please note: Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.",
"account.settings.delete.account.text.2": "Once your account is deleted, you cannot use it to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employers or universitys system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.",
"account.settings.delete.account.text.3.link": "follow the instructions for printing or downloading a certificate",
"account.settings.delete.account.text.warning": "Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on edX.",
"account.settings.delete.account.text.change.instead": "Want to change your email, name, or password instead?",
"account.settings.delete.account.button": "Delete My Account",
"account.settings.delete.account.please.activate": "activate your account",
"account.settings.delete.account.please.unlink": "unlink all social media accounts",
"account.settings.delete.account.modal.header": "Are you sure?",
"account.settings.delete.account.modal.text.1": "You have selected \"Delete My Account\". Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.",
"account.settings.delete.account.modal.text.2": "If you proceed, you will be unable to use this account to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employer's or university's system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.",
"account.settings.delete.account.modal.enter.password": "If you still wish to continue and delete your account, please enter your account password:",
"account.settings.delete.account.modal.confirm.delete": "Yes, Delete",
"account.settings.delete.account.modal.confirm.cancel": "Cancel",
"account.settings.delete.account.error.unable.to.delete": "Unable to delete account",
"account.settings.delete.account.error.no.password": "A password is required",
"account.settings.delete.account.error.unable.to.delete.details": "Sorry, there was an error trying to process your request. Please try again later.",
"account.settings.delete.account.modal.after.header": "We're sorry to see you go! Your account will be deleted shortly.",
"account.settings.delete.account.modal.after.text": "Account deletion, including removal from email lists, may take a few weeks to fully process through our system. If you want to opt-out of emails before then, please unsubscribe from the footer of any email.",
"account.settings.delete.account.modal.after.button": "Close",
"account.settings.delete.account.text.3": "You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion, {actionLink}.",
"account.settings.editable.field.password.reset.button.confirmation.support.link": "technical support",
"account.settings.editable.field.password.reset.button.confirmation": "We've sent a message to {email}. Click the link in the message to reset your password. Didn't receive the message? Contact {technicalSupportLink}.",
"account.settings.editable.field.password.reset.button": "Reset Password",
"account.settings.editable.field.password.reset.label": "Password",
"account.settings.sso.link.account": "Sign in with {name}",
"account.settings.sso.account.connected": "Linked",
"account.settings.sso.account.disconnect.error": "There was a problem disconnecting this account. Contact support if the problem persists.",
"account.settings.sso.unlink.account": "Unlink {name} account",
"account.settings.sso.no.providers": "No accounts can be linked at this time.",
"unexpected.error.message.text": "An unexpected error occurred. Please click the button below to return to refresh the page.",
"unexpected.error.button.text": "Try Again",
"siteheader.links.courses": "Courses",
"siteheader.links.programs": "Programs",
"siteheader.links.content.search": "Discover New",
"siteheader.user.menu.dashboard": "Dashboard",
"siteheader.user.menu.profile": "Profile",
"siteheader.user.menu.account.settings": "Account",
"siteheader.user.menu.order.history": "Order History",
"siteheader.user.menu.logout": "Logout",
"siteheader.user.menu.login": "Login",
"siteheader.user.menu.register": "Sign Up",
"app.loading.message": "Loading...",
"error.notfound.message": "The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again."
}

11
src/i18n/messages/he.json Normal file
View File

@@ -0,0 +1,11 @@
{
"siteheader.links.courses": "Courses",
"siteheader.links.programs": "Programs & Degrees",
"siteheader.links.schools": "Schools & Partners",
"siteheader.user.menu.dashboard": "Dashboard",
"siteheader.user.menu.profile": "Profile",
"siteheader.user.menu.account.settings": "Account",
"siteheader.user.menu.logout": "Logout",
"siteheader.user.menu.login": "Login",
"siteheader.user.menu.register": "Sign Up"
}

11
src/i18n/messages/id.json Normal file
View File

@@ -0,0 +1,11 @@
{
"siteheader.links.courses": "Courses",
"siteheader.links.programs": "Programs & Degrees",
"siteheader.links.schools": "Schools & Partners",
"siteheader.user.menu.dashboard": "Dashboard",
"siteheader.user.menu.profile": "Profile",
"siteheader.user.menu.account.settings": "Account",
"siteheader.user.menu.logout": "Logout",
"siteheader.user.menu.login": "Login",
"siteheader.user.menu.register": "Sign Up"
}

View File

@@ -0,0 +1,11 @@
{
"siteheader.links.courses": "Courses",
"siteheader.links.programs": "Programs & Degrees",
"siteheader.links.schools": "Schools & Partners",
"siteheader.user.menu.dashboard": "Dashboard",
"siteheader.user.menu.profile": "Profile",
"siteheader.user.menu.account.settings": "Account",
"siteheader.user.menu.logout": "Logout",
"siteheader.user.menu.login": "Login",
"siteheader.user.menu.register": "Sign Up"
}

11
src/i18n/messages/pl.json Normal file
View File

@@ -0,0 +1,11 @@
{
"siteheader.links.courses": "Courses",
"siteheader.links.programs": "Programs & Degrees",
"siteheader.links.schools": "Schools & Partners",
"siteheader.user.menu.dashboard": "Dashboard",
"siteheader.user.menu.profile": "Profile",
"siteheader.user.menu.account.settings": "Account",
"siteheader.user.menu.logout": "Logout",
"siteheader.user.menu.login": "Login",
"siteheader.user.menu.register": "Sign Up"
}

View File

@@ -0,0 +1,11 @@
{
"siteheader.links.courses": "Courses",
"siteheader.links.programs": "Programs & Degrees",
"siteheader.links.schools": "Schools & Partners",
"siteheader.user.menu.dashboard": "Dashboard",
"siteheader.user.menu.profile": "Profile",
"siteheader.user.menu.account.settings": "Account",
"siteheader.user.menu.logout": "Logout",
"siteheader.user.menu.login": "Login",
"siteheader.user.menu.register": "Sign Up"
}

11
src/i18n/messages/ru.json Normal file
View File

@@ -0,0 +1,11 @@
{
"siteheader.links.courses": "Courses",
"siteheader.links.programs": "Programs & Degrees",
"siteheader.links.schools": "Schools & Partners",
"siteheader.user.menu.dashboard": "Dashboard",
"siteheader.user.menu.profile": "Profile",
"siteheader.user.menu.account.settings": "Account",
"siteheader.user.menu.logout": "Logout",
"siteheader.user.menu.login": "Login",
"siteheader.user.menu.register": "Sign Up"
}

11
src/i18n/messages/th.json Normal file
View File

@@ -0,0 +1,11 @@
{
"siteheader.links.courses": "Courses",
"siteheader.links.programs": "Programs & Degrees",
"siteheader.links.schools": "Schools & Partners",
"siteheader.user.menu.dashboard": "Dashboard",
"siteheader.user.menu.profile": "Profile",
"siteheader.user.menu.account.settings": "Account",
"siteheader.user.menu.logout": "Logout",
"siteheader.user.menu.login": "Login",
"siteheader.user.menu.register": "Sign Up"
}

11
src/i18n/messages/uk.json Normal file
View File

@@ -0,0 +1,11 @@
{
"siteheader.links.courses": "Courses",
"siteheader.links.programs": "Programs & Degrees",
"siteheader.links.schools": "Schools & Partners",
"siteheader.user.menu.dashboard": "Dashboard",
"siteheader.user.menu.profile": "Profile",
"siteheader.user.menu.account.settings": "Account",
"siteheader.user.menu.logout": "Logout",
"siteheader.user.menu.login": "Login",
"siteheader.user.menu.register": "Sign Up"
}

View File

@@ -1,11 +1,126 @@
{
"account.settings.message.duplicate.tpa.provider": "The {provider} account you selected is already linked to another edX account.",
"account.settings.message.managed.settings": "Your profile settings are managed by {managerTitle}. Contact your administrator or {support} for help.",
"account.settings.message.managed.settings.support": "support",
"account.settings.page.heading": "Account Settings",
"account.settings.loading.message": "Loading...",
"account.settings.loading.error": "Error: {error}",
"account.settings.banner.beta.language": "You have set your language to {beta_language}, which is currently not fully translated. You can help us translate this language fully by joining the Transifex community and adding translations from English for learners that speak {beta_language}.",
"account.settings.banner.beta.language.action.switch.back": "Switch Back to {previous_language}",
"account.settings.banner.beta.language.action.help.translate": "Help Translate into {beta_language}",
"account.settings.section.account.information": "Account Information",
"account.settings.section.account.information.description": "These settings include basic information about your account.",
"account.settings.section.profile.information": "Profile Information",
"account.settings.section.site.preferences": "Site Preferences",
"account.settings.section.linked.accounts": "Linked Accounts",
"account.settings.section.linked.accounts.description": "You can link your identity accounts to simplify signing in to edX.",
"account.settings.field.username": "Username",
"account.settings.field.username.help.text": "The name that identifies you on edX. You cannot change your username.",
"account.settings.field.full.name": "Full name",
"account.settings.field.full.name.empty": "Add name",
"account.settings.field.full.name.help.text": "The name that is used for ID verification and that appears on your certificates.",
"account.settings.field.email": "Email address (Sign in)",
"account.settings.field.email.empty": "Add email address",
"account.settings.field.email.confirmation": "Weve sent a confirmation message to {value}. Click the link in the message to update your email address.",
"account.settings.field.email.help.text": "You receive messages from edX and course teams at this address.",
"account.settings.field.secondary.email": "Recovery email address",
"account.settings.field.secondary.email.empty": "Add a recovery email address",
"account.settings.field.secondary.email.confirmation": "Weve sent a confirmation message to {value}. Click the link in the message to update your recovery email address.",
"account.settings.email.field.confirmation.header": "Pending confirmation",
"account.settings.field.dob": "Year of birth",
"account.settings.field.dob.empty": "Add year of birth",
"account.settings.field.year_of_birth.options.empty": "Select a year of birth",
"account.settings.field.country": "Country",
"account.settings.field.country.empty": "Add country",
"account.settings.field.country.options.empty": "Select a Country",
"account.settings.field.site.language": "Site language",
"account.settings.field.site.language.help.text": "The language used throughout this site. This site is currently available in a limited number of languages.",
"account.settings.field.education": "Education",
"account.settings.field.education.empty": "Add level of education",
"account.settings.field.education.levels.empty": "Select a level of education",
"account.settings.field.education.levels.p": "Doctorate",
"account.settings.field.education.levels.m": "Master's or professional degree",
"account.settings.field.education.levels.b": "Bachelor's Degree",
"account.settings.field.education.levels.a": "Associate's degree",
"account.settings.field.education.levels.hs": "Secondary/high school",
"account.settings.field.education.levels.jhs": "Junior secondary/junior high/middle school",
"account.settings.field.education.levels.el": "Elementary/primary school",
"account.settings.field.education.levels.none": "No formal education",
"account.settings.field.education.levels.o": "Other education",
"account.settings.field.gender": "Gender",
"account.settings.field.gender.empty": "Add gender",
"account.settings.field.gender.options.empty": "Select a gender",
"account.settings.field.gender.options.f": "Female",
"account.settings.field.gender.options.m": "Male",
"account.settings.field.gender.options.o": "Other",
"account.settings.field.language.proficiencies": "Spoken languages",
"account.settings.field.language.proficiencies.empty": "Add a spoken language",
"account.settings.field.language_proficiencies.options.empty": "Select a Language",
"account.settings.field.time.zone": "Time zone",
"account.settings.field.time.zone.empty": "Set time zone",
"account.settings.field.time.zone.description": "Select the time zone for displaying course dates. If you do not specify a time zone, course dates, including assignment deadlines, will be displayed in your browsers local time zone.",
"account.settings.field.time.zone.default": "Default (Local Time Zone)",
"account.settings.field.time.zone.all": "All time zones",
"account.settings.field.time.zone.country": "Country time zones",
"account.settings.section.social.media": "Social Media Links",
"account.settings.section.social.media.description": "Optionally, link your personal accounts to the social media icons on your edX profile.",
"account.settings.field.social.platform.name.linkedin": "LinkedIn",
"account.settings.field.social.platform.name.linkedin.empty": "Add LinkedIn profile",
"account.settings.jump.nav.delete.account": "Delete My Account",
"account.settings.field.social.platform.name.twitter": "Twitter",
"account.settings.field.social.platform.name.twitter.empty": "Add Twitter profile",
"account.settings.field.social.platform.name.facebook": "Facebook",
"account.settings.field.social.platform.name.facebook.empty": "Add Facebook profile",
"account.settings.editable.field.action.save": "Save",
"account.settings.editable.field.action.cancel": "Cancel",
"account.settings.editable.field.action.edit": "Edit",
"account.settings.static.field.empty": "No value set. Contact your {enterprise} administrator to make changes.",
"account.settings.static.field.empty.no.admin": "No value set.",
"account.settings.delete.account.before.proceeding": "Before proceeding, please {actionLink}.",
"account.settings.delete.account.header": "Delete My Account",
"account.settings.delete.account.subheader": "We're sorry to see you go!",
"account.settings.delete.account.text.1": "Please note: Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.",
"account.settings.delete.account.text.2": "Once your account is deleted, you cannot use it to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employers or universitys system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.",
"account.settings.delete.account.text.3.link": "follow the instructions for printing or downloading a certificate",
"account.settings.delete.account.text.warning": "Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on edX.",
"account.settings.delete.account.text.change.instead": "Want to change your email, name, or password instead?",
"account.settings.delete.account.button": "Delete My Account",
"account.settings.delete.account.please.activate": "activate your account",
"account.settings.delete.account.please.unlink": "unlink all social media accounts",
"account.settings.delete.account.modal.header": "Are you sure?",
"account.settings.delete.account.modal.text.1": "You have selected \"Delete My Account\". Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.",
"account.settings.delete.account.modal.text.2": "If you proceed, you will be unable to use this account to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employer's or university's system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.",
"account.settings.delete.account.modal.enter.password": "If you still wish to continue and delete your account, please enter your account password:",
"account.settings.delete.account.modal.confirm.delete": "Yes, Delete",
"account.settings.delete.account.modal.confirm.cancel": "Cancel",
"account.settings.delete.account.error.unable.to.delete": "Unable to delete account",
"account.settings.delete.account.error.no.password": "A password is required",
"account.settings.delete.account.error.unable.to.delete.details": "Sorry, there was an error trying to process your request. Please try again later.",
"account.settings.delete.account.modal.after.header": "We're sorry to see you go! Your account will be deleted shortly.",
"account.settings.delete.account.modal.after.text": "Account deletion, including removal from email lists, may take a few weeks to fully process through our system. If you want to opt-out of emails before then, please unsubscribe from the footer of any email.",
"account.settings.delete.account.modal.after.button": "Close",
"account.settings.delete.account.text.3": "You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion, {actionLink}.",
"account.settings.editable.field.password.reset.button.confirmation.support.link": "technical support",
"account.settings.editable.field.password.reset.button.confirmation": "We've sent a message to {email}. Click the link in the message to reset your password. Didn't receive the message? Contact {technicalSupportLink}.",
"account.settings.editable.field.password.reset.button": "Reset Password",
"account.settings.editable.field.password.reset.label": "Password",
"account.settings.sso.link.account": "Sign in with {name}",
"account.settings.sso.account.connected": "Linked",
"account.settings.sso.account.disconnect.error": "There was a problem disconnecting this account. Contact support if the problem persists.",
"account.settings.sso.unlink.account": "Unlink {name} account",
"account.settings.sso.no.providers": "No accounts can be linked at this time.",
"unexpected.error.message.text": "An unexpected error occurred. Please click the button below to return to refresh the page.",
"unexpected.error.button.text": "Try Again",
"siteheader.links.courses": "Courses",
"siteheader.links.programs": "Programs & Degrees",
"siteheader.links.schools": "Schools & Partners",
"siteheader.links.programs": "Programs",
"siteheader.links.content.search": "Discover New",
"siteheader.user.menu.dashboard": "Dashboard",
"siteheader.user.menu.profile": "Profile",
"siteheader.user.menu.account.settings": "Account",
"siteheader.user.menu.order.history": "Order History",
"siteheader.user.menu.logout": "Logout",
"siteheader.user.menu.login": "Login",
"siteheader.user.menu.register": "Sign Up"
"siteheader.user.menu.register": "Sign Up",
"app.loading.message": "Loading...",
"error.notfound.message": "The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again."
}

View File

@@ -1,64 +0,0 @@
{
"account.settings.page.heading": "Account Settings",
"account.settings.loading.message": "Loading",
"account.settings.loading.error": "Error: {error}",
"account.settings.section.account.information": "Account Information",
"account.settings.section.account.information.description": "These settings include basic information about your account.",
"account.settings.field.username": "Username",
"account.settings.field.full.name": "Full name",
"account.settings.field.email": "Email address (Sign in)",
"account.settings.field.email.confirmation": "Weve sent a confirmation message to {value}. Click the link in the message to update your email address.",
"account.settings.email.field.confirmation.header": "Pending confirmation",
"account.settings.field.dob": "Year of birth",
"account.settings.field.country": "Country",
"account.settings.field.education": "Education",
"account.settings.field.education.levels.null": "Select a level of education",
"account.settings.field.education.levels.p": "Doctorate",
"account.settings.field.education.levels.m": "Master's or professional degree",
"account.settings.field.education.levels.b": "Bachelor's Degree",
"account.settings.field.education.levels.a": "Associate's degree",
"account.settings.field.education.levels.hs": "Secondary/high school",
"account.settings.field.education.levels.jhs": "Junior secondary/junior high/middle school",
"account.settings.field.education.levels.el": "Elementary/primary school",
"account.settings.field.education.levels.none": "No formal education",
"account.settings.field.education.levels.o": "Other education",
"account.settings.field.gender": "Gender",
"account.settings.field.gender.options.null": "Select a gender",
"account.settings.field.gender.options.f": "Female",
"account.settings.field.gender.options.m": "Male",
"account.settings.field.gender.options.o": "Other",
"account.settings.field.language.proficiencies": "Spoken Languages",
"account.settings.section.social.media": "Social Media Links",
"account.settings.section.social.media.description": "Optionally, link your personal accounts to the social media icons on your edX profile.",
"account.settings.field.social.platform.name.linkedin": "LinkedIn",
"account.settings.field.social.platform.name.twitter": "Twitter",
"account.settings.field.social.platform.name.facebook": "Facebook",
"account.settings.editable.field.password.reset.button": "We've sent a message to {email}. Click the link in the message to reset your password. Didn't receive the message? Contact {technicalSupportLink}.",
"account.settings.editable.field.action.save": "Save",
"account.settings.editable.field.action.cancel": "Cancel",
"account.settings.editable.field.action.edit": "Edit",
"account.settings.editable.field.password.reset.button.support.link": "technical support",
"account.settings.editable.field.password.reset.label": "Password",
"account.settings.sso.link.account": "Sign in with {name}",
"account.settings.sso.account.connected": "Linked",
"account.settings.sso.unlink.account": "Unlink {name} account",
"account.settings.sso.no.providers": "No accounts can be linked at this time.",
"account.settings.sso.loading": "Loading...",
"account.settings.sso.loading.error": "There was a problem loading linked accounts.",
"account.settings.sso.section.header": "Linked Accounts",
"account.settings.sso.section.subheader": "You can link your identity accounts to simplify signing in to edX.",
"siteheader.links.courses": "Courses",
"siteheader.links.programs": "Programs & Degrees",
"siteheader.links.schools": "Schools & Partners",
"siteheader.user.menu.dashboard": "Dashboard",
"siteheader.user.menu.profile": "Profile",
"siteheader.user.menu.account.settings": "Account",
"siteheader.user.menu.order.history": "Order History",
"siteheader.user.menu.logout": "Logout",
"siteheader.user.menu.login": "Login",
"siteheader.user.menu.register": "Sign Up",
"app.loading.message": "Loading",
"error.unexpected.message": "An unexpected error occurred.",
"error.notfound.message": "The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again.",
"app.page": "Congratulations! You have a new micro-frontend."
}

View File

@@ -1,15 +1,25 @@
import 'babel-polyfill';
import 'url-polyfill';
import 'formdata-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import { identifyAuthenticatedUser, sendPageEvent, configureAnalytics, initializeSegment } from '@edx/frontend-analytics';
import {
configureAnalytics,
identifyAnonymousUser,
identifyAuthenticatedUser,
initializeSegment,
sendPageEvent,
sendTrackingLogEvent,
} from '@edx/frontend-analytics';
import { configureLoggingService, NewRelicLoggingService } from '@edx/frontend-logging';
import { getAuthenticatedAPIClient } from '@edx/frontend-auth';
import { configure as configureI18n } from '@edx/frontend-i18n';
import { configuration } from './environment';
import { handleRtl } from './i18n/i18n-loader';
import configureStore from './store';
import { configureUserAccountApiService } from './common';
import { configureApiService as configureAccountSettingsApiService } from './account-settings';
import { configureService as configureAccountSettingsApiService } from './account-settings';
import messages from './i18n';
import './index.scss';
import App from './components/App';
@@ -32,10 +42,22 @@ const apiClient = getAuthenticatedAPIClient({
* so that we can hand it all to the redux store's initializer.
*/
function createInitialState() {
return Object.assign({}, { configuration }, apiClient.getAuthenticationState());
const errors = {};
const url = new URL(window.location.href);
// Extract duplicate third-party auth provider message from query string
errors.duplicateTpaProvider = url.searchParams.get('duplicate_provider');
if (errors.duplicateTpaProvider) {
// Remove the duplicate_provider query param to avoid bookmarking.
window.history.replaceState(null, '', `${url.protocol}//${url.host}${url.pathname}`);
}
return Object.assign({}, { configuration }, apiClient.getAuthenticationState(), { errors });
}
function configure() {
configureI18n(configuration, messages);
const { store, history } = configureStore(createInitialState(), configuration.ENVIRONMENT);
configureLoggingService(NewRelicLoggingService);
@@ -48,10 +70,6 @@ function configure() {
analyticsApiBaseUrl: configuration.LMS_BASE_URL,
});
if (configuration.ENVIRONMENT === 'production') {
handleRtl();
}
return {
store,
history,
@@ -60,13 +78,22 @@ function configure() {
apiClient.ensurePublicOrAuthenticationAndCookies(
window.location.pathname,
() => {
(accessToken) => {
const { store, history } = configure();
ReactDOM.render(<App store={store} history={history} />, document.getElementById('root'));
identifyAuthenticatedUser();
if (accessToken) {
identifyAuthenticatedUser(accessToken.userId);
} else {
identifyAnonymousUser();
}
sendPageEvent();
sendTrackingLogEvent('edx.user.settings.viewed', {
page: 'account',
visibility: null,
user_id: accessToken ? accessToken.userId : null,
});
},
);

View File

@@ -1,8 +0,0 @@
/* I'm here to allow autoprefixing in webpack.prod.config.js */
module.exports = {
plugins: [
/* eslint-disable-next-line global-require, import/no-extraneous-dependencies */
require('autoprefixer')({ grid: true, browsers: ['>1%'] }),
],
};

View File

@@ -2,6 +2,8 @@ import { combineReducers } from 'redux';
import { userAccount } from '@edx/frontend-auth';
import { connectRouter } from 'connected-react-router';
import { reducer as i18nReducer } from '@edx/frontend-i18n'; // eslint-disable-line
import {
reducer as accountSettingsReducer,
storeName as accountSettingsStoreName,
@@ -18,6 +20,8 @@ const createRootReducer = history =>
// creating the store in data/store.js.
authentication: identityReducer,
configuration: identityReducer,
errors: identityReducer,
i18n: i18nReducer,
userAccount,
[accountSettingsStoreName]: accountSettingsReducer,
router: connectRouter(history),

View File

@@ -2,7 +2,5 @@ import { all } from 'redux-saga/effects';
import { saga as accountSettingsSaga } from './account-settings';
export default function* rootSaga() {
yield all([
accountSettingsSaga(),
]);
yield all([accountSettingsSaga()]);
}

Some files were not shown because too many files have changed in this diff Show More