From 3a3baddad1c801d77dc398d2c6980f3c14f4a47c Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Sat, 30 Oct 2021 22:11:00 +0200 Subject: chore: move htdocs to repo root --- .gitignore | 2 +- .gitmodules | 8 +- assets/fonts/Inter/Inter.woff2 | Bin 0 -> 324864 bytes assets/fonts/Inter/LICENSE.txt | 94 + assets/fonts/Kanit/Kanit-Bold.woff | Bin 0 -> 73508 bytes assets/fonts/Kanit/Kanit-Bold.woff2 | Bin 0 -> 50480 bytes assets/fonts/Kanit/Kanit-BoldItalic.woff | Bin 0 -> 79120 bytes assets/fonts/Kanit/Kanit-BoldItalic.woff2 | Bin 0 -> 53888 bytes assets/fonts/Kanit/Kanit-Italic.woff | Bin 0 -> 76092 bytes assets/fonts/Kanit/Kanit-Italic.woff2 | Bin 0 -> 51768 bytes assets/fonts/Kanit/Kanit-Light.woff | Bin 0 -> 71864 bytes assets/fonts/Kanit/Kanit-Light.woff2 | Bin 0 -> 49256 bytes assets/fonts/Kanit/Kanit-LightItalic.woff | Bin 0 -> 75752 bytes assets/fonts/Kanit/Kanit-LightItalic.woff2 | Bin 0 -> 51788 bytes assets/fonts/Kanit/Kanit-Medium.woff | Bin 0 -> 73260 bytes assets/fonts/Kanit/Kanit-Medium.woff2 | Bin 0 -> 50200 bytes assets/fonts/Kanit/Kanit-MediumItalic.woff | Bin 0 -> 75980 bytes assets/fonts/Kanit/Kanit-MediumItalic.woff2 | Bin 0 -> 51752 bytes assets/fonts/Kanit/Kanit-Regular.woff | Bin 0 -> 72792 bytes assets/fonts/Kanit/Kanit-Regular.woff2 | Bin 0 -> 49980 bytes assets/fonts/Kanit/Kanit-SemiBold.woff | Bin 0 -> 73472 bytes assets/fonts/Kanit/Kanit-SemiBold.woff2 | Bin 0 -> 50748 bytes assets/fonts/Kanit/Kanit-SemiBoldItalic.woff | Bin 0 -> 76072 bytes assets/fonts/Kanit/Kanit-SemiBoldItalic.woff2 | Bin 0 -> 51896 bytes assets/fonts/Kanit/OFL.txt | 93 + assets/images/armand-philippot-logo.svg | 1 + assets/images/armand-philippot.jpg | Bin 0 -> 21553 bytes assets/images/cc-by-sa.svg | 1 + assets/images/favicon/android-chrome-192x192.png | Bin 0 -> 3750 bytes assets/images/favicon/android-chrome-512x512.png | Bin 0 -> 10707 bytes assets/images/favicon/apple-touch-icon.png | Bin 0 -> 3514 bytes assets/images/favicon/browserconfig.xml | 9 + assets/images/favicon/favicon-16x16.png | Bin 0 -> 414 bytes assets/images/favicon/favicon-32x32.png | Bin 0 -> 741 bytes assets/images/favicon/favicon.ico | Bin 0 -> 7406 bytes assets/images/favicon/mstile-144x144.png | Bin 0 -> 2798 bytes assets/images/favicon/mstile-150x150.png | Bin 0 -> 3262 bytes assets/images/favicon/mstile-310x150.png | Bin 0 -> 3707 bytes assets/images/favicon/mstile-310x310.png | Bin 0 -> 7339 bytes assets/images/favicon/mstile-70x70.png | Bin 0 -> 2131 bytes assets/images/favicon/safari-pinned-tab.svg | 1 + assets/images/favicon/site.webmanifest | 19 + assets/images/github.svg | 1 + assets/images/gitlab.svg | 1 + assets/js/app.js | 7285 ++++++++++++++++++++ assets/js/runtime.js | 1158 ++++ assets/js/style.js | 5300 ++++++++++++++ config/paths.js | 4 +- config/webpack.common.js | 4 +- htdocs/index.html | 77 - htdocs/legal-notice.html | 90 - htdocs/license.html | 93 - htdocs/mentions-legales.html | 90 - htdocs/projects/js-small-apps | 1 - htdocs/projects/react-small-apps | 1 - htdocs/src/fonts/Inter/Inter.woff2 | Bin 324864 -> 0 bytes htdocs/src/fonts/Inter/LICENSE.txt | 94 - htdocs/src/fonts/Kanit/Kanit-Bold.woff | Bin 73508 -> 0 bytes htdocs/src/fonts/Kanit/Kanit-Bold.woff2 | Bin 50480 -> 0 bytes htdocs/src/fonts/Kanit/Kanit-BoldItalic.woff | Bin 79120 -> 0 bytes htdocs/src/fonts/Kanit/Kanit-BoldItalic.woff2 | Bin 53888 -> 0 bytes htdocs/src/fonts/Kanit/Kanit-Italic.woff | Bin 76092 -> 0 bytes htdocs/src/fonts/Kanit/Kanit-Italic.woff2 | Bin 51768 -> 0 bytes htdocs/src/fonts/Kanit/Kanit-Light.woff | Bin 71864 -> 0 bytes htdocs/src/fonts/Kanit/Kanit-Light.woff2 | Bin 49256 -> 0 bytes htdocs/src/fonts/Kanit/Kanit-LightItalic.woff | Bin 75752 -> 0 bytes htdocs/src/fonts/Kanit/Kanit-LightItalic.woff2 | Bin 51788 -> 0 bytes htdocs/src/fonts/Kanit/Kanit-Medium.woff | Bin 73260 -> 0 bytes htdocs/src/fonts/Kanit/Kanit-Medium.woff2 | Bin 50200 -> 0 bytes htdocs/src/fonts/Kanit/Kanit-MediumItalic.woff | Bin 75980 -> 0 bytes htdocs/src/fonts/Kanit/Kanit-MediumItalic.woff2 | Bin 51752 -> 0 bytes htdocs/src/fonts/Kanit/Kanit-Regular.woff | Bin 72792 -> 0 bytes htdocs/src/fonts/Kanit/Kanit-Regular.woff2 | Bin 49980 -> 0 bytes htdocs/src/fonts/Kanit/Kanit-SemiBold.woff | Bin 73472 -> 0 bytes htdocs/src/fonts/Kanit/Kanit-SemiBold.woff2 | Bin 50748 -> 0 bytes htdocs/src/fonts/Kanit/Kanit-SemiBoldItalic.woff | Bin 76072 -> 0 bytes htdocs/src/fonts/Kanit/Kanit-SemiBoldItalic.woff2 | Bin 51896 -> 0 bytes htdocs/src/fonts/Kanit/OFL.txt | 93 - htdocs/src/images/armand-philippot-logo.svg | 170 - htdocs/src/images/armand-philippot.jpg | Bin 28584 -> 0 bytes htdocs/src/images/cc-by-sa.svg | 79 - .../src/images/favicon/android-chrome-192x192.png | Bin 5576 -> 0 bytes .../src/images/favicon/android-chrome-512x512.png | Bin 14971 -> 0 bytes htdocs/src/images/favicon/apple-touch-icon.png | Bin 5270 -> 0 bytes htdocs/src/images/favicon/browserconfig.xml | 9 - htdocs/src/images/favicon/favicon-16x16.png | Bin 774 -> 0 bytes htdocs/src/images/favicon/favicon-32x32.png | Bin 1093 -> 0 bytes htdocs/src/images/favicon/favicon.ico | Bin 7406 -> 0 bytes htdocs/src/images/favicon/mstile-144x144.png | Bin 4236 -> 0 bytes htdocs/src/images/favicon/mstile-150x150.png | Bin 4258 -> 0 bytes htdocs/src/images/favicon/mstile-310x150.png | Bin 4708 -> 0 bytes htdocs/src/images/favicon/mstile-310x310.png | Bin 8861 -> 0 bytes htdocs/src/images/favicon/mstile-70x70.png | Bin 3034 -> 0 bytes htdocs/src/images/favicon/safari-pinned-tab.svg | 25 - htdocs/src/images/favicon/site.webmanifest | 19 - htdocs/src/images/github.svg | 69 - htdocs/src/images/gitlab.svg | 93 - htdocs/src/js/app.js | 382 - htdocs/src/js/config/projects.js | 224 - htdocs/src/js/i18n/i18n.js | 42 - htdocs/src/js/i18n/locales/en.js | 36 - htdocs/src/js/i18n/locales/fr.js | 36 - htdocs/src/js/utilities/animations.js | 45 - htdocs/src/js/utilities/helpers.js | 19 - htdocs/src/scss/abstracts/_functions.scss | 4 - htdocs/src/scss/abstracts/_mixins.scss | 2 - htdocs/src/scss/abstracts/_placeholders.scss | 32 - htdocs/src/scss/abstracts/_variables.scss | 103 - htdocs/src/scss/abstracts/functions/_convert.scss | 16 - htdocs/src/scss/abstracts/functions/_css-vars.scss | 8 - htdocs/src/scss/abstracts/functions/_encode.scss | 14 - .../src/scss/abstracts/functions/_str-replace.scss | 20 - htdocs/src/scss/abstracts/mixins/_css-vars.scss | 20 - .../src/scss/abstracts/mixins/_media-queries.scss | 81 - htdocs/src/scss/base/_animations.scss | 89 - htdocs/src/scss/base/_base.scss | 3 - htdocs/src/scss/base/_colors.scss | 21 - htdocs/src/scss/base/_fonts.scss | 115 - htdocs/src/scss/base/_helpers.scss | 44 - htdocs/src/scss/base/_spacings.scss | 24 - htdocs/src/scss/base/_typography.scss | 48 - htdocs/src/scss/components/_buttons.scss | 21 - htdocs/src/scss/layout/_footer.scss | 40 - htdocs/src/scss/layout/_grid.scss | 43 - htdocs/src/scss/layout/_header.scss | 143 - htdocs/src/scss/layout/_main.scss | 153 - htdocs/src/scss/layout/_nav.scss | 70 - htdocs/src/scss/layout/_toolbar.scss | 34 - htdocs/src/scss/style.scss | 40 - index.html | 77 + legal-notice.html | 90 + license.html | 93 + mentions-legales.html | 90 + package.json | 2 +- projects/js-small-apps | 1 + projects/react-small-apps | 1 + src/fonts/Inter/Inter.woff2 | Bin 0 -> 324864 bytes src/fonts/Inter/LICENSE.txt | 94 + src/fonts/Kanit/Kanit-Bold.woff | Bin 0 -> 73508 bytes src/fonts/Kanit/Kanit-Bold.woff2 | Bin 0 -> 50480 bytes src/fonts/Kanit/Kanit-BoldItalic.woff | Bin 0 -> 79120 bytes src/fonts/Kanit/Kanit-BoldItalic.woff2 | Bin 0 -> 53888 bytes src/fonts/Kanit/Kanit-Italic.woff | Bin 0 -> 76092 bytes src/fonts/Kanit/Kanit-Italic.woff2 | Bin 0 -> 51768 bytes src/fonts/Kanit/Kanit-Light.woff | Bin 0 -> 71864 bytes src/fonts/Kanit/Kanit-Light.woff2 | Bin 0 -> 49256 bytes src/fonts/Kanit/Kanit-LightItalic.woff | Bin 0 -> 75752 bytes src/fonts/Kanit/Kanit-LightItalic.woff2 | Bin 0 -> 51788 bytes src/fonts/Kanit/Kanit-Medium.woff | Bin 0 -> 73260 bytes src/fonts/Kanit/Kanit-Medium.woff2 | Bin 0 -> 50200 bytes src/fonts/Kanit/Kanit-MediumItalic.woff | Bin 0 -> 75980 bytes src/fonts/Kanit/Kanit-MediumItalic.woff2 | Bin 0 -> 51752 bytes src/fonts/Kanit/Kanit-Regular.woff | Bin 0 -> 72792 bytes src/fonts/Kanit/Kanit-Regular.woff2 | Bin 0 -> 49980 bytes src/fonts/Kanit/Kanit-SemiBold.woff | Bin 0 -> 73472 bytes src/fonts/Kanit/Kanit-SemiBold.woff2 | Bin 0 -> 50748 bytes src/fonts/Kanit/Kanit-SemiBoldItalic.woff | Bin 0 -> 76072 bytes src/fonts/Kanit/Kanit-SemiBoldItalic.woff2 | Bin 0 -> 51896 bytes src/fonts/Kanit/OFL.txt | 93 + src/images/armand-philippot-logo.svg | 170 + src/images/armand-philippot.jpg | Bin 0 -> 28584 bytes src/images/cc-by-sa.svg | 79 + src/images/favicon/android-chrome-192x192.png | Bin 0 -> 5576 bytes src/images/favicon/android-chrome-512x512.png | Bin 0 -> 14971 bytes src/images/favicon/apple-touch-icon.png | Bin 0 -> 5270 bytes src/images/favicon/browserconfig.xml | 9 + src/images/favicon/favicon-16x16.png | Bin 0 -> 774 bytes src/images/favicon/favicon-32x32.png | Bin 0 -> 1093 bytes src/images/favicon/favicon.ico | Bin 0 -> 7406 bytes src/images/favicon/mstile-144x144.png | Bin 0 -> 4236 bytes src/images/favicon/mstile-150x150.png | Bin 0 -> 4258 bytes src/images/favicon/mstile-310x150.png | Bin 0 -> 4708 bytes src/images/favicon/mstile-310x310.png | Bin 0 -> 8861 bytes src/images/favicon/mstile-70x70.png | Bin 0 -> 3034 bytes src/images/favicon/safari-pinned-tab.svg | 25 + src/images/favicon/site.webmanifest | 19 + src/images/github.svg | 69 + src/images/gitlab.svg | 93 + src/js/app.js | 382 + src/js/config/projects.js | 224 + src/js/i18n/i18n.js | 42 + src/js/i18n/locales/en.js | 36 + src/js/i18n/locales/fr.js | 36 + src/js/utilities/animations.js | 45 + src/js/utilities/helpers.js | 19 + src/scss/abstracts/_functions.scss | 4 + src/scss/abstracts/_mixins.scss | 2 + src/scss/abstracts/_placeholders.scss | 32 + src/scss/abstracts/_variables.scss | 103 + src/scss/abstracts/functions/_convert.scss | 16 + src/scss/abstracts/functions/_css-vars.scss | 8 + src/scss/abstracts/functions/_encode.scss | 14 + src/scss/abstracts/functions/_str-replace.scss | 20 + src/scss/abstracts/mixins/_css-vars.scss | 20 + src/scss/abstracts/mixins/_media-queries.scss | 81 + src/scss/base/_animations.scss | 89 + src/scss/base/_base.scss | 3 + src/scss/base/_colors.scss | 21 + src/scss/base/_fonts.scss | 115 + src/scss/base/_helpers.scss | 44 + src/scss/base/_spacings.scss | 24 + src/scss/base/_typography.scss | 48 + src/scss/components/_buttons.scss | 21 + src/scss/layout/_footer.scss | 40 + src/scss/layout/_grid.scss | 43 + src/scss/layout/_header.scss | 143 + src/scss/layout/_main.scss | 138 + src/scss/layout/_nav.scss | 70 + src/scss/layout/_toolbar.scss | 34 + src/scss/style.scss | 40 + 210 files changed, 16933 insertions(+), 2985 deletions(-) create mode 100644 assets/fonts/Inter/Inter.woff2 create mode 100644 assets/fonts/Inter/LICENSE.txt create mode 100644 assets/fonts/Kanit/Kanit-Bold.woff create mode 100644 assets/fonts/Kanit/Kanit-Bold.woff2 create mode 100644 assets/fonts/Kanit/Kanit-BoldItalic.woff create mode 100644 assets/fonts/Kanit/Kanit-BoldItalic.woff2 create mode 100644 assets/fonts/Kanit/Kanit-Italic.woff create mode 100644 assets/fonts/Kanit/Kanit-Italic.woff2 create mode 100644 assets/fonts/Kanit/Kanit-Light.woff create mode 100644 assets/fonts/Kanit/Kanit-Light.woff2 create mode 100644 assets/fonts/Kanit/Kanit-LightItalic.woff create mode 100644 assets/fonts/Kanit/Kanit-LightItalic.woff2 create mode 100644 assets/fonts/Kanit/Kanit-Medium.woff create mode 100644 assets/fonts/Kanit/Kanit-Medium.woff2 create mode 100644 assets/fonts/Kanit/Kanit-MediumItalic.woff create mode 100644 assets/fonts/Kanit/Kanit-MediumItalic.woff2 create mode 100644 assets/fonts/Kanit/Kanit-Regular.woff create mode 100644 assets/fonts/Kanit/Kanit-Regular.woff2 create mode 100644 assets/fonts/Kanit/Kanit-SemiBold.woff create mode 100644 assets/fonts/Kanit/Kanit-SemiBold.woff2 create mode 100644 assets/fonts/Kanit/Kanit-SemiBoldItalic.woff create mode 100644 assets/fonts/Kanit/Kanit-SemiBoldItalic.woff2 create mode 100644 assets/fonts/Kanit/OFL.txt create mode 100644 assets/images/armand-philippot-logo.svg create mode 100644 assets/images/armand-philippot.jpg create mode 100644 assets/images/cc-by-sa.svg create mode 100644 assets/images/favicon/android-chrome-192x192.png create mode 100644 assets/images/favicon/android-chrome-512x512.png create mode 100644 assets/images/favicon/apple-touch-icon.png create mode 100644 assets/images/favicon/browserconfig.xml create mode 100644 assets/images/favicon/favicon-16x16.png create mode 100644 assets/images/favicon/favicon-32x32.png create mode 100644 assets/images/favicon/favicon.ico create mode 100644 assets/images/favicon/mstile-144x144.png create mode 100644 assets/images/favicon/mstile-150x150.png create mode 100644 assets/images/favicon/mstile-310x150.png create mode 100644 assets/images/favicon/mstile-310x310.png create mode 100644 assets/images/favicon/mstile-70x70.png create mode 100644 assets/images/favicon/safari-pinned-tab.svg create mode 100644 assets/images/favicon/site.webmanifest create mode 100644 assets/images/github.svg create mode 100644 assets/images/gitlab.svg create mode 100644 assets/js/app.js create mode 100644 assets/js/runtime.js create mode 100644 assets/js/style.js delete mode 100644 htdocs/index.html delete mode 100644 htdocs/legal-notice.html delete mode 100644 htdocs/license.html delete mode 100644 htdocs/mentions-legales.html delete mode 160000 htdocs/projects/js-small-apps delete mode 160000 htdocs/projects/react-small-apps delete mode 100644 htdocs/src/fonts/Inter/Inter.woff2 delete mode 100644 htdocs/src/fonts/Inter/LICENSE.txt delete mode 100644 htdocs/src/fonts/Kanit/Kanit-Bold.woff delete mode 100644 htdocs/src/fonts/Kanit/Kanit-Bold.woff2 delete mode 100644 htdocs/src/fonts/Kanit/Kanit-BoldItalic.woff delete mode 100644 htdocs/src/fonts/Kanit/Kanit-BoldItalic.woff2 delete mode 100644 htdocs/src/fonts/Kanit/Kanit-Italic.woff delete mode 100644 htdocs/src/fonts/Kanit/Kanit-Italic.woff2 delete mode 100644 htdocs/src/fonts/Kanit/Kanit-Light.woff delete mode 100644 htdocs/src/fonts/Kanit/Kanit-Light.woff2 delete mode 100644 htdocs/src/fonts/Kanit/Kanit-LightItalic.woff delete mode 100644 htdocs/src/fonts/Kanit/Kanit-LightItalic.woff2 delete mode 100644 htdocs/src/fonts/Kanit/Kanit-Medium.woff delete mode 100644 htdocs/src/fonts/Kanit/Kanit-Medium.woff2 delete mode 100644 htdocs/src/fonts/Kanit/Kanit-MediumItalic.woff delete mode 100644 htdocs/src/fonts/Kanit/Kanit-MediumItalic.woff2 delete mode 100644 htdocs/src/fonts/Kanit/Kanit-Regular.woff delete mode 100644 htdocs/src/fonts/Kanit/Kanit-Regular.woff2 delete mode 100644 htdocs/src/fonts/Kanit/Kanit-SemiBold.woff delete mode 100644 htdocs/src/fonts/Kanit/Kanit-SemiBold.woff2 delete mode 100644 htdocs/src/fonts/Kanit/Kanit-SemiBoldItalic.woff delete mode 100644 htdocs/src/fonts/Kanit/Kanit-SemiBoldItalic.woff2 delete mode 100644 htdocs/src/fonts/Kanit/OFL.txt delete mode 100644 htdocs/src/images/armand-philippot-logo.svg delete mode 100644 htdocs/src/images/armand-philippot.jpg delete mode 100644 htdocs/src/images/cc-by-sa.svg delete mode 100644 htdocs/src/images/favicon/android-chrome-192x192.png delete mode 100644 htdocs/src/images/favicon/android-chrome-512x512.png delete mode 100644 htdocs/src/images/favicon/apple-touch-icon.png delete mode 100644 htdocs/src/images/favicon/browserconfig.xml delete mode 100644 htdocs/src/images/favicon/favicon-16x16.png delete mode 100644 htdocs/src/images/favicon/favicon-32x32.png delete mode 100644 htdocs/src/images/favicon/favicon.ico delete mode 100644 htdocs/src/images/favicon/mstile-144x144.png delete mode 100644 htdocs/src/images/favicon/mstile-150x150.png delete mode 100644 htdocs/src/images/favicon/mstile-310x150.png delete mode 100644 htdocs/src/images/favicon/mstile-310x310.png delete mode 100644 htdocs/src/images/favicon/mstile-70x70.png delete mode 100644 htdocs/src/images/favicon/safari-pinned-tab.svg delete mode 100644 htdocs/src/images/favicon/site.webmanifest delete mode 100644 htdocs/src/images/github.svg delete mode 100644 htdocs/src/images/gitlab.svg delete mode 100644 htdocs/src/js/app.js delete mode 100644 htdocs/src/js/config/projects.js delete mode 100644 htdocs/src/js/i18n/i18n.js delete mode 100644 htdocs/src/js/i18n/locales/en.js delete mode 100644 htdocs/src/js/i18n/locales/fr.js delete mode 100644 htdocs/src/js/utilities/animations.js delete mode 100644 htdocs/src/js/utilities/helpers.js delete mode 100644 htdocs/src/scss/abstracts/_functions.scss delete mode 100644 htdocs/src/scss/abstracts/_mixins.scss delete mode 100644 htdocs/src/scss/abstracts/_placeholders.scss delete mode 100644 htdocs/src/scss/abstracts/_variables.scss delete mode 100644 htdocs/src/scss/abstracts/functions/_convert.scss delete mode 100644 htdocs/src/scss/abstracts/functions/_css-vars.scss delete mode 100644 htdocs/src/scss/abstracts/functions/_encode.scss delete mode 100644 htdocs/src/scss/abstracts/functions/_str-replace.scss delete mode 100644 htdocs/src/scss/abstracts/mixins/_css-vars.scss delete mode 100644 htdocs/src/scss/abstracts/mixins/_media-queries.scss delete mode 100644 htdocs/src/scss/base/_animations.scss delete mode 100644 htdocs/src/scss/base/_base.scss delete mode 100644 htdocs/src/scss/base/_colors.scss delete mode 100644 htdocs/src/scss/base/_fonts.scss delete mode 100644 htdocs/src/scss/base/_helpers.scss delete mode 100644 htdocs/src/scss/base/_spacings.scss delete mode 100644 htdocs/src/scss/base/_typography.scss delete mode 100644 htdocs/src/scss/components/_buttons.scss delete mode 100644 htdocs/src/scss/layout/_footer.scss delete mode 100644 htdocs/src/scss/layout/_grid.scss delete mode 100644 htdocs/src/scss/layout/_header.scss delete mode 100644 htdocs/src/scss/layout/_main.scss delete mode 100644 htdocs/src/scss/layout/_nav.scss delete mode 100644 htdocs/src/scss/layout/_toolbar.scss delete mode 100644 htdocs/src/scss/style.scss create mode 100644 index.html create mode 100644 legal-notice.html create mode 100644 license.html create mode 100644 mentions-legales.html create mode 160000 projects/js-small-apps create mode 160000 projects/react-small-apps create mode 100644 src/fonts/Inter/Inter.woff2 create mode 100644 src/fonts/Inter/LICENSE.txt create mode 100644 src/fonts/Kanit/Kanit-Bold.woff create mode 100644 src/fonts/Kanit/Kanit-Bold.woff2 create mode 100644 src/fonts/Kanit/Kanit-BoldItalic.woff create mode 100644 src/fonts/Kanit/Kanit-BoldItalic.woff2 create mode 100644 src/fonts/Kanit/Kanit-Italic.woff create mode 100644 src/fonts/Kanit/Kanit-Italic.woff2 create mode 100644 src/fonts/Kanit/Kanit-Light.woff create mode 100644 src/fonts/Kanit/Kanit-Light.woff2 create mode 100644 src/fonts/Kanit/Kanit-LightItalic.woff create mode 100644 src/fonts/Kanit/Kanit-LightItalic.woff2 create mode 100644 src/fonts/Kanit/Kanit-Medium.woff create mode 100644 src/fonts/Kanit/Kanit-Medium.woff2 create mode 100644 src/fonts/Kanit/Kanit-MediumItalic.woff create mode 100644 src/fonts/Kanit/Kanit-MediumItalic.woff2 create mode 100644 src/fonts/Kanit/Kanit-Regular.woff create mode 100644 src/fonts/Kanit/Kanit-Regular.woff2 create mode 100644 src/fonts/Kanit/Kanit-SemiBold.woff create mode 100644 src/fonts/Kanit/Kanit-SemiBold.woff2 create mode 100644 src/fonts/Kanit/Kanit-SemiBoldItalic.woff create mode 100644 src/fonts/Kanit/Kanit-SemiBoldItalic.woff2 create mode 100644 src/fonts/Kanit/OFL.txt create mode 100644 src/images/armand-philippot-logo.svg create mode 100644 src/images/armand-philippot.jpg create mode 100644 src/images/cc-by-sa.svg create mode 100644 src/images/favicon/android-chrome-192x192.png create mode 100644 src/images/favicon/android-chrome-512x512.png create mode 100644 src/images/favicon/apple-touch-icon.png create mode 100644 src/images/favicon/browserconfig.xml create mode 100644 src/images/favicon/favicon-16x16.png create mode 100644 src/images/favicon/favicon-32x32.png create mode 100644 src/images/favicon/favicon.ico create mode 100644 src/images/favicon/mstile-144x144.png create mode 100644 src/images/favicon/mstile-150x150.png create mode 100644 src/images/favicon/mstile-310x150.png create mode 100644 src/images/favicon/mstile-310x310.png create mode 100644 src/images/favicon/mstile-70x70.png create mode 100644 src/images/favicon/safari-pinned-tab.svg create mode 100644 src/images/favicon/site.webmanifest create mode 100644 src/images/github.svg create mode 100644 src/images/gitlab.svg create mode 100644 src/js/app.js create mode 100644 src/js/config/projects.js create mode 100644 src/js/i18n/i18n.js create mode 100644 src/js/i18n/locales/en.js create mode 100644 src/js/i18n/locales/fr.js create mode 100644 src/js/utilities/animations.js create mode 100644 src/js/utilities/helpers.js create mode 100644 src/scss/abstracts/_functions.scss create mode 100644 src/scss/abstracts/_mixins.scss create mode 100644 src/scss/abstracts/_placeholders.scss create mode 100644 src/scss/abstracts/_variables.scss create mode 100644 src/scss/abstracts/functions/_convert.scss create mode 100644 src/scss/abstracts/functions/_css-vars.scss create mode 100644 src/scss/abstracts/functions/_encode.scss create mode 100644 src/scss/abstracts/functions/_str-replace.scss create mode 100644 src/scss/abstracts/mixins/_css-vars.scss create mode 100644 src/scss/abstracts/mixins/_media-queries.scss create mode 100644 src/scss/base/_animations.scss create mode 100644 src/scss/base/_base.scss create mode 100644 src/scss/base/_colors.scss create mode 100644 src/scss/base/_fonts.scss create mode 100644 src/scss/base/_helpers.scss create mode 100644 src/scss/base/_spacings.scss create mode 100644 src/scss/base/_typography.scss create mode 100644 src/scss/components/_buttons.scss create mode 100644 src/scss/layout/_footer.scss create mode 100644 src/scss/layout/_grid.scss create mode 100644 src/scss/layout/_header.scss create mode 100644 src/scss/layout/_main.scss create mode 100644 src/scss/layout/_nav.scss create mode 100644 src/scss/layout/_toolbar.scss create mode 100644 src/scss/style.scss diff --git a/.gitignore b/.gitignore index 5f5dda1..d518723 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ logs # Misc .vscode .eslintcache -htdocs/assets/hmr +assets/hmr # Dotenv .env* diff --git a/.gitmodules b/.gitmodules index fb25397..8249105 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ -[submodule "htdocs/projects/js-small-apps"] - path = htdocs/projects/js-small-apps +[submodule "projects/js-small-apps"] + path = projects/js-small-apps url = git@github.com:ArmandPhilippot/js-small-apps.git -[submodule "htdocs/projects/react-small-apps"] - path = htdocs/projects/react-small-apps +[submodule "projects/react-small-apps"] + path = projects/react-small-apps url = git@github.com:ArmandPhilippot/react-small-apps.git diff --git a/assets/fonts/Inter/Inter.woff2 b/assets/fonts/Inter/Inter.woff2 new file mode 100644 index 0000000..365eedc Binary files /dev/null and b/assets/fonts/Inter/Inter.woff2 differ diff --git a/assets/fonts/Inter/LICENSE.txt b/assets/fonts/Inter/LICENSE.txt new file mode 100644 index 0000000..ff80f8c --- /dev/null +++ b/assets/fonts/Inter/LICENSE.txt @@ -0,0 +1,94 @@ +Copyright (c) 2016-2020 The Inter Project Authors. +"Inter" is trademark of Rasmus Andersson. +https://github.com/rsms/inter + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION AND CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/assets/fonts/Kanit/Kanit-Bold.woff b/assets/fonts/Kanit/Kanit-Bold.woff new file mode 100644 index 0000000..7602cee Binary files /dev/null and b/assets/fonts/Kanit/Kanit-Bold.woff differ diff --git a/assets/fonts/Kanit/Kanit-Bold.woff2 b/assets/fonts/Kanit/Kanit-Bold.woff2 new file mode 100644 index 0000000..8494234 Binary files /dev/null and b/assets/fonts/Kanit/Kanit-Bold.woff2 differ diff --git a/assets/fonts/Kanit/Kanit-BoldItalic.woff b/assets/fonts/Kanit/Kanit-BoldItalic.woff new file mode 100644 index 0000000..af36319 Binary files /dev/null and b/assets/fonts/Kanit/Kanit-BoldItalic.woff differ diff --git a/assets/fonts/Kanit/Kanit-BoldItalic.woff2 b/assets/fonts/Kanit/Kanit-BoldItalic.woff2 new file mode 100644 index 0000000..61f04ef Binary files /dev/null and b/assets/fonts/Kanit/Kanit-BoldItalic.woff2 differ diff --git a/assets/fonts/Kanit/Kanit-Italic.woff b/assets/fonts/Kanit/Kanit-Italic.woff new file mode 100644 index 0000000..462f295 Binary files /dev/null and b/assets/fonts/Kanit/Kanit-Italic.woff differ diff --git a/assets/fonts/Kanit/Kanit-Italic.woff2 b/assets/fonts/Kanit/Kanit-Italic.woff2 new file mode 100644 index 0000000..1723709 Binary files /dev/null and b/assets/fonts/Kanit/Kanit-Italic.woff2 differ diff --git a/assets/fonts/Kanit/Kanit-Light.woff b/assets/fonts/Kanit/Kanit-Light.woff new file mode 100644 index 0000000..872ce1b Binary files /dev/null and b/assets/fonts/Kanit/Kanit-Light.woff differ diff --git a/assets/fonts/Kanit/Kanit-Light.woff2 b/assets/fonts/Kanit/Kanit-Light.woff2 new file mode 100644 index 0000000..6b35eda Binary files /dev/null and b/assets/fonts/Kanit/Kanit-Light.woff2 differ diff --git a/assets/fonts/Kanit/Kanit-LightItalic.woff b/assets/fonts/Kanit/Kanit-LightItalic.woff new file mode 100644 index 0000000..b81b7b3 Binary files /dev/null and b/assets/fonts/Kanit/Kanit-LightItalic.woff differ diff --git a/assets/fonts/Kanit/Kanit-LightItalic.woff2 b/assets/fonts/Kanit/Kanit-LightItalic.woff2 new file mode 100644 index 0000000..f933a2c Binary files /dev/null and b/assets/fonts/Kanit/Kanit-LightItalic.woff2 differ diff --git a/assets/fonts/Kanit/Kanit-Medium.woff b/assets/fonts/Kanit/Kanit-Medium.woff new file mode 100644 index 0000000..d5b3510 Binary files /dev/null and b/assets/fonts/Kanit/Kanit-Medium.woff differ diff --git a/assets/fonts/Kanit/Kanit-Medium.woff2 b/assets/fonts/Kanit/Kanit-Medium.woff2 new file mode 100644 index 0000000..a012e98 Binary files /dev/null and b/assets/fonts/Kanit/Kanit-Medium.woff2 differ diff --git a/assets/fonts/Kanit/Kanit-MediumItalic.woff b/assets/fonts/Kanit/Kanit-MediumItalic.woff new file mode 100644 index 0000000..6859f58 Binary files /dev/null and b/assets/fonts/Kanit/Kanit-MediumItalic.woff differ diff --git a/assets/fonts/Kanit/Kanit-MediumItalic.woff2 b/assets/fonts/Kanit/Kanit-MediumItalic.woff2 new file mode 100644 index 0000000..8af9298 Binary files /dev/null and b/assets/fonts/Kanit/Kanit-MediumItalic.woff2 differ diff --git a/assets/fonts/Kanit/Kanit-Regular.woff b/assets/fonts/Kanit/Kanit-Regular.woff new file mode 100644 index 0000000..eec0b42 Binary files /dev/null and b/assets/fonts/Kanit/Kanit-Regular.woff differ diff --git a/assets/fonts/Kanit/Kanit-Regular.woff2 b/assets/fonts/Kanit/Kanit-Regular.woff2 new file mode 100644 index 0000000..9b925bf Binary files /dev/null and b/assets/fonts/Kanit/Kanit-Regular.woff2 differ diff --git a/assets/fonts/Kanit/Kanit-SemiBold.woff b/assets/fonts/Kanit/Kanit-SemiBold.woff new file mode 100644 index 0000000..dacdb5b Binary files /dev/null and b/assets/fonts/Kanit/Kanit-SemiBold.woff differ diff --git a/assets/fonts/Kanit/Kanit-SemiBold.woff2 b/assets/fonts/Kanit/Kanit-SemiBold.woff2 new file mode 100644 index 0000000..52d4527 Binary files /dev/null and b/assets/fonts/Kanit/Kanit-SemiBold.woff2 differ diff --git a/assets/fonts/Kanit/Kanit-SemiBoldItalic.woff b/assets/fonts/Kanit/Kanit-SemiBoldItalic.woff new file mode 100644 index 0000000..ebf0137 Binary files /dev/null and b/assets/fonts/Kanit/Kanit-SemiBoldItalic.woff differ diff --git a/assets/fonts/Kanit/Kanit-SemiBoldItalic.woff2 b/assets/fonts/Kanit/Kanit-SemiBoldItalic.woff2 new file mode 100644 index 0000000..300d77a Binary files /dev/null and b/assets/fonts/Kanit/Kanit-SemiBoldItalic.woff2 differ diff --git a/assets/fonts/Kanit/OFL.txt b/assets/fonts/Kanit/OFL.txt new file mode 100644 index 0000000..0f48ea4 --- /dev/null +++ b/assets/fonts/Kanit/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Kanit Project Authors (https://github.com/cadsondemak/kanit) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/assets/images/armand-philippot-logo.svg b/assets/images/armand-philippot-logo.svg new file mode 100644 index 0000000..2063b14 --- /dev/null +++ b/assets/images/armand-philippot-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/images/armand-philippot.jpg b/assets/images/armand-philippot.jpg new file mode 100644 index 0000000..6895716 Binary files /dev/null and b/assets/images/armand-philippot.jpg differ diff --git a/assets/images/cc-by-sa.svg b/assets/images/cc-by-sa.svg new file mode 100644 index 0000000..4274ea8 --- /dev/null +++ b/assets/images/cc-by-sa.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/images/favicon/android-chrome-192x192.png b/assets/images/favicon/android-chrome-192x192.png new file mode 100644 index 0000000..b37fed6 Binary files /dev/null and b/assets/images/favicon/android-chrome-192x192.png differ diff --git a/assets/images/favicon/android-chrome-512x512.png b/assets/images/favicon/android-chrome-512x512.png new file mode 100644 index 0000000..7fa0021 Binary files /dev/null and b/assets/images/favicon/android-chrome-512x512.png differ diff --git a/assets/images/favicon/apple-touch-icon.png b/assets/images/favicon/apple-touch-icon.png new file mode 100644 index 0000000..3fb2467 Binary files /dev/null and b/assets/images/favicon/apple-touch-icon.png differ diff --git a/assets/images/favicon/browserconfig.xml b/assets/images/favicon/browserconfig.xml new file mode 100644 index 0000000..f9c2e67 --- /dev/null +++ b/assets/images/favicon/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #2b5797 + + + diff --git a/assets/images/favicon/favicon-16x16.png b/assets/images/favicon/favicon-16x16.png new file mode 100644 index 0000000..03c11e6 Binary files /dev/null and b/assets/images/favicon/favicon-16x16.png differ diff --git a/assets/images/favicon/favicon-32x32.png b/assets/images/favicon/favicon-32x32.png new file mode 100644 index 0000000..63b26a0 Binary files /dev/null and b/assets/images/favicon/favicon-32x32.png differ diff --git a/assets/images/favicon/favicon.ico b/assets/images/favicon/favicon.ico new file mode 100644 index 0000000..d1d41a8 Binary files /dev/null and b/assets/images/favicon/favicon.ico differ diff --git a/assets/images/favicon/mstile-144x144.png b/assets/images/favicon/mstile-144x144.png new file mode 100644 index 0000000..c359117 Binary files /dev/null and b/assets/images/favicon/mstile-144x144.png differ diff --git a/assets/images/favicon/mstile-150x150.png b/assets/images/favicon/mstile-150x150.png new file mode 100644 index 0000000..bd1ed24 Binary files /dev/null and b/assets/images/favicon/mstile-150x150.png differ diff --git a/assets/images/favicon/mstile-310x150.png b/assets/images/favicon/mstile-310x150.png new file mode 100644 index 0000000..4c34b22 Binary files /dev/null and b/assets/images/favicon/mstile-310x150.png differ diff --git a/assets/images/favicon/mstile-310x310.png b/assets/images/favicon/mstile-310x310.png new file mode 100644 index 0000000..5285a9f Binary files /dev/null and b/assets/images/favicon/mstile-310x310.png differ diff --git a/assets/images/favicon/mstile-70x70.png b/assets/images/favicon/mstile-70x70.png new file mode 100644 index 0000000..55466ef Binary files /dev/null and b/assets/images/favicon/mstile-70x70.png differ diff --git a/assets/images/favicon/safari-pinned-tab.svg b/assets/images/favicon/safari-pinned-tab.svg new file mode 100644 index 0000000..d9cf57f --- /dev/null +++ b/assets/images/favicon/safari-pinned-tab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/images/favicon/site.webmanifest b/assets/images/favicon/site.webmanifest new file mode 100644 index 0000000..31df508 --- /dev/null +++ b/assets/images/favicon/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "Armand Philippot", + "short_name": "AP", + "icons": [ + { + "src": "/wp-content/themes/apcom/assets/images/favicon/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/wp-content/themes/apcom/assets/images/favicon/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#194476", + "background_color": "#194476", + "display": "standalone" +} diff --git a/assets/images/github.svg b/assets/images/github.svg new file mode 100644 index 0000000..02541eb --- /dev/null +++ b/assets/images/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/images/gitlab.svg b/assets/images/gitlab.svg new file mode 100644 index 0000000..b3dd6f1 --- /dev/null +++ b/assets/images/gitlab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/js/app.js b/assets/js/app.js new file mode 100644 index 0000000..0a9f472 --- /dev/null +++ b/assets/js/app.js @@ -0,0 +1,7285 @@ +(self["webpackChunkdemo_armandphilippot_com"] = self["webpackChunkdemo_armandphilippot_com"] || []).push([["scripts"],{ + +/***/ "./node_modules/@babel/runtime/regenerator/index.js": +/*!**********************************************************!*\ + !*** ./node_modules/@babel/runtime/regenerator/index.js ***! + \**********************************************************/ +/***/ (function(module, __unused_webpack_exports, __webpack_require__) { + +module.exports = __webpack_require__(/*! regenerator-runtime */ "./node_modules/regenerator-runtime/runtime.js"); + + +/***/ }), + +/***/ "./node_modules/ansi-html-community/index.js": +/*!***************************************************!*\ + !*** ./node_modules/ansi-html-community/index.js ***! + \***************************************************/ +/***/ (function(module) { + +"use strict"; + + +module.exports = ansiHTML + +// Reference to https://github.com/sindresorhus/ansi-regex +var _regANSI = /(?:(?:\u001b\[)|\u009b)(?:(?:[0-9]{1,3})?(?:(?:;[0-9]{0,3})*)?[A-M|f-m])|\u001b[A-M]/ + +var _defColors = { + reset: ['fff', '000'], // [FOREGROUD_COLOR, BACKGROUND_COLOR] + black: '000', + red: 'ff0000', + green: '209805', + yellow: 'e8bf03', + blue: '0000ff', + magenta: 'ff00ff', + cyan: '00ffee', + lightgrey: 'f0f0f0', + darkgrey: '888' +} +var _styles = { + 30: 'black', + 31: 'red', + 32: 'green', + 33: 'yellow', + 34: 'blue', + 35: 'magenta', + 36: 'cyan', + 37: 'lightgrey' +} +var _openTags = { + '1': 'font-weight:bold', // bold + '2': 'opacity:0.5', // dim + '3': '', // italic + '4': '', // underscore + '8': 'display:none', // hidden + '9': '' // delete +} +var _closeTags = { + '23': '', // reset italic + '24': '', // reset underscore + '29': '' // reset delete +} + +;[0, 21, 22, 27, 28, 39, 49].forEach(function (n) { + _closeTags[n] = '' +}) + +/** + * Converts text with ANSI color codes to HTML markup. + * @param {String} text + * @returns {*} + */ +function ansiHTML (text) { + // Returns the text if the string has no ANSI escape code. + if (!_regANSI.test(text)) { + return text + } + + // Cache opened sequence. + var ansiCodes = [] + // Replace with markup. + var ret = text.replace(/\033\[(\d+)m/g, function (match, seq) { + var ot = _openTags[seq] + if (ot) { + // If current sequence has been opened, close it. + if (!!~ansiCodes.indexOf(seq)) { // eslint-disable-line no-extra-boolean-cast + ansiCodes.pop() + return '' + } + // Open tag. + ansiCodes.push(seq) + return ot[0] === '<' ? ot : '' + } + + var ct = _closeTags[seq] + if (ct) { + // Pop sequence + ansiCodes.pop() + return ct + } + return '' + }) + + // Make sure tags are closed. + var l = ansiCodes.length + ;(l > 0) && (ret += Array(l + 1).join('')) + + return ret +} + +/** + * Customize colors. + * @param {Object} colors reference to _defColors + */ +ansiHTML.setColors = function (colors) { + if (typeof colors !== 'object') { + throw new Error('`colors` parameter must be an Object.') + } + + var _finalColors = {} + for (var key in _defColors) { + var hex = colors.hasOwnProperty(key) ? colors[key] : null + if (!hex) { + _finalColors[key] = _defColors[key] + continue + } + if ('reset' === key) { + if (typeof hex === 'string') { + hex = [hex] + } + if (!Array.isArray(hex) || hex.length === 0 || hex.some(function (h) { + return typeof h !== 'string' + })) { + throw new Error('The value of `' + key + '` property must be an Array and each item could only be a hex string, e.g.: FF0000') + } + var defHexColor = _defColors[key] + if (!hex[0]) { + hex[0] = defHexColor[0] + } + if (hex.length === 1 || !hex[1]) { + hex = [hex[0]] + hex.push(defHexColor[1]) + } + + hex = hex.slice(0, 2) + } else if (typeof hex !== 'string') { + throw new Error('The value of `' + key + '` property must be a hex string, e.g.: FF0000') + } + _finalColors[key] = hex + } + _setTags(_finalColors) +} + +/** + * Reset colors. + */ +ansiHTML.reset = function () { + _setTags(_defColors) +} + +/** + * Expose tags, including open and close. + * @type {Object} + */ +ansiHTML.tags = {} + +if (Object.defineProperty) { + Object.defineProperty(ansiHTML.tags, 'open', { + get: function () { return _openTags } + }) + Object.defineProperty(ansiHTML.tags, 'close', { + get: function () { return _closeTags } + }) +} else { + ansiHTML.tags.open = _openTags + ansiHTML.tags.close = _closeTags +} + +function _setTags (colors) { + // reset all + _openTags['0'] = 'font-weight:normal;opacity:1;color:#' + colors.reset[0] + ';background:#' + colors.reset[1] + // inverse + _openTags['7'] = 'color:#' + colors.reset[1] + ';background:#' + colors.reset[0] + // dark grey + _openTags['90'] = 'color:#' + colors.darkgrey + + for (var code in _styles) { + var color = _styles[code] + var oriColor = colors[color] || '000' + _openTags[code] = 'color:#' + oriColor + code = parseInt(code) + _openTags[(code + 10).toString()] = 'background:#' + oriColor + } +} + +ansiHTML.reset() + + +/***/ }), + +/***/ "./src/js/app.js": +/*!***********************!*\ + !*** ./src/js/app.js ***! + \***********************/ +/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _babel_runtime_helpers_toConsumableArray__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/toConsumableArray */ "./node_modules/@babel/runtime/helpers/esm/toConsumableArray.js"); +/* harmony import */ var _config_projects__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./config/projects */ "./src/js/config/projects.js"); +/* harmony import */ var _i18n_i18n__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./i18n/i18n */ "./src/js/i18n/i18n.js"); +/* harmony import */ var _utilities_animations__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./utilities/animations */ "./src/js/utilities/animations.js"); +/* harmony import */ var _utilities_helpers__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./utilities/helpers */ "./src/js/utilities/helpers.js"); + + + + + +/** + * Show/hide header and footer with slide animation (left). + */ + +function toggleHeaderFooter() { + var header = document.querySelector('header'); + var footer = document.querySelector('footer'); + var elements = [header, footer]; + elements.forEach(function (el) { + if (el.classList.contains('hide')) { + (0,_utilities_animations__WEBPACK_IMPORTED_MODULE_3__.showFromLeft)(el); + } else { + (0,_utilities_animations__WEBPACK_IMPORTED_MODULE_3__.hideToLeft)(el); + } + }); +} +/** + * Show/hide project details with slide animation (bottom). + */ + + +function toggleProjectDetails() { + var details = document.querySelector('.project-details'); + + if (details.classList.contains('hide')) { + (0,_utilities_animations__WEBPACK_IMPORTED_MODULE_3__.showFromBottom)(details); + } else { + (0,_utilities_animations__WEBPACK_IMPORTED_MODULE_3__.hideToBottom)(details); + } +} +/** + * Add an event listener to show or hide header and footer. + */ + + +function listenMenuBtn() { + var menuBtn = document.querySelector('.btn--menu'); + menuBtn.addEventListener('click', toggleHeaderFooter); +} +/** + * Show or hide the project details button depending on current location. + */ + + +function toggleProjectDetailsBtn() { + var button = document.querySelector('.btn--details'); + var currentPath = window.location.hash; + + if (currentPath) { + button.style.display = ''; + } else { + button.style.display = 'none'; + } +} +/** + * Update the visibility of some DOM elements depending on viewport. + */ + + +function updateView() { + var header = document.querySelector('header'); + var footer = document.querySelector('footer'); + var toolbar = document.querySelector('.toolbar'); + var details = document.querySelector('.project-details'); + + if ((0,_utilities_helpers__WEBPACK_IMPORTED_MODULE_4__.isSmallVw)()) { + header.classList.add('hide'); + footer.classList.add('hide'); + toolbar.classList.remove('hide'); + details === null || details === void 0 ? void 0 : details.classList.add('hide'); + details === null || details === void 0 ? void 0 : details.classList.remove('fade-in'); + } else { + (0,_utilities_animations__WEBPACK_IMPORTED_MODULE_3__.showFromLeft)(header); + (0,_utilities_animations__WEBPACK_IMPORTED_MODULE_3__.showFromLeft)(footer); + toolbar.classList.add('hide'); + details === null || details === void 0 ? void 0 : details.classList.remove('hide'); + details === null || details === void 0 ? void 0 : details.classList.add('fade-in'); + } + + toggleProjectDetailsBtn(); +} +/** + * Update view when the window size changes. + */ + + +function listenWindowSize() { + window.addEventListener('resize', updateView); +} +/** + * Retrieve a project by id. + * @param {Integer} id - The project id. + * @returns {Object} The current project. + */ + + +function getCurrentProject(id) { + return _config_projects__WEBPACK_IMPORTED_MODULE_1__["default"].find(function (project) { + return project.id === id; + }); +} +/** + * Get a list item for the given repo. + * @param {String} name - The repository name. + * @param {String} url - The repository URL. + * @returns {HTMLElement} A list item. + */ + + +function getRepoItem(name, url) { + var item = document.createElement('li'); + var link = document.createElement('a'); + var span = document.createElement('span'); + span.classList.add('screen-reader-text'); + span.textContent = name; + link.classList.add('list__link', "list__link--".concat(name.toLocaleLowerCase())); + link.href = url; + link.appendChild(span); + item.classList.add('list__item'); + item.appendChild(link); + return item; +} +/** + * Get the repos list wrapped inside ul element and the title. + * @param {Object[]} repos - An array of repo with name and URL. + * @returns {[title, list]} An array of HTMLElements for title and list. + */ + + +function getRepos(repos) { + if (repos.length === 0) return []; + var wrapper = document.createElement('div'); + var title = document.createElement('h3'); + var list = document.createElement('ul'); + var items = repos.map(function (repo) { + return getRepoItem(repo.name, repo.url); + }); + title.classList.add('project-details__title'); + title.textContent = (0,_i18n_i18n__WEBPACK_IMPORTED_MODULE_2__.translate)('main.project.details.repo', { + count: repos.length + }); + list.classList.add('list', 'list--repos'); + list.append.apply(list, (0,_babel_runtime_helpers_toConsumableArray__WEBPACK_IMPORTED_MODULE_0__["default"])(items)); + wrapper.append(title, list); + return [title, list]; +} +/** + * Get the technologies list wrapped inside ul element and the title. + * @param {String[]} technologies - An array of technology names. + * @returns {[title, list]} An array of HTMLElements for title and list. + */ + + +function getTechs(technologies) { + if (technologies.length === 0) return []; + var title = document.createElement('h3'); + title.classList.add('project-details__title'); + title.textContent = (0,_i18n_i18n__WEBPACK_IMPORTED_MODULE_2__.translate)('main.project.details.tech', { + count: technologies.length + }); + var list = document.createElement('ul'); + var items = technologies.map(function (technology) { + var item = document.createElement('li'); + item.textContent = technology; + return item; + }); + list.classList.add('list', 'list--tech'); + list.append.apply(list, (0,_babel_runtime_helpers_toConsumableArray__WEBPACK_IMPORTED_MODULE_0__["default"])(items)); + return [title, list]; +} +/** + * Retrieve the project details. + * @param {Object} project - The project. + * @returns {HTMLElement} The project details wrapped in a div. + */ + + +function getProjectDetails(project) { + var details = document.createElement('div'); + var title = document.createElement('h2'); + var techList = project !== null && project !== void 0 && project.technologies ? getTechs(project.technologies) : []; + var reposList = getRepos(project.repo); + var locale = (0,_i18n_i18n__WEBPACK_IMPORTED_MODULE_2__.currentLocale)(); + var description; + + if (project.description) { + description = document.createElement('div'); + description.classList.add('project-details__description'); + description.textContent = project.description[locale] || ''; + } else { + description = ''; + } + + title.classList.add('project-details__title'); + title.textContent = (0,_i18n_i18n__WEBPACK_IMPORTED_MODULE_2__.translate)('main.project.details.about', { + name: project.name + }); + details.classList.add('project-details'); + if (!(0,_utilities_helpers__WEBPACK_IMPORTED_MODULE_4__.isSmallVw)()) details.classList.add('fade-in'); + details.replaceChildren.apply(details, [title, description].concat((0,_babel_runtime_helpers_toConsumableArray__WEBPACK_IMPORTED_MODULE_0__["default"])(techList), (0,_babel_runtime_helpers_toConsumableArray__WEBPACK_IMPORTED_MODULE_0__["default"])(reposList))); + return details; +} +/** + * Get an iframe for the given path/url. + * @param {String} src - The path/url to use as source. + * @returns {HTMLElement} The iframe. + */ + + +function getIframe(src) { + var iframe = document.createElement('iframe'); + iframe.src = src; + return iframe; +} +/** + * Retrieve the project preview. + * @param {String} projectPath - The project path. + * @returns {HTMLElement} The project preview wrapped in a div. + */ + + +function getProjectPreview(projectPath) { + var preview = document.createElement('div'); + var iframe = getIframe(projectPath); + preview.classList.add('project-preview', 'fade-in'); + preview.replaceChildren(iframe); + return preview; +} +/** + * Display the targeted project. + * @param {String} id - The project id. + */ + + +function showProject(id) { + var currentProject = getCurrentProject(id); + var main = document.querySelector('.main'); + var details = getProjectDetails(currentProject); + var preview = getProjectPreview(currentProject.path); + var detailsBtn = document.querySelector('.btn--details'); + if ((0,_utilities_helpers__WEBPACK_IMPORTED_MODULE_4__.isSmallVw)()) details.classList.add('hide'); + detailsBtn.textContent = (0,_i18n_i18n__WEBPACK_IMPORTED_MODULE_2__.translate)('main.project.details.about', { + name: currentProject.name + }); + detailsBtn.addEventListener('click', toggleProjectDetails); + window.history.pushState({}, currentProject.name, "/#".concat(id)); + document.title = "".concat(currentProject.name, " | Demo | Armand Philippot"); + main.replaceChildren(preview, details); +} +/** + * Add a CSS class to the current project in projects nav. + * @param {String} id - The project id. + */ + + +function setSelectedProject(id) { + var links = document.querySelectorAll('.nav__link'); + links.forEach(function (link) { + if (link.id === id) { + link.classList.add('nav__link--selected'); + } else { + link.classList.remove('nav__link--selected'); + } + }); +} +/** + * Create a list item for a project. + * @param {String} id - The project id. + * @param {String} name - The project name. + * @returns {HTMLElement} The list item. + */ + + +function getProjectsNavItem(id, name) { + var item = document.createElement('li'); + var link = document.createElement('a'); + link.classList.add('nav__link'); + link.href = "/#".concat(id); + link.id = id; + link.textContent = name; + link.addEventListener('click', function (e) { + e.preventDefault(); + showProject(id); + setSelectedProject(id); + toggleProjectDetailsBtn(); + if ((0,_utilities_helpers__WEBPACK_IMPORTED_MODULE_4__.isSmallVw)()) toggleHeaderFooter(); + }); + item.classList.add('nav__item'); + item.appendChild(link); + return item; +} +/** + * Print the list of available projects. + */ + + +function printProjectsNav() { + var ul = document.querySelector('.nav .nav__list'); + _config_projects__WEBPACK_IMPORTED_MODULE_1__["default"].forEach(function (project) { + var item = getProjectsNavItem(project.id, project.name); + ul.appendChild(item); + }); +} +/** + * Add style.js script for development purposes. + */ + + +function loadWebpackStyles() { + if ((0,_utilities_helpers__WEBPACK_IMPORTED_MODULE_4__.isStyleJsExists)()) { + var head = document.querySelector('head'); + var script = document.createElement('script'); + script.src = 'assets/js/style.js'; + head.appendChild(script); + } +} +/** + * Load corresponding project if the requested page contains a hash. + */ + + +function printRequestedPage() { + var currentPath = window.location.hash; + + if (currentPath) { + var id = currentPath.replace('#', ''); + showProject(id); + setSelectedProject(id); + } +} +/** + * Replace the legal notice link and text. + */ + + +function replaceLegalNoticeLink() { + var link = document.querySelector('.nav__link--legal'); + link.href = (0,_i18n_i18n__WEBPACK_IMPORTED_MODULE_2__.translate)('footer.legalNotice.link'); + link.textContent = (0,_i18n_i18n__WEBPACK_IMPORTED_MODULE_2__.translate)('footer.legalNotice.txt'); +} +/** + * Translate all text available in HTML templates. + */ + + +function translateHTMLContent() { + var brandingDesc = document.querySelector('.branding__description'); + var navLabel = document.querySelector('.nav__label'); + var license = document.querySelector('.copyright__license'); + var instructions = document.querySelector('.instructions'); + brandingDesc.textContent = (0,_i18n_i18n__WEBPACK_IMPORTED_MODULE_2__.translate)('branding.description'); + navLabel.textContent = (0,_i18n_i18n__WEBPACK_IMPORTED_MODULE_2__.translate)('nav.title'); + license.title = (0,_i18n_i18n__WEBPACK_IMPORTED_MODULE_2__.translate)('footer.license'); + if (instructions) instructions.textContent = (0,_i18n_i18n__WEBPACK_IMPORTED_MODULE_2__.translate)('main.instructions'); +} +/** + * Translate the website according to the user preferred language. + */ + + +function setAppLocale() { + var preferredLanguage = navigator.language; + var supportedLanguage = _i18n_i18n__WEBPACK_IMPORTED_MODULE_2__.supportedLanguages.find(function (lang) { + return preferredLanguage.startsWith(lang.code); + } // eslint-disable-next-line function-paren-newline -- Conflict with Prettier + ); + var locale = (supportedLanguage === null || supportedLanguage === void 0 ? void 0 : supportedLanguage.code) || 'en'; + (0,_i18n_i18n__WEBPACK_IMPORTED_MODULE_2__.setLocale)(locale); +} +/** + * Initialize the website with the projects list. + */ + + +function init() { + setAppLocale(); + translateHTMLContent(); + replaceLegalNoticeLink(); + loadWebpackStyles(); + printProjectsNav(); + updateView(); + listenWindowSize(); + listenMenuBtn(); + printRequestedPage(); +} + +init(); + +/***/ }), + +/***/ "./src/js/config/projects.js": +/*!***********************************!*\ + !*** ./src/js/config/projects.js ***! + \***********************************/ +/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +var projects = [{ + id: 'bin2dec', + name: 'Bin2Dec', + description: { + en: 'Convert a binary string to a decimal number.', + fr: 'Convertit un nombre binaire en un nombre décimal.' + }, + path: './projects/js-small-apps/bin2dec/index.html', + repo: [{ + name: 'Github', + url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/bin2dec' + }, { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/bin2dec' + }], + technologies: ['Vanilla Javascript'] +}, { + id: 'budget-app', + name: 'Budget App', + description: { + en: 'By selecting a language in the initialization form, only the currency is converted (the app is not translated). Also, no data is saved on page reload.', + fr: "En sélectionnant une langue dans le formulaire d'initialisation, seul le format des nombres change (l'application n'est pas traduite). Aucune donnée n'est conservée après rechargement de la page." + }, + path: './projects/js-small-apps/budget-app/index.html', + repo: [{ + name: 'Github', + url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/budget-app' + }, { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/budget-app' + }], + technologies: ['Vanilla Javascript'] +}, { + id: 'calculator', + name: 'Calculator', + description: { + en: 'A basic calculator. Decimal part is limited to 3 digits. The first part is limited to 8 digits. If the result does not respect these limits, you will see an error.', + fr: 'Une simple calculette. La partie décimale est limitée à 3 chiffres. La première partie est limitée à 8 chiffres. Si le résultat ne respecte pas ces limites, vous verrez une erreur.' + }, + path: './projects/js-small-apps/calculator/index.html', + repo: [{ + name: 'Github', + url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/calculator' + }, { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/calculator' + }], + technologies: ['Vanilla Javascript'] +}, { + id: 'clock', + name: 'Clock', + description: { + en: 'What time is it? You can have the current hour in three formats: an analogic clock, a numeric display or a text.', + fr: "Quelle heure est-il ? Vous pouvez voir l'heure actuelle dans trois formats différents : une horloge analogique, un affichage numérique et sous forme de texte." + }, + path: './projects/js-small-apps/clock/index.html', + repo: [{ + name: 'Github', + url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/clock' + }, { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/clock' + }], + technologies: ['Vanilla Javascript', 'SVG'] +}, { + id: 'color-cycle', + name: 'Color cycle', + description: { + en: 'Play with hexadecimal colors. Set a color, then choose one or more increment values and start the preview.', + fr: "Jouez avec les couleurs hexadécimales. Définissez une couleur, puis choisissez une ou plusieurs valeurs d'incrémentation et démarrez l'aperçu." + }, + path: './projects/js-small-apps/color-cycle/index.html', + repo: [{ + name: 'Github', + url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/color-cycle' + }, { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/color-cycle' + }], + technologies: ['Vanilla Javascript'] +}, { + id: 'css-border-previewer', + name: 'CSS Border Previewer', + description: { + en: 'Play with CSS borders (style, width, radius). Then, you can copy the generated code if the preview suits you.', + fr: "Jouez avec les bordures CSS (style, largeur, radius). Ensuite, vous pouvez copier le code généré si l'aperçu vous satisfait." + }, + path: './projects/js-small-apps/css-border-previewer/index.html', + repo: [{ + name: 'Github', + url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/css-border-previewer' + }, { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/css-border-previewer' + }], + technologies: ['Vanilla Javascript'] +}, { + id: 'meme-generator', + name: 'Meme Generator', + description: { + en: 'Choose a random image, set one or more texts then position them. Your meme is ready!', + fr: 'Choisissez une image aléatoire, définissez un ou plusieurs textes et positionnez-les. Votre meme est prêt !' + }, + path: './projects/react-small-apps/meme-generator/build/index.html', + repo: [{ + name: 'Github', + url: 'https://github.com/ArmandPhilippot/react-small-apps/tree/main/meme-generator' + }, { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/react-small-apps/-/tree/main/meme-generator' + }], + technologies: ['React', 'Fetch'] +}, { + id: 'notebook', + name: 'Notebook', + description: { + en: 'Create as many pages as you want and fill them. You can define a title and a body. Then you can easily navigate between your pages with the nav.', + fr: 'Créez autant de pages que vous le souhaitez et remplissez-les. Vous pouvez définir un titre et un corps de texte. Ensuite, vous pouvez facilement naviguer entre vos pages grâce à la navigation.' + }, + path: './projects/react-small-apps/notebook/build/', + repo: [{ + name: 'Github', + url: 'https://github.com/ArmandPhilippot/react-small-apps/tree/main/notebook' + }, { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/react-small-apps/-/tree/main/notebook' + }], + technologies: ['React', 'React router'] +}, { + id: 'rps-game', + name: 'Rock Paper Scissors', + description: { + en: 'A basic implementation of the game. Try to beat your friend or the computer.', + fr: "Une implémentation du jeu. Essayez de battre votre ami ou l'ordinateur." + }, + path: './projects/js-small-apps/rock-paper-scissors/index.html', + repo: [{ + name: 'Github', + url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/rock-paper-scissors' + }, { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/rock-paper-scissors' + }], + technologies: ['Vanilla Javascript'] +}, { + id: 'todos', + name: 'Todos', + description: { + en: 'You can add, remove or mark as done your todos. For each todos, you can add some details in addition to the title.\n\nLogin: demo@email.com\nPassword: demo', + fr: 'Vous pouvez ajouter, supprimer ou marquer comme fait vos "todo". Pour chaque "todo", vous pouvez ajouter des détails en plus du titre.\n\nLogin : demo@email.com\nMot de passe : demo' + }, + path: './projects/react-small-apps/todos/build/', + repo: [{ + name: 'Github', + url: 'https://github.com/ArmandPhilippot/react-small-apps/tree/main/todos' + }, { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/react-small-apps/-/tree/main/todos' + }], + technologies: ['React', 'React router', 'Redux'] +}, { + id: 'users-list', + name: 'Users list', + description: { + en: 'You can see a list of username. By clicking on it, the next column display information about the selected user.', + fr: "Vous pouvez voir une liste de noms d'utilisateur. En cliquant sur l'un d'eux, la colonne suivante affiche les informations à propos de cet utilisateur." + }, + path: './projects/js-small-apps/users-list/index.html', + repo: [{ + name: 'Github', + url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/users-list' + }, { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/users-list' + }], + technologies: ['Vanilla Javascript', 'Fetch'] +}]; +/* harmony default export */ __webpack_exports__["default"] = (projects); + +/***/ }), + +/***/ "./src/js/i18n/i18n.js": +/*!*****************************!*\ + !*** ./src/js/i18n/i18n.js ***! + \*****************************/ +/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "supportedLanguages": function() { return /* binding */ supportedLanguages; }, +/* harmony export */ "setLocale": function() { return /* binding */ setLocale; }, +/* harmony export */ "translate": function() { return /* binding */ translate; }, +/* harmony export */ "defaultLocale": function() { return /* binding */ defaultLocale; }, +/* harmony export */ "currentLocale": function() { return /* binding */ currentLocale; } +/* harmony export */ }); +/* harmony import */ var i18n_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! i18n-js */ "./node_modules/i18n-js/app/assets/javascripts/i18n.js"); +/* harmony import */ var i18n_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(i18n_js__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _locales_en__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./locales/en */ "./src/js/i18n/locales/en.js"); +/* harmony import */ var _locales_fr__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./locales/fr */ "./src/js/i18n/locales/fr.js"); + + + +var supportedLanguages = [{ + code: 'en', + label: 'English', + translations: _locales_en__WEBPACK_IMPORTED_MODULE_1__["default"] +}, { + code: 'fr', + label: 'Français', + translations: _locales_fr__WEBPACK_IMPORTED_MODULE_2__["default"] +}]; +supportedLanguages.forEach(function (locale) { + (i18n_js__WEBPACK_IMPORTED_MODULE_0___default().translations)[locale.code] = locale.translations; +}); + +function setLocale(locale) { + (i18n_js__WEBPACK_IMPORTED_MODULE_0___default().locale) = locale; +} + +function currentLocale() { + return i18n_js__WEBPACK_IMPORTED_MODULE_0___default().currentLocale(); +} + +function translate(name) { + var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + return i18n_js__WEBPACK_IMPORTED_MODULE_0___default().t(name, params); +} + +var defaultLocale = (i18n_js__WEBPACK_IMPORTED_MODULE_0___default().defaultLocale); + + +/***/ }), + +/***/ "./src/js/i18n/locales/en.js": +/*!***********************************!*\ + !*** ./src/js/i18n/locales/en.js ***! + \***********************************/ +/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +var en = { + branding: { + description: 'Front-end developer' + }, + nav: { + title: 'Apps list:' + }, + main: { + instructions: 'Select an app inside menu to see a live preview and app details (description, technologies, repositories).', + project: { + details: { + about: 'About {{name}}', + repo: { + one: 'Repository:', + other: 'Repositories:', + zero: 'Repositories:' + }, + tech: { + one: 'Technology:', + other: 'Technologies:', + zero: 'Technologies:' + } + } + } + }, + footer: { + legalNotice: { + txt: 'Legal notice', + link: 'legal-notice.html' + }, + license: 'License MIT' + } +}; +/* harmony default export */ __webpack_exports__["default"] = (en); + +/***/ }), + +/***/ "./src/js/i18n/locales/fr.js": +/*!***********************************!*\ + !*** ./src/js/i18n/locales/fr.js ***! + \***********************************/ +/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +var fr = { + branding: { + description: 'Intégrateur web' + }, + nav: { + title: 'Liste des applications :' + }, + main: { + instructions: "Sélectionnez une application dans le menu pour afficher un aperçu en direct et les informations sur l'application (description, technologies, dépôts).", + project: { + details: { + about: 'À propos de {{name}}', + repo: { + one: 'Dépôt :', + other: 'Dépôts :', + zero: 'Dépôt :' + }, + tech: { + one: 'Technologie :', + other: 'Technologies :', + zero: 'Technologie :' + } + } + } + }, + footer: { + legalNotice: { + txt: 'Mentions légales', + link: 'mentions-legales.html' + }, + license: 'Licence MIT' + } +}; +/* harmony default export */ __webpack_exports__["default"] = (fr); + +/***/ }), + +/***/ "./src/js/utilities/animations.js": +/*!****************************************!*\ + !*** ./src/js/utilities/animations.js ***! + \****************************************/ +/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "hideToLeft": function() { return /* binding */ hideToLeft; }, +/* harmony export */ "showFromLeft": function() { return /* binding */ showFromLeft; }, +/* harmony export */ "hideToBottom": function() { return /* binding */ hideToBottom; }, +/* harmony export */ "showFromBottom": function() { return /* binding */ showFromBottom; } +/* harmony export */ }); +/** + * Change the element classes to hide it with a slide out left animation. + * @param {HTMLElement} el - The HTMLElement to hide. + */ +function hideToLeft(el) { + el === null || el === void 0 ? void 0 : el.classList.remove('slide-in--left'); + el === null || el === void 0 ? void 0 : el.classList.add('slide-out--left'); + setTimeout(function () { + el === null || el === void 0 ? void 0 : el.classList.add('hide'); + }, 800); +} +/** + * Change the element classes to show it with a slide in left animation. + * @param {HTMLElement} el - The HTMLElement to show. + */ + + +function showFromLeft(el) { + el === null || el === void 0 ? void 0 : el.classList.remove('slide-out--left'); + el === null || el === void 0 ? void 0 : el.classList.remove('hide'); + el === null || el === void 0 ? void 0 : el.classList.add('slide-in--left'); +} +/** + * Change the element classes to hide it with a slide out bottom animation. + * @param {HTMLElement} el - The HTMLElement to hide. + */ + + +function hideToBottom(el) { + el === null || el === void 0 ? void 0 : el.classList.remove('slide-in--up'); + el === null || el === void 0 ? void 0 : el.classList.add('slide-out--bottom'); + setTimeout(function () { + el === null || el === void 0 ? void 0 : el.classList.add('hide'); + }, 800); +} +/** + * Change the element classes to show it with a slide in up animation. + * @param {HTMLElement} el - The HTMLElement to show. + */ + + +function showFromBottom(el) { + el === null || el === void 0 ? void 0 : el.classList.remove('slide-out--bottom'); + el === null || el === void 0 ? void 0 : el.classList.remove('hide'); + el === null || el === void 0 ? void 0 : el.classList.add('slide-in--up'); +} + + + +/***/ }), + +/***/ "./src/js/utilities/helpers.js": +/*!*************************************!*\ + !*** ./src/js/utilities/helpers.js ***! + \*************************************/ +/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "isSmallVw": function() { return /* binding */ isSmallVw; }, +/* harmony export */ "isStyleJsExists": function() { return /* binding */ isStyleJsExists; } +/* harmony export */ }); +/* harmony import */ var _babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/asyncToGenerator */ "./node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js"); +/* harmony import */ var _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @babel/runtime/regenerator */ "./node_modules/@babel/runtime/regenerator/index.js"); +/* harmony import */ var _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1__); + + + +/** + * Check the size of the current viewport. + * @returns {Boolean} True if viewport lower than 1200px; false otherwise. + */ +function isSmallVw() { + return window.innerWidth < 1200; +} +/** + * Check if /assets/styles.js exists (Webpack dev mode). + * @returns {Boolean} True if style.js exists ; false otherwise. + */ + + +function isStyleJsExists() { + return _isStyleJsExists.apply(this, arguments); +} + +function _isStyleJsExists() { + _isStyleJsExists = (0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__["default"])( /*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee() { + var filePath, response; + return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function _callee$(_context) { + while (1) { + switch (_context.prev = _context.next) { + case 0: + filePath = 'assets/js/style.js'; + _context.next = 3; + return fetch(filePath); + + case 3: + response = _context.sent; + return _context.abrupt("return", response.status === 200); + + case 5: + case "end": + return _context.stop(); + } + } + }, _callee); + })); + return _isStyleJsExists.apply(this, arguments); +} + + + +/***/ }), + +/***/ "./node_modules/events/events.js": +/*!***************************************!*\ + !*** ./node_modules/events/events.js ***! + \***************************************/ +/***/ (function(module) { + +"use strict"; +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +var R = typeof Reflect === 'object' ? Reflect : null +var ReflectApply = R && typeof R.apply === 'function' + ? R.apply + : function ReflectApply(target, receiver, args) { + return Function.prototype.apply.call(target, receiver, args); + } + +var ReflectOwnKeys +if (R && typeof R.ownKeys === 'function') { + ReflectOwnKeys = R.ownKeys +} else if (Object.getOwnPropertySymbols) { + ReflectOwnKeys = function ReflectOwnKeys(target) { + return Object.getOwnPropertyNames(target) + .concat(Object.getOwnPropertySymbols(target)); + }; +} else { + ReflectOwnKeys = function ReflectOwnKeys(target) { + return Object.getOwnPropertyNames(target); + }; +} + +function ProcessEmitWarning(warning) { + if (console && console.warn) console.warn(warning); +} + +var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) { + return value !== value; +} + +function EventEmitter() { + EventEmitter.init.call(this); +} +module.exports = EventEmitter; +module.exports.once = once; + +// Backwards-compat with node 0.10.x +EventEmitter.EventEmitter = EventEmitter; + +EventEmitter.prototype._events = undefined; +EventEmitter.prototype._eventsCount = 0; +EventEmitter.prototype._maxListeners = undefined; + +// By default EventEmitters will print a warning if more than 10 listeners are +// added to it. This is a useful default which helps finding memory leaks. +var defaultMaxListeners = 10; + +function checkListener(listener) { + if (typeof listener !== 'function') { + throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); + } +} + +Object.defineProperty(EventEmitter, 'defaultMaxListeners', { + enumerable: true, + get: function() { + return defaultMaxListeners; + }, + set: function(arg) { + if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) { + throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.'); + } + defaultMaxListeners = arg; + } +}); + +EventEmitter.init = function() { + + if (this._events === undefined || + this._events === Object.getPrototypeOf(this)._events) { + this._events = Object.create(null); + this._eventsCount = 0; + } + + this._maxListeners = this._maxListeners || undefined; +}; + +// Obviously not all Emitters should be limited to 10. This function allows +// that to be increased. Set to zero for unlimited. +EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { + if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) { + throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.'); + } + this._maxListeners = n; + return this; +}; + +function _getMaxListeners(that) { + if (that._maxListeners === undefined) + return EventEmitter.defaultMaxListeners; + return that._maxListeners; +} + +EventEmitter.prototype.getMaxListeners = function getMaxListeners() { + return _getMaxListeners(this); +}; + +EventEmitter.prototype.emit = function emit(type) { + var args = []; + for (var i = 1; i < arguments.length; i++) args.push(arguments[i]); + var doError = (type === 'error'); + + var events = this._events; + if (events !== undefined) + doError = (doError && events.error === undefined); + else if (!doError) + return false; + + // If there is no 'error' event listener then throw. + if (doError) { + var er; + if (args.length > 0) + er = args[0]; + if (er instanceof Error) { + // Note: The comments on the `throw` lines are intentional, they show + // up in Node's output if this results in an unhandled exception. + throw er; // Unhandled 'error' event + } + // At least give some kind of context to the user + var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : '')); + err.context = er; + throw err; // Unhandled 'error' event + } + + var handler = events[type]; + + if (handler === undefined) + return false; + + if (typeof handler === 'function') { + ReflectApply(handler, this, args); + } else { + var len = handler.length; + var listeners = arrayClone(handler, len); + for (var i = 0; i < len; ++i) + ReflectApply(listeners[i], this, args); + } + + return true; +}; + +function _addListener(target, type, listener, prepend) { + var m; + var events; + var existing; + + checkListener(listener); + + events = target._events; + if (events === undefined) { + events = target._events = Object.create(null); + target._eventsCount = 0; + } else { + // To avoid recursion in the case that type === "newListener"! Before + // adding it to the listeners, first emit "newListener". + if (events.newListener !== undefined) { + target.emit('newListener', type, + listener.listener ? listener.listener : listener); + + // Re-assign `events` because a newListener handler could have caused the + // this._events to be assigned to a new object + events = target._events; + } + existing = events[type]; + } + + if (existing === undefined) { + // Optimize the case of one listener. Don't need the extra array object. + existing = events[type] = listener; + ++target._eventsCount; + } else { + if (typeof existing === 'function') { + // Adding the second element, need to change to array. + existing = events[type] = + prepend ? [listener, existing] : [existing, listener]; + // If we've already got an array, just append. + } else if (prepend) { + existing.unshift(listener); + } else { + existing.push(listener); + } + + // Check for listener leak + m = _getMaxListeners(target); + if (m > 0 && existing.length > m && !existing.warned) { + existing.warned = true; + // No error code for this since it is a Warning + // eslint-disable-next-line no-restricted-syntax + var w = new Error('Possible EventEmitter memory leak detected. ' + + existing.length + ' ' + String(type) + ' listeners ' + + 'added. Use emitter.setMaxListeners() to ' + + 'increase limit'); + w.name = 'MaxListenersExceededWarning'; + w.emitter = target; + w.type = type; + w.count = existing.length; + ProcessEmitWarning(w); + } + } + + return target; +} + +EventEmitter.prototype.addListener = function addListener(type, listener) { + return _addListener(this, type, listener, false); +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.prependListener = + function prependListener(type, listener) { + return _addListener(this, type, listener, true); + }; + +function onceWrapper() { + if (!this.fired) { + this.target.removeListener(this.type, this.wrapFn); + this.fired = true; + if (arguments.length === 0) + return this.listener.call(this.target); + return this.listener.apply(this.target, arguments); + } +} + +function _onceWrap(target, type, listener) { + var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener }; + var wrapped = onceWrapper.bind(state); + wrapped.listener = listener; + state.wrapFn = wrapped; + return wrapped; +} + +EventEmitter.prototype.once = function once(type, listener) { + checkListener(listener); + this.on(type, _onceWrap(this, type, listener)); + return this; +}; + +EventEmitter.prototype.prependOnceListener = + function prependOnceListener(type, listener) { + checkListener(listener); + this.prependListener(type, _onceWrap(this, type, listener)); + return this; + }; + +// Emits a 'removeListener' event if and only if the listener was removed. +EventEmitter.prototype.removeListener = + function removeListener(type, listener) { + var list, events, position, i, originalListener; + + checkListener(listener); + + events = this._events; + if (events === undefined) + return this; + + list = events[type]; + if (list === undefined) + return this; + + if (list === listener || list.listener === listener) { + if (--this._eventsCount === 0) + this._events = Object.create(null); + else { + delete events[type]; + if (events.removeListener) + this.emit('removeListener', type, list.listener || listener); + } + } else if (typeof list !== 'function') { + position = -1; + + for (i = list.length - 1; i >= 0; i--) { + if (list[i] === listener || list[i].listener === listener) { + originalListener = list[i].listener; + position = i; + break; + } + } + + if (position < 0) + return this; + + if (position === 0) + list.shift(); + else { + spliceOne(list, position); + } + + if (list.length === 1) + events[type] = list[0]; + + if (events.removeListener !== undefined) + this.emit('removeListener', type, originalListener || listener); + } + + return this; + }; + +EventEmitter.prototype.off = EventEmitter.prototype.removeListener; + +EventEmitter.prototype.removeAllListeners = + function removeAllListeners(type) { + var listeners, events, i; + + events = this._events; + if (events === undefined) + return this; + + // not listening for removeListener, no need to emit + if (events.removeListener === undefined) { + if (arguments.length === 0) { + this._events = Object.create(null); + this._eventsCount = 0; + } else if (events[type] !== undefined) { + if (--this._eventsCount === 0) + this._events = Object.create(null); + else + delete events[type]; + } + return this; + } + + // emit removeListener for all listeners on all events + if (arguments.length === 0) { + var keys = Object.keys(events); + var key; + for (i = 0; i < keys.length; ++i) { + key = keys[i]; + if (key === 'removeListener') continue; + this.removeAllListeners(key); + } + this.removeAllListeners('removeListener'); + this._events = Object.create(null); + this._eventsCount = 0; + return this; + } + + listeners = events[type]; + + if (typeof listeners === 'function') { + this.removeListener(type, listeners); + } else if (listeners !== undefined) { + // LIFO order + for (i = listeners.length - 1; i >= 0; i--) { + this.removeListener(type, listeners[i]); + } + } + + return this; + }; + +function _listeners(target, type, unwrap) { + var events = target._events; + + if (events === undefined) + return []; + + var evlistener = events[type]; + if (evlistener === undefined) + return []; + + if (typeof evlistener === 'function') + return unwrap ? [evlistener.listener || evlistener] : [evlistener]; + + return unwrap ? + unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); +} + +EventEmitter.prototype.listeners = function listeners(type) { + return _listeners(this, type, true); +}; + +EventEmitter.prototype.rawListeners = function rawListeners(type) { + return _listeners(this, type, false); +}; + +EventEmitter.listenerCount = function(emitter, type) { + if (typeof emitter.listenerCount === 'function') { + return emitter.listenerCount(type); + } else { + return listenerCount.call(emitter, type); + } +}; + +EventEmitter.prototype.listenerCount = listenerCount; +function listenerCount(type) { + var events = this._events; + + if (events !== undefined) { + var evlistener = events[type]; + + if (typeof evlistener === 'function') { + return 1; + } else if (evlistener !== undefined) { + return evlistener.length; + } + } + + return 0; +} + +EventEmitter.prototype.eventNames = function eventNames() { + return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : []; +}; + +function arrayClone(arr, n) { + var copy = new Array(n); + for (var i = 0; i < n; ++i) + copy[i] = arr[i]; + return copy; +} + +function spliceOne(list, index) { + for (; index + 1 < list.length; index++) + list[index] = list[index + 1]; + list.pop(); +} + +function unwrapListeners(arr) { + var ret = new Array(arr.length); + for (var i = 0; i < ret.length; ++i) { + ret[i] = arr[i].listener || arr[i]; + } + return ret; +} + +function once(emitter, name) { + return new Promise(function (resolve, reject) { + function errorListener(err) { + emitter.removeListener(name, resolver); + reject(err); + } + + function resolver() { + if (typeof emitter.removeListener === 'function') { + emitter.removeListener('error', errorListener); + } + resolve([].slice.call(arguments)); + }; + + eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); + if (name !== 'error') { + addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true }); + } + }); +} + +function addErrorHandlerIfEventEmitter(emitter, handler, flags) { + if (typeof emitter.on === 'function') { + eventTargetAgnosticAddListener(emitter, 'error', handler, flags); + } +} + +function eventTargetAgnosticAddListener(emitter, name, listener, flags) { + if (typeof emitter.on === 'function') { + if (flags.once) { + emitter.once(name, listener); + } else { + emitter.on(name, listener); + } + } else if (typeof emitter.addEventListener === 'function') { + // EventTarget does not have `error` event semantics like Node + // EventEmitters, we do not listen for `error` events here. + emitter.addEventListener(name, function wrapListener(arg) { + // IE does not have builtin `{ once: true }` support so we + // have to do it manually. + if (flags.once) { + emitter.removeEventListener(name, wrapListener); + } + listener(arg); + }); + } else { + throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter); + } +} + + +/***/ }), + +/***/ "./node_modules/html-entities/lib/index.js": +/*!*************************************************!*\ + !*** ./node_modules/html-entities/lib/index.js ***! + \*************************************************/ +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { + +"use strict"; + +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +var named_references_1 = __webpack_require__(/*! ./named-references */ "./node_modules/html-entities/lib/named-references.js"); +var numeric_unicode_map_1 = __webpack_require__(/*! ./numeric-unicode-map */ "./node_modules/html-entities/lib/numeric-unicode-map.js"); +var surrogate_pairs_1 = __webpack_require__(/*! ./surrogate-pairs */ "./node_modules/html-entities/lib/surrogate-pairs.js"); +var allNamedReferences = __assign(__assign({}, named_references_1.namedReferences), { all: named_references_1.namedReferences.html5 }); +var encodeRegExps = { + specialChars: /[<>'"&]/g, + nonAscii: /(?:[<>'"&\u0080-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g, + nonAsciiPrintable: /(?:[<>'"&\x01-\x08\x11-\x15\x17-\x1F\x7f-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g, + extensive: /(?:[\x01-\x0c\x0e-\x1f\x21-\x2c\x2e-\x2f\x3a-\x40\x5b-\x60\x7b-\x7d\x7f-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g +}; +var defaultEncodeOptions = { + mode: 'specialChars', + level: 'all', + numeric: 'decimal' +}; +/** Encodes all the necessary (specified by `level`) characters in the text */ +function encode(text, _a) { + var _b = _a === void 0 ? defaultEncodeOptions : _a, _c = _b.mode, mode = _c === void 0 ? 'specialChars' : _c, _d = _b.numeric, numeric = _d === void 0 ? 'decimal' : _d, _e = _b.level, level = _e === void 0 ? 'all' : _e; + if (!text) { + return ''; + } + var encodeRegExp = encodeRegExps[mode]; + var references = allNamedReferences[level].characters; + var isHex = numeric === 'hexadecimal'; + encodeRegExp.lastIndex = 0; + var _b = encodeRegExp.exec(text); + var _c; + if (_b) { + _c = ''; + var _d = 0; + do { + if (_d !== _b.index) { + _c += text.substring(_d, _b.index); + } + var _e = _b[0]; + var result_1 = references[_e]; + if (!result_1) { + var code_1 = _e.length > 1 ? surrogate_pairs_1.getCodePoint(_e, 0) : _e.charCodeAt(0); + result_1 = (isHex ? '&#x' + code_1.toString(16) : '&#' + code_1) + ';'; + } + _c += result_1; + _d = _b.index + _e.length; + } while ((_b = encodeRegExp.exec(text))); + if (_d !== text.length) { + _c += text.substring(_d); + } + } + else { + _c = + text; + } + return _c; +} +exports.encode = encode; +var defaultDecodeOptions = { + scope: 'body', + level: 'all' +}; +var strict = /&(?:#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+);/g; +var attribute = /&(?:#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+)[;=]?/g; +var baseDecodeRegExps = { + xml: { + strict: strict, + attribute: attribute, + body: named_references_1.bodyRegExps.xml + }, + html4: { + strict: strict, + attribute: attribute, + body: named_references_1.bodyRegExps.html4 + }, + html5: { + strict: strict, + attribute: attribute, + body: named_references_1.bodyRegExps.html5 + } +}; +var decodeRegExps = __assign(__assign({}, baseDecodeRegExps), { all: baseDecodeRegExps.html5 }); +var fromCharCode = String.fromCharCode; +var outOfBoundsChar = fromCharCode(65533); +var defaultDecodeEntityOptions = { + level: 'all' +}; +/** Decodes a single entity */ +function decodeEntity(entity, _a) { + var _b = (_a === void 0 ? defaultDecodeEntityOptions : _a).level, level = _b === void 0 ? 'all' : _b; + if (!entity) { + return ''; + } + var _b = entity; + var decodeEntityLastChar_1 = entity[entity.length - 1]; + if (false) {} + else if (false) {} + else { + var decodeResultByReference_1 = allNamedReferences[level].entities[entity]; + if (decodeResultByReference_1) { + _b = decodeResultByReference_1; + } + else if (entity[0] === '&' && entity[1] === '#') { + var decodeSecondChar_1 = entity[2]; + var decodeCode_1 = decodeSecondChar_1 == 'x' || decodeSecondChar_1 == 'X' + ? parseInt(entity.substr(3), 16) + : parseInt(entity.substr(2)); + _b = + decodeCode_1 >= 0x10ffff + ? outOfBoundsChar + : decodeCode_1 > 65535 + ? surrogate_pairs_1.fromCodePoint(decodeCode_1) + : fromCharCode(numeric_unicode_map_1.numericUnicodeMap[decodeCode_1] || decodeCode_1); + } + } + return _b; +} +exports.decodeEntity = decodeEntity; +/** Decodes all entities in the text */ +function decode(text, _a) { + var decodeSecondChar_1 = _a === void 0 ? defaultDecodeOptions : _a, decodeCode_1 = decodeSecondChar_1.level, level = decodeCode_1 === void 0 ? 'all' : decodeCode_1, _b = decodeSecondChar_1.scope, scope = _b === void 0 ? level === 'xml' ? 'strict' : 'body' : _b; + if (!text) { + return ''; + } + var decodeRegExp = decodeRegExps[level][scope]; + var references = allNamedReferences[level].entities; + var isAttribute = scope === 'attribute'; + var isStrict = scope === 'strict'; + decodeRegExp.lastIndex = 0; + var replaceMatch_1 = decodeRegExp.exec(text); + var replaceResult_1; + if (replaceMatch_1) { + replaceResult_1 = ''; + var replaceLastIndex_1 = 0; + do { + if (replaceLastIndex_1 !== replaceMatch_1.index) { + replaceResult_1 += text.substring(replaceLastIndex_1, replaceMatch_1.index); + } + var replaceInput_1 = replaceMatch_1[0]; + var decodeResult_1 = replaceInput_1; + var decodeEntityLastChar_2 = replaceInput_1[replaceInput_1.length - 1]; + if (isAttribute + && decodeEntityLastChar_2 === '=') { + decodeResult_1 = replaceInput_1; + } + else if (isStrict + && decodeEntityLastChar_2 !== ';') { + decodeResult_1 = replaceInput_1; + } + else { + var decodeResultByReference_2 = references[replaceInput_1]; + if (decodeResultByReference_2) { + decodeResult_1 = decodeResultByReference_2; + } + else if (replaceInput_1[0] === '&' && replaceInput_1[1] === '#') { + var decodeSecondChar_2 = replaceInput_1[2]; + var decodeCode_2 = decodeSecondChar_2 == 'x' || decodeSecondChar_2 == 'X' + ? parseInt(replaceInput_1.substr(3), 16) + : parseInt(replaceInput_1.substr(2)); + decodeResult_1 = + decodeCode_2 >= 0x10ffff + ? outOfBoundsChar + : decodeCode_2 > 65535 + ? surrogate_pairs_1.fromCodePoint(decodeCode_2) + : fromCharCode(numeric_unicode_map_1.numericUnicodeMap[decodeCode_2] || decodeCode_2); + } + } + replaceResult_1 += decodeResult_1; + replaceLastIndex_1 = replaceMatch_1.index + replaceInput_1.length; + } while ((replaceMatch_1 = decodeRegExp.exec(text))); + if (replaceLastIndex_1 !== text.length) { + replaceResult_1 += text.substring(replaceLastIndex_1); + } + } + else { + replaceResult_1 = + text; + } + return replaceResult_1; +} +exports.decode = decode; + + +/***/ }), + +/***/ "./node_modules/html-entities/lib/named-references.js": +/*!************************************************************!*\ + !*** ./node_modules/html-entities/lib/named-references.js ***! + \************************************************************/ +/***/ (function(__unused_webpack_module, exports) { + +"use strict"; +Object.defineProperty(exports, "__esModule", ({value:true}));exports.bodyRegExps={xml:/&(?:#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+);?/g,html4:/&(?:nbsp|iexcl|cent|pound|curren|yen|brvbar|sect|uml|copy|ordf|laquo|not|shy|reg|macr|deg|plusmn|sup2|sup3|acute|micro|para|middot|cedil|sup1|ordm|raquo|frac14|frac12|frac34|iquest|Agrave|Aacute|Acirc|Atilde|Auml|Aring|AElig|Ccedil|Egrave|Eacute|Ecirc|Euml|Igrave|Iacute|Icirc|Iuml|ETH|Ntilde|Ograve|Oacute|Ocirc|Otilde|Ouml|times|Oslash|Ugrave|Uacute|Ucirc|Uuml|Yacute|THORN|szlig|agrave|aacute|acirc|atilde|auml|aring|aelig|ccedil|egrave|eacute|ecirc|euml|igrave|iacute|icirc|iuml|eth|ntilde|ograve|oacute|ocirc|otilde|ouml|divide|oslash|ugrave|uacute|ucirc|uuml|yacute|thorn|yuml|quot|amp|lt|gt|#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+);?/g,html5:/&(?:AElig|AMP|Aacute|Acirc|Agrave|Aring|Atilde|Auml|COPY|Ccedil|ETH|Eacute|Ecirc|Egrave|Euml|GT|Iacute|Icirc|Igrave|Iuml|LT|Ntilde|Oacute|Ocirc|Ograve|Oslash|Otilde|Ouml|QUOT|REG|THORN|Uacute|Ucirc|Ugrave|Uuml|Yacute|aacute|acirc|acute|aelig|agrave|amp|aring|atilde|auml|brvbar|ccedil|cedil|cent|copy|curren|deg|divide|eacute|ecirc|egrave|eth|euml|frac12|frac14|frac34|gt|iacute|icirc|iexcl|igrave|iquest|iuml|laquo|lt|macr|micro|middot|nbsp|not|ntilde|oacute|ocirc|ograve|ordf|ordm|oslash|otilde|ouml|para|plusmn|pound|quot|raquo|reg|sect|shy|sup1|sup2|sup3|szlig|thorn|times|uacute|ucirc|ugrave|uml|uuml|yacute|yen|yuml|#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+);?/g};exports.namedReferences={xml:{entities:{"<":"<",">":">",""":'"',"'":"'","&":"&"},characters:{"<":"<",">":">",'"':""","'":"'","&":"&"}},html4:{entities:{"'":"'"," ":" "," ":" ","¡":"¡","¡":"¡","¢":"¢","¢":"¢","£":"£","£":"£","¤":"¤","¤":"¤","¥":"¥","¥":"¥","¦":"¦","¦":"¦","§":"§","§":"§","¨":"¨","¨":"¨","©":"©","©":"©","ª":"ª","ª":"ª","«":"«","«":"«","¬":"¬","¬":"¬","­":"­","­":"­","®":"®","®":"®","¯":"¯","¯":"¯","°":"°","°":"°","±":"±","±":"±","²":"²","²":"²","³":"³","³":"³","´":"´","´":"´","µ":"µ","µ":"µ","¶":"¶","¶":"¶","·":"·","·":"·","¸":"¸","¸":"¸","¹":"¹","¹":"¹","º":"º","º":"º","»":"»","»":"»","¼":"¼","¼":"¼","½":"½","½":"½","¾":"¾","¾":"¾","¿":"¿","¿":"¿","À":"À","À":"À","Á":"Á","Á":"Á","Â":"Â","Â":"Â","Ã":"Ã","Ã":"Ã","Ä":"Ä","Ä":"Ä","Å":"Å","Å":"Å","Æ":"Æ","Æ":"Æ","Ç":"Ç","Ç":"Ç","È":"È","È":"È","É":"É","É":"É","Ê":"Ê","Ê":"Ê","Ë":"Ë","Ë":"Ë","Ì":"Ì","Ì":"Ì","Í":"Í","Í":"Í","Î":"Î","Î":"Î","Ï":"Ï","Ï":"Ï","Ð":"Ð","Ð":"Ð","Ñ":"Ñ","Ñ":"Ñ","Ò":"Ò","Ò":"Ò","Ó":"Ó","Ó":"Ó","Ô":"Ô","Ô":"Ô","Õ":"Õ","Õ":"Õ","Ö":"Ö","Ö":"Ö","×":"×","×":"×","Ø":"Ø","Ø":"Ø","Ù":"Ù","Ù":"Ù","Ú":"Ú","Ú":"Ú","Û":"Û","Û":"Û","Ü":"Ü","Ü":"Ü","Ý":"Ý","Ý":"Ý","Þ":"Þ","Þ":"Þ","ß":"ß","ß":"ß","à":"à","à":"à","á":"á","á":"á","â":"â","â":"â","ã":"ã","ã":"ã","ä":"ä","ä":"ä","å":"å","å":"å","æ":"æ","æ":"æ","ç":"ç","ç":"ç","è":"è","è":"è","é":"é","é":"é","ê":"ê","ê":"ê","ë":"ë","ë":"ë","ì":"ì","ì":"ì","í":"í","í":"í","î":"î","î":"î","ï":"ï","ï":"ï","ð":"ð","ð":"ð","ñ":"ñ","ñ":"ñ","ò":"ò","ò":"ò","ó":"ó","ó":"ó","ô":"ô","ô":"ô","õ":"õ","õ":"õ","ö":"ö","ö":"ö","÷":"÷","÷":"÷","ø":"ø","ø":"ø","ù":"ù","ù":"ù","ú":"ú","ú":"ú","û":"û","û":"û","ü":"ü","ü":"ü","ý":"ý","ý":"ý","þ":"þ","þ":"þ","ÿ":"ÿ","ÿ":"ÿ",""":'"',""":'"',"&":"&","&":"&","<":"<","<":"<",">":">",">":">","Œ":"Œ","œ":"œ","Š":"Š","š":"š","Ÿ":"Ÿ","ˆ":"ˆ","˜":"˜"," ":" "," ":" "," ":" ","‌":"‌","‍":"‍","‎":"‎","‏":"‏","–":"–","—":"—","‘":"‘","’":"’","‚":"‚","“":"“","”":"”","„":"„","†":"†","‡":"‡","‰":"‰","‹":"‹","›":"›","€":"€","ƒ":"ƒ","Α":"Α","Β":"Β","Γ":"Γ","Δ":"Δ","Ε":"Ε","Ζ":"Ζ","Η":"Η","Θ":"Θ","Ι":"Ι","Κ":"Κ","Λ":"Λ","Μ":"Μ","Ν":"Ν","Ξ":"Ξ","Ο":"Ο","Π":"Π","Ρ":"Ρ","Σ":"Σ","Τ":"Τ","Υ":"Υ","Φ":"Φ","Χ":"Χ","Ψ":"Ψ","Ω":"Ω","α":"α","β":"β","γ":"γ","δ":"δ","ε":"ε","ζ":"ζ","η":"η","θ":"θ","ι":"ι","κ":"κ","λ":"λ","μ":"μ","ν":"ν","ξ":"ξ","ο":"ο","π":"π","ρ":"ρ","ς":"ς","σ":"σ","τ":"τ","υ":"υ","φ":"φ","χ":"χ","ψ":"ψ","ω":"ω","ϑ":"ϑ","ϒ":"ϒ","ϖ":"ϖ","•":"•","…":"…","′":"′","″":"″","‾":"‾","⁄":"⁄","℘":"℘","ℑ":"ℑ","ℜ":"ℜ","™":"™","ℵ":"ℵ","←":"←","↑":"↑","→":"→","↓":"↓","↔":"↔","↵":"↵","⇐":"⇐","⇑":"⇑","⇒":"⇒","⇓":"⇓","⇔":"⇔","∀":"∀","∂":"∂","∃":"∃","∅":"∅","∇":"∇","∈":"∈","∉":"∉","∋":"∋","∏":"∏","∑":"∑","−":"−","∗":"∗","√":"√","∝":"∝","∞":"∞","∠":"∠","∧":"∧","∨":"∨","∩":"∩","∪":"∪","∫":"∫","∴":"∴","∼":"∼","≅":"≅","≈":"≈","≠":"≠","≡":"≡","≤":"≤","≥":"≥","⊂":"⊂","⊃":"⊃","⊄":"⊄","⊆":"⊆","⊇":"⊇","⊕":"⊕","⊗":"⊗","⊥":"⊥","⋅":"⋅","⌈":"⌈","⌉":"⌉","⌊":"⌊","⌋":"⌋","⟨":"〈","⟩":"〉","◊":"◊","♠":"♠","♣":"♣","♥":"♥","♦":"♦"},characters:{"'":"'"," ":" ","¡":"¡","¢":"¢","£":"£","¤":"¤","¥":"¥","¦":"¦","§":"§","¨":"¨","©":"©","ª":"ª","«":"«","¬":"¬","­":"­","®":"®","¯":"¯","°":"°","±":"±","²":"²","³":"³","´":"´","µ":"µ","¶":"¶","·":"·","¸":"¸","¹":"¹","º":"º","»":"»","¼":"¼","½":"½","¾":"¾","¿":"¿","À":"À","Á":"Á","Â":"Â","Ã":"Ã","Ä":"Ä","Å":"Å","Æ":"Æ","Ç":"Ç","È":"È","É":"É","Ê":"Ê","Ë":"Ë","Ì":"Ì","Í":"Í","Î":"Î","Ï":"Ï","Ð":"Ð","Ñ":"Ñ","Ò":"Ò","Ó":"Ó","Ô":"Ô","Õ":"Õ","Ö":"Ö","×":"×","Ø":"Ø","Ù":"Ù","Ú":"Ú","Û":"Û","Ü":"Ü","Ý":"Ý","Þ":"Þ","ß":"ß","à":"à","á":"á","â":"â","ã":"ã","ä":"ä","å":"å","æ":"æ","ç":"ç","è":"è","é":"é","ê":"ê","ë":"ë","ì":"ì","í":"í","î":"î","ï":"ï","ð":"ð","ñ":"ñ","ò":"ò","ó":"ó","ô":"ô","õ":"õ","ö":"ö","÷":"÷","ø":"ø","ù":"ù","ú":"ú","û":"û","ü":"ü","ý":"ý","þ":"þ","ÿ":"ÿ",'"':""","&":"&","<":"<",">":">","Œ":"Œ","œ":"œ","Š":"Š","š":"š","Ÿ":"Ÿ","ˆ":"ˆ","˜":"˜"," ":" "," ":" "," ":" ","‌":"‌","‍":"‍","‎":"‎","‏":"‏","–":"–","—":"—","‘":"‘","’":"’","‚":"‚","“":"“","”":"”","„":"„","†":"†","‡":"‡","‰":"‰","‹":"‹","›":"›","€":"€","ƒ":"ƒ","Α":"Α","Β":"Β","Γ":"Γ","Δ":"Δ","Ε":"Ε","Ζ":"Ζ","Η":"Η","Θ":"Θ","Ι":"Ι","Κ":"Κ","Λ":"Λ","Μ":"Μ","Ν":"Ν","Ξ":"Ξ","Ο":"Ο","Π":"Π","Ρ":"Ρ","Σ":"Σ","Τ":"Τ","Υ":"Υ","Φ":"Φ","Χ":"Χ","Ψ":"Ψ","Ω":"Ω","α":"α","β":"β","γ":"γ","δ":"δ","ε":"ε","ζ":"ζ","η":"η","θ":"θ","ι":"ι","κ":"κ","λ":"λ","μ":"μ","ν":"ν","ξ":"ξ","ο":"ο","π":"π","ρ":"ρ","ς":"ς","σ":"σ","τ":"τ","υ":"υ","φ":"φ","χ":"χ","ψ":"ψ","ω":"ω","ϑ":"ϑ","ϒ":"ϒ","ϖ":"ϖ","•":"•","…":"…","′":"′","″":"″","‾":"‾","⁄":"⁄","℘":"℘","ℑ":"ℑ","ℜ":"ℜ","™":"™","ℵ":"ℵ","←":"←","↑":"↑","→":"→","↓":"↓","↔":"↔","↵":"↵","⇐":"⇐","⇑":"⇑","⇒":"⇒","⇓":"⇓","⇔":"⇔","∀":"∀","∂":"∂","∃":"∃","∅":"∅","∇":"∇","∈":"∈","∉":"∉","∋":"∋","∏":"∏","∑":"∑","−":"−","∗":"∗","√":"√","∝":"∝","∞":"∞","∠":"∠","∧":"∧","∨":"∨","∩":"∩","∪":"∪","∫":"∫","∴":"∴","∼":"∼","≅":"≅","≈":"≈","≠":"≠","≡":"≡","≤":"≤","≥":"≥","⊂":"⊂","⊃":"⊃","⊄":"⊄","⊆":"⊆","⊇":"⊇","⊕":"⊕","⊗":"⊗","⊥":"⊥","⋅":"⋅","⌈":"⌈","⌉":"⌉","⌊":"⌊","⌋":"⌋","〈":"⟨","〉":"⟩","◊":"◊","♠":"♠","♣":"♣","♥":"♥","♦":"♦"}},html5:{entities:{"Æ":"Æ","Æ":"Æ","&":"&","&":"&","Á":"Á","Á":"Á","Ă":"Ă","Â":"Â","Â":"Â","А":"А","𝔄":"𝔄","À":"À","À":"À","Α":"Α","Ā":"Ā","⩓":"⩓","Ą":"Ą","𝔸":"𝔸","⁡":"⁡","Å":"Å","Å":"Å","𝒜":"𝒜","≔":"≔","Ã":"Ã","Ã":"Ã","Ä":"Ä","Ä":"Ä","∖":"∖","⫧":"⫧","⌆":"⌆","Б":"Б","∵":"∵","ℬ":"ℬ","Β":"Β","𝔅":"𝔅","𝔹":"𝔹","˘":"˘","ℬ":"ℬ","≎":"≎","Ч":"Ч","©":"©","©":"©","Ć":"Ć","⋒":"⋒","ⅅ":"ⅅ","ℭ":"ℭ","Č":"Č","Ç":"Ç","Ç":"Ç","Ĉ":"Ĉ","∰":"∰","Ċ":"Ċ","¸":"¸","·":"·","ℭ":"ℭ","Χ":"Χ","⊙":"⊙","⊖":"⊖","⊕":"⊕","⊗":"⊗","∲":"∲","”":"”","’":"’","∷":"∷","⩴":"⩴","≡":"≡","∯":"∯","∮":"∮","ℂ":"ℂ","∐":"∐","∳":"∳","⨯":"⨯","𝒞":"𝒞","⋓":"⋓","≍":"≍","ⅅ":"ⅅ","⤑":"⤑","Ђ":"Ђ","Ѕ":"Ѕ","Џ":"Џ","‡":"‡","↡":"↡","⫤":"⫤","Ď":"Ď","Д":"Д","∇":"∇","Δ":"Δ","𝔇":"𝔇","´":"´","˙":"˙","˝":"˝","`":"`","˜":"˜","⋄":"⋄","ⅆ":"ⅆ","𝔻":"𝔻","¨":"¨","⃜":"⃜","≐":"≐","∯":"∯","¨":"¨","⇓":"⇓","⇐":"⇐","⇔":"⇔","⫤":"⫤","⟸":"⟸","⟺":"⟺","⟹":"⟹","⇒":"⇒","⊨":"⊨","⇑":"⇑","⇕":"⇕","∥":"∥","↓":"↓","⤓":"⤓","⇵":"⇵","̑":"̑","⥐":"⥐","⥞":"⥞","↽":"↽","⥖":"⥖","⥟":"⥟","⇁":"⇁","⥗":"⥗","⊤":"⊤","↧":"↧","⇓":"⇓","𝒟":"𝒟","Đ":"Đ","Ŋ":"Ŋ","Ð":"Ð","Ð":"Ð","É":"É","É":"É","Ě":"Ě","Ê":"Ê","Ê":"Ê","Э":"Э","Ė":"Ė","𝔈":"𝔈","È":"È","È":"È","∈":"∈","Ē":"Ē","◻":"◻","▫":"▫","Ę":"Ę","𝔼":"𝔼","Ε":"Ε","⩵":"⩵","≂":"≂","⇌":"⇌","ℰ":"ℰ","⩳":"⩳","Η":"Η","Ë":"Ë","Ë":"Ë","∃":"∃","ⅇ":"ⅇ","Ф":"Ф","𝔉":"𝔉","◼":"◼","▪":"▪","𝔽":"𝔽","∀":"∀","ℱ":"ℱ","ℱ":"ℱ","Ѓ":"Ѓ",">":">",">":">","Γ":"Γ","Ϝ":"Ϝ","Ğ":"Ğ","Ģ":"Ģ","Ĝ":"Ĝ","Г":"Г","Ġ":"Ġ","𝔊":"𝔊","⋙":"⋙","𝔾":"𝔾","≥":"≥","⋛":"⋛","≧":"≧","⪢":"⪢","≷":"≷","⩾":"⩾","≳":"≳","𝒢":"𝒢","≫":"≫","Ъ":"Ъ","ˇ":"ˇ","^":"^","Ĥ":"Ĥ","ℌ":"ℌ","ℋ":"ℋ","ℍ":"ℍ","─":"─","ℋ":"ℋ","Ħ":"Ħ","≎":"≎","≏":"≏","Е":"Е","IJ":"IJ","Ё":"Ё","Í":"Í","Í":"Í","Î":"Î","Î":"Î","И":"И","İ":"İ","ℑ":"ℑ","Ì":"Ì","Ì":"Ì","ℑ":"ℑ","Ī":"Ī","ⅈ":"ⅈ","⇒":"⇒","∬":"∬","∫":"∫","⋂":"⋂","⁣":"⁣","⁢":"⁢","Į":"Į","𝕀":"𝕀","Ι":"Ι","ℐ":"ℐ","Ĩ":"Ĩ","І":"І","Ï":"Ï","Ï":"Ï","Ĵ":"Ĵ","Й":"Й","𝔍":"𝔍","𝕁":"𝕁","𝒥":"𝒥","Ј":"Ј","Є":"Є","Х":"Х","Ќ":"Ќ","Κ":"Κ","Ķ":"Ķ","К":"К","𝔎":"𝔎","𝕂":"𝕂","𝒦":"𝒦","Љ":"Љ","<":"<","<":"<","Ĺ":"Ĺ","Λ":"Λ","⟪":"⟪","ℒ":"ℒ","↞":"↞","Ľ":"Ľ","Ļ":"Ļ","Л":"Л","⟨":"⟨","←":"←","⇤":"⇤","⇆":"⇆","⌈":"⌈","⟦":"⟦","⥡":"⥡","⇃":"⇃","⥙":"⥙","⌊":"⌊","↔":"↔","⥎":"⥎","⊣":"⊣","↤":"↤","⥚":"⥚","⊲":"⊲","⧏":"⧏","⊴":"⊴","⥑":"⥑","⥠":"⥠","↿":"↿","⥘":"⥘","↼":"↼","⥒":"⥒","⇐":"⇐","⇔":"⇔","⋚":"⋚","≦":"≦","≶":"≶","⪡":"⪡","⩽":"⩽","≲":"≲","𝔏":"𝔏","⋘":"⋘","⇚":"⇚","Ŀ":"Ŀ","⟵":"⟵","⟷":"⟷","⟶":"⟶","⟸":"⟸","⟺":"⟺","⟹":"⟹","𝕃":"𝕃","↙":"↙","↘":"↘","ℒ":"ℒ","↰":"↰","Ł":"Ł","≪":"≪","⤅":"⤅","М":"М"," ":" ","ℳ":"ℳ","𝔐":"𝔐","∓":"∓","𝕄":"𝕄","ℳ":"ℳ","Μ":"Μ","Њ":"Њ","Ń":"Ń","Ň":"Ň","Ņ":"Ņ","Н":"Н","​":"​","​":"​","​":"​","​":"​","≫":"≫","≪":"≪"," ":"\n","𝔑":"𝔑","⁠":"⁠"," ":" ","ℕ":"ℕ","⫬":"⫬","≢":"≢","≭":"≭","∦":"∦","∉":"∉","≠":"≠","≂̸":"≂̸","∄":"∄","≯":"≯","≱":"≱","≧̸":"≧̸","≫̸":"≫̸","≹":"≹","⩾̸":"⩾̸","≵":"≵","≎̸":"≎̸","≏̸":"≏̸","⋪":"⋪","⧏̸":"⧏̸","⋬":"⋬","≮":"≮","≰":"≰","≸":"≸","≪̸":"≪̸","⩽̸":"⩽̸","≴":"≴","⪢̸":"⪢̸","⪡̸":"⪡̸","⊀":"⊀","⪯̸":"⪯̸","⋠":"⋠","∌":"∌","⋫":"⋫","⧐̸":"⧐̸","⋭":"⋭","⊏̸":"⊏̸","⋢":"⋢","⊐̸":"⊐̸","⋣":"⋣","⊂⃒":"⊂⃒","⊈":"⊈","⊁":"⊁","⪰̸":"⪰̸","⋡":"⋡","≿̸":"≿̸","⊃⃒":"⊃⃒","⊉":"⊉","≁":"≁","≄":"≄","≇":"≇","≉":"≉","∤":"∤","𝒩":"𝒩","Ñ":"Ñ","Ñ":"Ñ","Ν":"Ν","Œ":"Œ","Ó":"Ó","Ó":"Ó","Ô":"Ô","Ô":"Ô","О":"О","Ő":"Ő","𝔒":"𝔒","Ò":"Ò","Ò":"Ò","Ō":"Ō","Ω":"Ω","Ο":"Ο","𝕆":"𝕆","“":"“","‘":"‘","⩔":"⩔","𝒪":"𝒪","Ø":"Ø","Ø":"Ø","Õ":"Õ","Õ":"Õ","⨷":"⨷","Ö":"Ö","Ö":"Ö","‾":"‾","⏞":"⏞","⎴":"⎴","⏜":"⏜","∂":"∂","П":"П","𝔓":"𝔓","Φ":"Φ","Π":"Π","±":"±","ℌ":"ℌ","ℙ":"ℙ","⪻":"⪻","≺":"≺","⪯":"⪯","≼":"≼","≾":"≾","″":"″","∏":"∏","∷":"∷","∝":"∝","𝒫":"𝒫","Ψ":"Ψ",""":'"',""":'"',"𝔔":"𝔔","ℚ":"ℚ","𝒬":"𝒬","⤐":"⤐","®":"®","®":"®","Ŕ":"Ŕ","⟫":"⟫","↠":"↠","⤖":"⤖","Ř":"Ř","Ŗ":"Ŗ","Р":"Р","ℜ":"ℜ","∋":"∋","⇋":"⇋","⥯":"⥯","ℜ":"ℜ","Ρ":"Ρ","⟩":"⟩","→":"→","⇥":"⇥","⇄":"⇄","⌉":"⌉","⟧":"⟧","⥝":"⥝","⇂":"⇂","⥕":"⥕","⌋":"⌋","⊢":"⊢","↦":"↦","⥛":"⥛","⊳":"⊳","⧐":"⧐","⊵":"⊵","⥏":"⥏","⥜":"⥜","↾":"↾","⥔":"⥔","⇀":"⇀","⥓":"⥓","⇒":"⇒","ℝ":"ℝ","⥰":"⥰","⇛":"⇛","ℛ":"ℛ","↱":"↱","⧴":"⧴","Щ":"Щ","Ш":"Ш","Ь":"Ь","Ś":"Ś","⪼":"⪼","Š":"Š","Ş":"Ş","Ŝ":"Ŝ","С":"С","𝔖":"𝔖","↓":"↓","←":"←","→":"→","↑":"↑","Σ":"Σ","∘":"∘","𝕊":"𝕊","√":"√","□":"□","⊓":"⊓","⊏":"⊏","⊑":"⊑","⊐":"⊐","⊒":"⊒","⊔":"⊔","𝒮":"𝒮","⋆":"⋆","⋐":"⋐","⋐":"⋐","⊆":"⊆","≻":"≻","⪰":"⪰","≽":"≽","≿":"≿","∋":"∋","∑":"∑","⋑":"⋑","⊃":"⊃","⊇":"⊇","⋑":"⋑","Þ":"Þ","Þ":"Þ","™":"™","Ћ":"Ћ","Ц":"Ц"," ":"\t","Τ":"Τ","Ť":"Ť","Ţ":"Ţ","Т":"Т","𝔗":"𝔗","∴":"∴","Θ":"Θ","  ":"  "," ":" ","∼":"∼","≃":"≃","≅":"≅","≈":"≈","𝕋":"𝕋","⃛":"⃛","𝒯":"𝒯","Ŧ":"Ŧ","Ú":"Ú","Ú":"Ú","↟":"↟","⥉":"⥉","Ў":"Ў","Ŭ":"Ŭ","Û":"Û","Û":"Û","У":"У","Ű":"Ű","𝔘":"𝔘","Ù":"Ù","Ù":"Ù","Ū":"Ū","_":"_","⏟":"⏟","⎵":"⎵","⏝":"⏝","⋃":"⋃","⊎":"⊎","Ų":"Ų","𝕌":"𝕌","↑":"↑","⤒":"⤒","⇅":"⇅","↕":"↕","⥮":"⥮","⊥":"⊥","↥":"↥","⇑":"⇑","⇕":"⇕","↖":"↖","↗":"↗","ϒ":"ϒ","Υ":"Υ","Ů":"Ů","𝒰":"𝒰","Ũ":"Ũ","Ü":"Ü","Ü":"Ü","⊫":"⊫","⫫":"⫫","В":"В","⊩":"⊩","⫦":"⫦","⋁":"⋁","‖":"‖","‖":"‖","∣":"∣","|":"|","❘":"❘","≀":"≀"," ":" ","𝔙":"𝔙","𝕍":"𝕍","𝒱":"𝒱","⊪":"⊪","Ŵ":"Ŵ","⋀":"⋀","𝔚":"𝔚","𝕎":"𝕎","𝒲":"𝒲","𝔛":"𝔛","Ξ":"Ξ","𝕏":"𝕏","𝒳":"𝒳","Я":"Я","Ї":"Ї","Ю":"Ю","Ý":"Ý","Ý":"Ý","Ŷ":"Ŷ","Ы":"Ы","𝔜":"𝔜","𝕐":"𝕐","𝒴":"𝒴","Ÿ":"Ÿ","Ж":"Ж","Ź":"Ź","Ž":"Ž","З":"З","Ż":"Ż","​":"​","Ζ":"Ζ","ℨ":"ℨ","ℤ":"ℤ","𝒵":"𝒵","á":"á","á":"á","ă":"ă","∾":"∾","∾̳":"∾̳","∿":"∿","â":"â","â":"â","´":"´","´":"´","а":"а","æ":"æ","æ":"æ","⁡":"⁡","𝔞":"𝔞","à":"à","à":"à","ℵ":"ℵ","ℵ":"ℵ","α":"α","ā":"ā","⨿":"⨿","&":"&","&":"&","∧":"∧","⩕":"⩕","⩜":"⩜","⩘":"⩘","⩚":"⩚","∠":"∠","⦤":"⦤","∠":"∠","∡":"∡","⦨":"⦨","⦩":"⦩","⦪":"⦪","⦫":"⦫","⦬":"⦬","⦭":"⦭","⦮":"⦮","⦯":"⦯","∟":"∟","⊾":"⊾","⦝":"⦝","∢":"∢","Å":"Å","⍼":"⍼","ą":"ą","𝕒":"𝕒","≈":"≈","⩰":"⩰","⩯":"⩯","≊":"≊","≋":"≋","'":"'","≈":"≈","≊":"≊","å":"å","å":"å","𝒶":"𝒶","*":"*","≈":"≈","≍":"≍","ã":"ã","ã":"ã","ä":"ä","ä":"ä","∳":"∳","⨑":"⨑","⫭":"⫭","≌":"≌","϶":"϶","‵":"‵","∽":"∽","⋍":"⋍","⊽":"⊽","⌅":"⌅","⌅":"⌅","⎵":"⎵","⎶":"⎶","≌":"≌","б":"б","„":"„","∵":"∵","∵":"∵","⦰":"⦰","϶":"϶","ℬ":"ℬ","β":"β","ℶ":"ℶ","≬":"≬","𝔟":"𝔟","⋂":"⋂","◯":"◯","⋃":"⋃","⨀":"⨀","⨁":"⨁","⨂":"⨂","⨆":"⨆","★":"★","▽":"▽","△":"△","⨄":"⨄","⋁":"⋁","⋀":"⋀","⤍":"⤍","⧫":"⧫","▪":"▪","▴":"▴","▾":"▾","◂":"◂","▸":"▸","␣":"␣","▒":"▒","░":"░","▓":"▓","█":"█","=⃥":"=⃥","≡⃥":"≡⃥","⌐":"⌐","𝕓":"𝕓","⊥":"⊥","⊥":"⊥","⋈":"⋈","╗":"╗","╔":"╔","╖":"╖","╓":"╓","═":"═","╦":"╦","╩":"╩","╤":"╤","╧":"╧","╝":"╝","╚":"╚","╜":"╜","╙":"╙","║":"║","╬":"╬","╣":"╣","╠":"╠","╫":"╫","╢":"╢","╟":"╟","⧉":"⧉","╕":"╕","╒":"╒","┐":"┐","┌":"┌","─":"─","╥":"╥","╨":"╨","┬":"┬","┴":"┴","⊟":"⊟","⊞":"⊞","⊠":"⊠","╛":"╛","╘":"╘","┘":"┘","└":"└","│":"│","╪":"╪","╡":"╡","╞":"╞","┼":"┼","┤":"┤","├":"├","‵":"‵","˘":"˘","¦":"¦","¦":"¦","𝒷":"𝒷","⁏":"⁏","∽":"∽","⋍":"⋍","\":"\\","⧅":"⧅","⟈":"⟈","•":"•","•":"•","≎":"≎","⪮":"⪮","≏":"≏","≏":"≏","ć":"ć","∩":"∩","⩄":"⩄","⩉":"⩉","⩋":"⩋","⩇":"⩇","⩀":"⩀","∩︀":"∩︀","⁁":"⁁","ˇ":"ˇ","⩍":"⩍","č":"č","ç":"ç","ç":"ç","ĉ":"ĉ","⩌":"⩌","⩐":"⩐","ċ":"ċ","¸":"¸","¸":"¸","⦲":"⦲","¢":"¢","¢":"¢","·":"·","𝔠":"𝔠","ч":"ч","✓":"✓","✓":"✓","χ":"χ","○":"○","⧃":"⧃","ˆ":"ˆ","≗":"≗","↺":"↺","↻":"↻","®":"®","Ⓢ":"Ⓢ","⊛":"⊛","⊚":"⊚","⊝":"⊝","≗":"≗","⨐":"⨐","⫯":"⫯","⧂":"⧂","♣":"♣","♣":"♣",":":":","≔":"≔","≔":"≔",",":",","@":"@","∁":"∁","∘":"∘","∁":"∁","ℂ":"ℂ","≅":"≅","⩭":"⩭","∮":"∮","𝕔":"𝕔","∐":"∐","©":"©","©":"©","℗":"℗","↵":"↵","✗":"✗","𝒸":"𝒸","⫏":"⫏","⫑":"⫑","⫐":"⫐","⫒":"⫒","⋯":"⋯","⤸":"⤸","⤵":"⤵","⋞":"⋞","⋟":"⋟","↶":"↶","⤽":"⤽","∪":"∪","⩈":"⩈","⩆":"⩆","⩊":"⩊","⊍":"⊍","⩅":"⩅","∪︀":"∪︀","↷":"↷","⤼":"⤼","⋞":"⋞","⋟":"⋟","⋎":"⋎","⋏":"⋏","¤":"¤","¤":"¤","↶":"↶","↷":"↷","⋎":"⋎","⋏":"⋏","∲":"∲","∱":"∱","⌭":"⌭","⇓":"⇓","⥥":"⥥","†":"†","ℸ":"ℸ","↓":"↓","‐":"‐","⊣":"⊣","⤏":"⤏","˝":"˝","ď":"ď","д":"д","ⅆ":"ⅆ","‡":"‡","⇊":"⇊","⩷":"⩷","°":"°","°":"°","δ":"δ","⦱":"⦱","⥿":"⥿","𝔡":"𝔡","⇃":"⇃","⇂":"⇂","⋄":"⋄","⋄":"⋄","♦":"♦","♦":"♦","¨":"¨","ϝ":"ϝ","⋲":"⋲","÷":"÷","÷":"÷","÷":"÷","⋇":"⋇","⋇":"⋇","ђ":"ђ","⌞":"⌞","⌍":"⌍","$":"$","𝕕":"𝕕","˙":"˙","≐":"≐","≑":"≑","∸":"∸","∔":"∔","⊡":"⊡","⌆":"⌆","↓":"↓","⇊":"⇊","⇃":"⇃","⇂":"⇂","⤐":"⤐","⌟":"⌟","⌌":"⌌","𝒹":"𝒹","ѕ":"ѕ","⧶":"⧶","đ":"đ","⋱":"⋱","▿":"▿","▾":"▾","⇵":"⇵","⥯":"⥯","⦦":"⦦","џ":"џ","⟿":"⟿","⩷":"⩷","≑":"≑","é":"é","é":"é","⩮":"⩮","ě":"ě","≖":"≖","ê":"ê","ê":"ê","≕":"≕","э":"э","ė":"ė","ⅇ":"ⅇ","≒":"≒","𝔢":"𝔢","⪚":"⪚","è":"è","è":"è","⪖":"⪖","⪘":"⪘","⪙":"⪙","⏧":"⏧","ℓ":"ℓ","⪕":"⪕","⪗":"⪗","ē":"ē","∅":"∅","∅":"∅","∅":"∅"," ":" "," ":" "," ":" ","ŋ":"ŋ"," ":" ","ę":"ę","𝕖":"𝕖","⋕":"⋕","⧣":"⧣","⩱":"⩱","ε":"ε","ε":"ε","ϵ":"ϵ","≖":"≖","≕":"≕","≂":"≂","⪖":"⪖","⪕":"⪕","=":"=","≟":"≟","≡":"≡","⩸":"⩸","⧥":"⧥","≓":"≓","⥱":"⥱","ℯ":"ℯ","≐":"≐","≂":"≂","η":"η","ð":"ð","ð":"ð","ë":"ë","ë":"ë","€":"€","!":"!","∃":"∃","ℰ":"ℰ","ⅇ":"ⅇ","≒":"≒","ф":"ф","♀":"♀","ffi":"ffi","ff":"ff","ffl":"ffl","𝔣":"𝔣","fi":"fi","fj":"fj","♭":"♭","fl":"fl","▱":"▱","ƒ":"ƒ","𝕗":"𝕗","∀":"∀","⋔":"⋔","⫙":"⫙","⨍":"⨍","½":"½","½":"½","⅓":"⅓","¼":"¼","¼":"¼","⅕":"⅕","⅙":"⅙","⅛":"⅛","⅔":"⅔","⅖":"⅖","¾":"¾","¾":"¾","⅗":"⅗","⅜":"⅜","⅘":"⅘","⅚":"⅚","⅝":"⅝","⅞":"⅞","⁄":"⁄","⌢":"⌢","𝒻":"𝒻","≧":"≧","⪌":"⪌","ǵ":"ǵ","γ":"γ","ϝ":"ϝ","⪆":"⪆","ğ":"ğ","ĝ":"ĝ","г":"г","ġ":"ġ","≥":"≥","⋛":"⋛","≥":"≥","≧":"≧","⩾":"⩾","⩾":"⩾","⪩":"⪩","⪀":"⪀","⪂":"⪂","⪄":"⪄","⋛︀":"⋛︀","⪔":"⪔","𝔤":"𝔤","≫":"≫","⋙":"⋙","ℷ":"ℷ","ѓ":"ѓ","≷":"≷","⪒":"⪒","⪥":"⪥","⪤":"⪤","≩":"≩","⪊":"⪊","⪊":"⪊","⪈":"⪈","⪈":"⪈","≩":"≩","⋧":"⋧","𝕘":"𝕘","`":"`","ℊ":"ℊ","≳":"≳","⪎":"⪎","⪐":"⪐",">":">",">":">","⪧":"⪧","⩺":"⩺","⋗":"⋗","⦕":"⦕","⩼":"⩼","⪆":"⪆","⥸":"⥸","⋗":"⋗","⋛":"⋛","⪌":"⪌","≷":"≷","≳":"≳","≩︀":"≩︀","≩︀":"≩︀","⇔":"⇔"," ":" ","½":"½","ℋ":"ℋ","ъ":"ъ","↔":"↔","⥈":"⥈","↭":"↭","ℏ":"ℏ","ĥ":"ĥ","♥":"♥","♥":"♥","…":"…","⊹":"⊹","𝔥":"𝔥","⤥":"⤥","⤦":"⤦","⇿":"⇿","∻":"∻","↩":"↩","↪":"↪","𝕙":"𝕙","―":"―","𝒽":"𝒽","ℏ":"ℏ","ħ":"ħ","⁃":"⁃","‐":"‐","í":"í","í":"í","⁣":"⁣","î":"î","î":"î","и":"и","е":"е","¡":"¡","¡":"¡","⇔":"⇔","𝔦":"𝔦","ì":"ì","ì":"ì","ⅈ":"ⅈ","⨌":"⨌","∭":"∭","⧜":"⧜","℩":"℩","ij":"ij","ī":"ī","ℑ":"ℑ","ℐ":"ℐ","ℑ":"ℑ","ı":"ı","⊷":"⊷","Ƶ":"Ƶ","∈":"∈","℅":"℅","∞":"∞","⧝":"⧝","ı":"ı","∫":"∫","⊺":"⊺","ℤ":"ℤ","⊺":"⊺","⨗":"⨗","⨼":"⨼","ё":"ё","į":"į","𝕚":"𝕚","ι":"ι","⨼":"⨼","¿":"¿","¿":"¿","𝒾":"𝒾","∈":"∈","⋹":"⋹","⋵":"⋵","⋴":"⋴","⋳":"⋳","∈":"∈","⁢":"⁢","ĩ":"ĩ","і":"і","ï":"ï","ï":"ï","ĵ":"ĵ","й":"й","𝔧":"𝔧","ȷ":"ȷ","𝕛":"𝕛","𝒿":"𝒿","ј":"ј","є":"є","κ":"κ","ϰ":"ϰ","ķ":"ķ","к":"к","𝔨":"𝔨","ĸ":"ĸ","х":"х","ќ":"ќ","𝕜":"𝕜","𝓀":"𝓀","⇚":"⇚","⇐":"⇐","⤛":"⤛","⤎":"⤎","≦":"≦","⪋":"⪋","⥢":"⥢","ĺ":"ĺ","⦴":"⦴","ℒ":"ℒ","λ":"λ","⟨":"⟨","⦑":"⦑","⟨":"⟨","⪅":"⪅","«":"«","«":"«","←":"←","⇤":"⇤","⤟":"⤟","⤝":"⤝","↩":"↩","↫":"↫","⤹":"⤹","⥳":"⥳","↢":"↢","⪫":"⪫","⤙":"⤙","⪭":"⪭","⪭︀":"⪭︀","⤌":"⤌","❲":"❲","{":"{","[":"[","⦋":"⦋","⦏":"⦏","⦍":"⦍","ľ":"ľ","ļ":"ļ","⌈":"⌈","{":"{","л":"л","⤶":"⤶","“":"“","„":"„","⥧":"⥧","⥋":"⥋","↲":"↲","≤":"≤","←":"←","↢":"↢","↽":"↽","↼":"↼","⇇":"⇇","↔":"↔","⇆":"⇆","⇋":"⇋","↭":"↭","⋋":"⋋","⋚":"⋚","≤":"≤","≦":"≦","⩽":"⩽","⩽":"⩽","⪨":"⪨","⩿":"⩿","⪁":"⪁","⪃":"⪃","⋚︀":"⋚︀","⪓":"⪓","⪅":"⪅","⋖":"⋖","⋚":"⋚","⪋":"⪋","≶":"≶","≲":"≲","⥼":"⥼","⌊":"⌊","𝔩":"𝔩","≶":"≶","⪑":"⪑","↽":"↽","↼":"↼","⥪":"⥪","▄":"▄","љ":"љ","≪":"≪","⇇":"⇇","⌞":"⌞","⥫":"⥫","◺":"◺","ŀ":"ŀ","⎰":"⎰","⎰":"⎰","≨":"≨","⪉":"⪉","⪉":"⪉","⪇":"⪇","⪇":"⪇","≨":"≨","⋦":"⋦","⟬":"⟬","⇽":"⇽","⟦":"⟦","⟵":"⟵","⟷":"⟷","⟼":"⟼","⟶":"⟶","↫":"↫","↬":"↬","⦅":"⦅","𝕝":"𝕝","⨭":"⨭","⨴":"⨴","∗":"∗","_":"_","◊":"◊","◊":"◊","⧫":"⧫","(":"(","⦓":"⦓","⇆":"⇆","⌟":"⌟","⇋":"⇋","⥭":"⥭","‎":"‎","⊿":"⊿","‹":"‹","𝓁":"𝓁","↰":"↰","≲":"≲","⪍":"⪍","⪏":"⪏","[":"[","‘":"‘","‚":"‚","ł":"ł","<":"<","<":"<","⪦":"⪦","⩹":"⩹","⋖":"⋖","⋋":"⋋","⋉":"⋉","⥶":"⥶","⩻":"⩻","⦖":"⦖","◃":"◃","⊴":"⊴","◂":"◂","⥊":"⥊","⥦":"⥦","≨︀":"≨︀","≨︀":"≨︀","∺":"∺","¯":"¯","¯":"¯","♂":"♂","✠":"✠","✠":"✠","↦":"↦","↦":"↦","↧":"↧","↤":"↤","↥":"↥","▮":"▮","⨩":"⨩","м":"м","—":"—","∡":"∡","𝔪":"𝔪","℧":"℧","µ":"µ","µ":"µ","∣":"∣","*":"*","⫰":"⫰","·":"·","·":"·","−":"−","⊟":"⊟","∸":"∸","⨪":"⨪","⫛":"⫛","…":"…","∓":"∓","⊧":"⊧","𝕞":"𝕞","∓":"∓","𝓂":"𝓂","∾":"∾","μ":"μ","⊸":"⊸","⊸":"⊸","⋙̸":"⋙̸","≫⃒":"≫⃒","≫̸":"≫̸","⇍":"⇍","⇎":"⇎","⋘̸":"⋘̸","≪⃒":"≪⃒","≪̸":"≪̸","⇏":"⇏","⊯":"⊯","⊮":"⊮","∇":"∇","ń":"ń","∠⃒":"∠⃒","≉":"≉","⩰̸":"⩰̸","≋̸":"≋̸","ʼn":"ʼn","≉":"≉","♮":"♮","♮":"♮","ℕ":"ℕ"," ":" "," ":" ","≎̸":"≎̸","≏̸":"≏̸","⩃":"⩃","ň":"ň","ņ":"ņ","≇":"≇","⩭̸":"⩭̸","⩂":"⩂","н":"н","–":"–","≠":"≠","⇗":"⇗","⤤":"⤤","↗":"↗","↗":"↗","≐̸":"≐̸","≢":"≢","⤨":"⤨","≂̸":"≂̸","∄":"∄","∄":"∄","𝔫":"𝔫","≧̸":"≧̸","≱":"≱","≱":"≱","≧̸":"≧̸","⩾̸":"⩾̸","⩾̸":"⩾̸","≵":"≵","≯":"≯","≯":"≯","⇎":"⇎","↮":"↮","⫲":"⫲","∋":"∋","⋼":"⋼","⋺":"⋺","∋":"∋","њ":"њ","⇍":"⇍","≦̸":"≦̸","↚":"↚","‥":"‥","≰":"≰","↚":"↚","↮":"↮","≰":"≰","≦̸":"≦̸","⩽̸":"⩽̸","⩽̸":"⩽̸","≮":"≮","≴":"≴","≮":"≮","⋪":"⋪","⋬":"⋬","∤":"∤","𝕟":"𝕟","¬":"¬","¬":"¬","∉":"∉","⋹̸":"⋹̸","⋵̸":"⋵̸","∉":"∉","⋷":"⋷","⋶":"⋶","∌":"∌","∌":"∌","⋾":"⋾","⋽":"⋽","∦":"∦","∦":"∦","⫽⃥":"⫽⃥","∂̸":"∂̸","⨔":"⨔","⊀":"⊀","⋠":"⋠","⪯̸":"⪯̸","⊀":"⊀","⪯̸":"⪯̸","⇏":"⇏","↛":"↛","⤳̸":"⤳̸","↝̸":"↝̸","↛":"↛","⋫":"⋫","⋭":"⋭","⊁":"⊁","⋡":"⋡","⪰̸":"⪰̸","𝓃":"𝓃","∤":"∤","∦":"∦","≁":"≁","≄":"≄","≄":"≄","∤":"∤","∦":"∦","⋢":"⋢","⋣":"⋣","⊄":"⊄","⫅̸":"⫅̸","⊈":"⊈","⊂⃒":"⊂⃒","⊈":"⊈","⫅̸":"⫅̸","⊁":"⊁","⪰̸":"⪰̸","⊅":"⊅","⫆̸":"⫆̸","⊉":"⊉","⊃⃒":"⊃⃒","⊉":"⊉","⫆̸":"⫆̸","≹":"≹","ñ":"ñ","ñ":"ñ","≸":"≸","⋪":"⋪","⋬":"⋬","⋫":"⋫","⋭":"⋭","ν":"ν","#":"#","№":"№"," ":" ","⊭":"⊭","⤄":"⤄","≍⃒":"≍⃒","⊬":"⊬","≥⃒":"≥⃒",">⃒":">⃒","⧞":"⧞","⤂":"⤂","≤⃒":"≤⃒","<⃒":"<⃒","⊴⃒":"⊴⃒","⤃":"⤃","⊵⃒":"⊵⃒","∼⃒":"∼⃒","⇖":"⇖","⤣":"⤣","↖":"↖","↖":"↖","⤧":"⤧","Ⓢ":"Ⓢ","ó":"ó","ó":"ó","⊛":"⊛","⊚":"⊚","ô":"ô","ô":"ô","о":"о","⊝":"⊝","ő":"ő","⨸":"⨸","⊙":"⊙","⦼":"⦼","œ":"œ","⦿":"⦿","𝔬":"𝔬","˛":"˛","ò":"ò","ò":"ò","⧁":"⧁","⦵":"⦵","Ω":"Ω","∮":"∮","↺":"↺","⦾":"⦾","⦻":"⦻","‾":"‾","⧀":"⧀","ō":"ō","ω":"ω","ο":"ο","⦶":"⦶","⊖":"⊖","𝕠":"𝕠","⦷":"⦷","⦹":"⦹","⊕":"⊕","∨":"∨","↻":"↻","⩝":"⩝","ℴ":"ℴ","ℴ":"ℴ","ª":"ª","ª":"ª","º":"º","º":"º","⊶":"⊶","⩖":"⩖","⩗":"⩗","⩛":"⩛","ℴ":"ℴ","ø":"ø","ø":"ø","⊘":"⊘","õ":"õ","õ":"õ","⊗":"⊗","⨶":"⨶","ö":"ö","ö":"ö","⌽":"⌽","∥":"∥","¶":"¶","¶":"¶","∥":"∥","⫳":"⫳","⫽":"⫽","∂":"∂","п":"п","%":"%",".":".","‰":"‰","⊥":"⊥","‱":"‱","𝔭":"𝔭","φ":"φ","ϕ":"ϕ","ℳ":"ℳ","☎":"☎","π":"π","⋔":"⋔","ϖ":"ϖ","ℏ":"ℏ","ℎ":"ℎ","ℏ":"ℏ","+":"+","⨣":"⨣","⊞":"⊞","⨢":"⨢","∔":"∔","⨥":"⨥","⩲":"⩲","±":"±","±":"±","⨦":"⨦","⨧":"⨧","±":"±","⨕":"⨕","𝕡":"𝕡","£":"£","£":"£","≺":"≺","⪳":"⪳","⪷":"⪷","≼":"≼","⪯":"⪯","≺":"≺","⪷":"⪷","≼":"≼","⪯":"⪯","⪹":"⪹","⪵":"⪵","⋨":"⋨","≾":"≾","′":"′","ℙ":"ℙ","⪵":"⪵","⪹":"⪹","⋨":"⋨","∏":"∏","⌮":"⌮","⌒":"⌒","⌓":"⌓","∝":"∝","∝":"∝","≾":"≾","⊰":"⊰","𝓅":"𝓅","ψ":"ψ"," ":" ","𝔮":"𝔮","⨌":"⨌","𝕢":"𝕢","⁗":"⁗","𝓆":"𝓆","ℍ":"ℍ","⨖":"⨖","?":"?","≟":"≟",""":'"',""":'"',"⇛":"⇛","⇒":"⇒","⤜":"⤜","⤏":"⤏","⥤":"⥤","∽̱":"∽̱","ŕ":"ŕ","√":"√","⦳":"⦳","⟩":"⟩","⦒":"⦒","⦥":"⦥","⟩":"⟩","»":"»","»":"»","→":"→","⥵":"⥵","⇥":"⇥","⤠":"⤠","⤳":"⤳","⤞":"⤞","↪":"↪","↬":"↬","⥅":"⥅","⥴":"⥴","↣":"↣","↝":"↝","⤚":"⤚","∶":"∶","ℚ":"ℚ","⤍":"⤍","❳":"❳","}":"}","]":"]","⦌":"⦌","⦎":"⦎","⦐":"⦐","ř":"ř","ŗ":"ŗ","⌉":"⌉","}":"}","р":"р","⤷":"⤷","⥩":"⥩","”":"”","”":"”","↳":"↳","ℜ":"ℜ","ℛ":"ℛ","ℜ":"ℜ","ℝ":"ℝ","▭":"▭","®":"®","®":"®","⥽":"⥽","⌋":"⌋","𝔯":"𝔯","⇁":"⇁","⇀":"⇀","⥬":"⥬","ρ":"ρ","ϱ":"ϱ","→":"→","↣":"↣","⇁":"⇁","⇀":"⇀","⇄":"⇄","⇌":"⇌","⇉":"⇉","↝":"↝","⋌":"⋌","˚":"˚","≓":"≓","⇄":"⇄","⇌":"⇌","‏":"‏","⎱":"⎱","⎱":"⎱","⫮":"⫮","⟭":"⟭","⇾":"⇾","⟧":"⟧","⦆":"⦆","𝕣":"𝕣","⨮":"⨮","⨵":"⨵",")":")","⦔":"⦔","⨒":"⨒","⇉":"⇉","›":"›","𝓇":"𝓇","↱":"↱","]":"]","’":"’","’":"’","⋌":"⋌","⋊":"⋊","▹":"▹","⊵":"⊵","▸":"▸","⧎":"⧎","⥨":"⥨","℞":"℞","ś":"ś","‚":"‚","≻":"≻","⪴":"⪴","⪸":"⪸","š":"š","≽":"≽","⪰":"⪰","ş":"ş","ŝ":"ŝ","⪶":"⪶","⪺":"⪺","⋩":"⋩","⨓":"⨓","≿":"≿","с":"с","⋅":"⋅","⊡":"⊡","⩦":"⩦","⇘":"⇘","⤥":"⤥","↘":"↘","↘":"↘","§":"§","§":"§",";":";","⤩":"⤩","∖":"∖","∖":"∖","✶":"✶","𝔰":"𝔰","⌢":"⌢","♯":"♯","щ":"щ","ш":"ш","∣":"∣","∥":"∥","­":"­","­":"­","σ":"σ","ς":"ς","ς":"ς","∼":"∼","⩪":"⩪","≃":"≃","≃":"≃","⪞":"⪞","⪠":"⪠","⪝":"⪝","⪟":"⪟","≆":"≆","⨤":"⨤","⥲":"⥲","←":"←","∖":"∖","⨳":"⨳","⧤":"⧤","∣":"∣","⌣":"⌣","⪪":"⪪","⪬":"⪬","⪬︀":"⪬︀","ь":"ь","/":"/","⧄":"⧄","⌿":"⌿","𝕤":"𝕤","♠":"♠","♠":"♠","∥":"∥","⊓":"⊓","⊓︀":"⊓︀","⊔":"⊔","⊔︀":"⊔︀","⊏":"⊏","⊑":"⊑","⊏":"⊏","⊑":"⊑","⊐":"⊐","⊒":"⊒","⊐":"⊐","⊒":"⊒","□":"□","□":"□","▪":"▪","▪":"▪","→":"→","𝓈":"𝓈","∖":"∖","⌣":"⌣","⋆":"⋆","☆":"☆","★":"★","ϵ":"ϵ","ϕ":"ϕ","¯":"¯","⊂":"⊂","⫅":"⫅","⪽":"⪽","⊆":"⊆","⫃":"⫃","⫁":"⫁","⫋":"⫋","⊊":"⊊","⪿":"⪿","⥹":"⥹","⊂":"⊂","⊆":"⊆","⫅":"⫅","⊊":"⊊","⫋":"⫋","⫇":"⫇","⫕":"⫕","⫓":"⫓","≻":"≻","⪸":"⪸","≽":"≽","⪰":"⪰","⪺":"⪺","⪶":"⪶","⋩":"⋩","≿":"≿","∑":"∑","♪":"♪","¹":"¹","¹":"¹","²":"²","²":"²","³":"³","³":"³","⊃":"⊃","⫆":"⫆","⪾":"⪾","⫘":"⫘","⊇":"⊇","⫄":"⫄","⟉":"⟉","⫗":"⫗","⥻":"⥻","⫂":"⫂","⫌":"⫌","⊋":"⊋","⫀":"⫀","⊃":"⊃","⊇":"⊇","⫆":"⫆","⊋":"⊋","⫌":"⫌","⫈":"⫈","⫔":"⫔","⫖":"⫖","⇙":"⇙","⤦":"⤦","↙":"↙","↙":"↙","⤪":"⤪","ß":"ß","ß":"ß","⌖":"⌖","τ":"τ","⎴":"⎴","ť":"ť","ţ":"ţ","т":"т","⃛":"⃛","⌕":"⌕","𝔱":"𝔱","∴":"∴","∴":"∴","θ":"θ","ϑ":"ϑ","ϑ":"ϑ","≈":"≈","∼":"∼"," ":" ","≈":"≈","∼":"∼","þ":"þ","þ":"þ","˜":"˜","×":"×","×":"×","⊠":"⊠","⨱":"⨱","⨰":"⨰","∭":"∭","⤨":"⤨","⊤":"⊤","⌶":"⌶","⫱":"⫱","𝕥":"𝕥","⫚":"⫚","⤩":"⤩","‴":"‴","™":"™","▵":"▵","▿":"▿","◃":"◃","⊴":"⊴","≜":"≜","▹":"▹","⊵":"⊵","◬":"◬","≜":"≜","⨺":"⨺","⨹":"⨹","⧍":"⧍","⨻":"⨻","⏢":"⏢","𝓉":"𝓉","ц":"ц","ћ":"ћ","ŧ":"ŧ","≬":"≬","↞":"↞","↠":"↠","⇑":"⇑","⥣":"⥣","ú":"ú","ú":"ú","↑":"↑","ў":"ў","ŭ":"ŭ","û":"û","û":"û","у":"у","⇅":"⇅","ű":"ű","⥮":"⥮","⥾":"⥾","𝔲":"𝔲","ù":"ù","ù":"ù","↿":"↿","↾":"↾","▀":"▀","⌜":"⌜","⌜":"⌜","⌏":"⌏","◸":"◸","ū":"ū","¨":"¨","¨":"¨","ų":"ų","𝕦":"𝕦","↑":"↑","↕":"↕","↿":"↿","↾":"↾","⊎":"⊎","υ":"υ","ϒ":"ϒ","υ":"υ","⇈":"⇈","⌝":"⌝","⌝":"⌝","⌎":"⌎","ů":"ů","◹":"◹","𝓊":"𝓊","⋰":"⋰","ũ":"ũ","▵":"▵","▴":"▴","⇈":"⇈","ü":"ü","ü":"ü","⦧":"⦧","⇕":"⇕","⫨":"⫨","⫩":"⫩","⊨":"⊨","⦜":"⦜","ϵ":"ϵ","ϰ":"ϰ","∅":"∅","ϕ":"ϕ","ϖ":"ϖ","∝":"∝","↕":"↕","ϱ":"ϱ","ς":"ς","⊊︀":"⊊︀","⫋︀":"⫋︀","⊋︀":"⊋︀","⫌︀":"⫌︀","ϑ":"ϑ","⊲":"⊲","⊳":"⊳","в":"в","⊢":"⊢","∨":"∨","⊻":"⊻","≚":"≚","⋮":"⋮","|":"|","|":"|","𝔳":"𝔳","⊲":"⊲","⊂⃒":"⊂⃒","⊃⃒":"⊃⃒","𝕧":"𝕧","∝":"∝","⊳":"⊳","𝓋":"𝓋","⫋︀":"⫋︀","⊊︀":"⊊︀","⫌︀":"⫌︀","⊋︀":"⊋︀","⦚":"⦚","ŵ":"ŵ","⩟":"⩟","∧":"∧","≙":"≙","℘":"℘","𝔴":"𝔴","𝕨":"𝕨","℘":"℘","≀":"≀","≀":"≀","𝓌":"𝓌","⋂":"⋂","◯":"◯","⋃":"⋃","▽":"▽","𝔵":"𝔵","⟺":"⟺","⟷":"⟷","ξ":"ξ","⟸":"⟸","⟵":"⟵","⟼":"⟼","⋻":"⋻","⨀":"⨀","𝕩":"𝕩","⨁":"⨁","⨂":"⨂","⟹":"⟹","⟶":"⟶","𝓍":"𝓍","⨆":"⨆","⨄":"⨄","△":"△","⋁":"⋁","⋀":"⋀","ý":"ý","ý":"ý","я":"я","ŷ":"ŷ","ы":"ы","¥":"¥","¥":"¥","𝔶":"𝔶","ї":"ї","𝕪":"𝕪","𝓎":"𝓎","ю":"ю","ÿ":"ÿ","ÿ":"ÿ","ź":"ź","ž":"ž","з":"з","ż":"ż","ℨ":"ℨ","ζ":"ζ","𝔷":"𝔷","ж":"ж","⇝":"⇝","𝕫":"𝕫","𝓏":"𝓏","‍":"‍","‌":"‌"},characters:{"Æ":"Æ","&":"&","Á":"Á","Ă":"Ă","Â":"Â","А":"А","𝔄":"𝔄","À":"À","Α":"Α","Ā":"Ā","⩓":"⩓","Ą":"Ą","𝔸":"𝔸","⁡":"⁡","Å":"Å","𝒜":"𝒜","≔":"≔","Ã":"Ã","Ä":"Ä","∖":"∖","⫧":"⫧","⌆":"⌆","Б":"Б","∵":"∵","ℬ":"ℬ","Β":"Β","𝔅":"𝔅","𝔹":"𝔹","˘":"˘","≎":"≎","Ч":"Ч","©":"©","Ć":"Ć","⋒":"⋒","ⅅ":"ⅅ","ℭ":"ℭ","Č":"Č","Ç":"Ç","Ĉ":"Ĉ","∰":"∰","Ċ":"Ċ","¸":"¸","·":"·","Χ":"Χ","⊙":"⊙","⊖":"⊖","⊕":"⊕","⊗":"⊗","∲":"∲","”":"”","’":"’","∷":"∷","⩴":"⩴","≡":"≡","∯":"∯","∮":"∮","ℂ":"ℂ","∐":"∐","∳":"∳","⨯":"⨯","𝒞":"𝒞","⋓":"⋓","≍":"≍","⤑":"⤑","Ђ":"Ђ","Ѕ":"Ѕ","Џ":"Џ","‡":"‡","↡":"↡","⫤":"⫤","Ď":"Ď","Д":"Д","∇":"∇","Δ":"Δ","𝔇":"𝔇","´":"´","˙":"˙","˝":"˝","`":"`","˜":"˜","⋄":"⋄","ⅆ":"ⅆ","𝔻":"𝔻","¨":"¨","⃜":"⃜","≐":"≐","⇓":"⇓","⇐":"⇐","⇔":"⇔","⟸":"⟸","⟺":"⟺","⟹":"⟹","⇒":"⇒","⊨":"⊨","⇑":"⇑","⇕":"⇕","∥":"∥","↓":"↓","⤓":"⤓","⇵":"⇵","̑":"̑","⥐":"⥐","⥞":"⥞","↽":"↽","⥖":"⥖","⥟":"⥟","⇁":"⇁","⥗":"⥗","⊤":"⊤","↧":"↧","𝒟":"𝒟","Đ":"Đ","Ŋ":"Ŋ","Ð":"Ð","É":"É","Ě":"Ě","Ê":"Ê","Э":"Э","Ė":"Ė","𝔈":"𝔈","È":"È","∈":"∈","Ē":"Ē","◻":"◻","▫":"▫","Ę":"Ę","𝔼":"𝔼","Ε":"Ε","⩵":"⩵","≂":"≂","⇌":"⇌","ℰ":"ℰ","⩳":"⩳","Η":"Η","Ë":"Ë","∃":"∃","ⅇ":"ⅇ","Ф":"Ф","𝔉":"𝔉","◼":"◼","▪":"▪","𝔽":"𝔽","∀":"∀","ℱ":"ℱ","Ѓ":"Ѓ",">":">","Γ":"Γ","Ϝ":"Ϝ","Ğ":"Ğ","Ģ":"Ģ","Ĝ":"Ĝ","Г":"Г","Ġ":"Ġ","𝔊":"𝔊","⋙":"⋙","𝔾":"𝔾","≥":"≥","⋛":"⋛","≧":"≧","⪢":"⪢","≷":"≷","⩾":"⩾","≳":"≳","𝒢":"𝒢","≫":"≫","Ъ":"Ъ","ˇ":"ˇ","^":"^","Ĥ":"Ĥ","ℌ":"ℌ","ℋ":"ℋ","ℍ":"ℍ","─":"─","Ħ":"Ħ","≏":"≏","Е":"Е","IJ":"IJ","Ё":"Ё","Í":"Í","Î":"Î","И":"И","İ":"İ","ℑ":"ℑ","Ì":"Ì","Ī":"Ī","ⅈ":"ⅈ","∬":"∬","∫":"∫","⋂":"⋂","⁣":"⁣","⁢":"⁢","Į":"Į","𝕀":"𝕀","Ι":"Ι","ℐ":"ℐ","Ĩ":"Ĩ","І":"І","Ï":"Ï","Ĵ":"Ĵ","Й":"Й","𝔍":"𝔍","𝕁":"𝕁","𝒥":"𝒥","Ј":"Ј","Є":"Є","Х":"Х","Ќ":"Ќ","Κ":"Κ","Ķ":"Ķ","К":"К","𝔎":"𝔎","𝕂":"𝕂","𝒦":"𝒦","Љ":"Љ","<":"<","Ĺ":"Ĺ","Λ":"Λ","⟪":"⟪","ℒ":"ℒ","↞":"↞","Ľ":"Ľ","Ļ":"Ļ","Л":"Л","⟨":"⟨","←":"←","⇤":"⇤","⇆":"⇆","⌈":"⌈","⟦":"⟦","⥡":"⥡","⇃":"⇃","⥙":"⥙","⌊":"⌊","↔":"↔","⥎":"⥎","⊣":"⊣","↤":"↤","⥚":"⥚","⊲":"⊲","⧏":"⧏","⊴":"⊴","⥑":"⥑","⥠":"⥠","↿":"↿","⥘":"⥘","↼":"↼","⥒":"⥒","⋚":"⋚","≦":"≦","≶":"≶","⪡":"⪡","⩽":"⩽","≲":"≲","𝔏":"𝔏","⋘":"⋘","⇚":"⇚","Ŀ":"Ŀ","⟵":"⟵","⟷":"⟷","⟶":"⟶","𝕃":"𝕃","↙":"↙","↘":"↘","↰":"↰","Ł":"Ł","≪":"≪","⤅":"⤅","М":"М"," ":" ","ℳ":"ℳ","𝔐":"𝔐","∓":"∓","𝕄":"𝕄","Μ":"Μ","Њ":"Њ","Ń":"Ń","Ň":"Ň","Ņ":"Ņ","Н":"Н","​":"​","\n":" ","𝔑":"𝔑","⁠":"⁠"," ":" ","ℕ":"ℕ","⫬":"⫬","≢":"≢","≭":"≭","∦":"∦","∉":"∉","≠":"≠","≂̸":"≂̸","∄":"∄","≯":"≯","≱":"≱","≧̸":"≧̸","≫̸":"≫̸","≹":"≹","⩾̸":"⩾̸","≵":"≵","≎̸":"≎̸","≏̸":"≏̸","⋪":"⋪","⧏̸":"⧏̸","⋬":"⋬","≮":"≮","≰":"≰","≸":"≸","≪̸":"≪̸","⩽̸":"⩽̸","≴":"≴","⪢̸":"⪢̸","⪡̸":"⪡̸","⊀":"⊀","⪯̸":"⪯̸","⋠":"⋠","∌":"∌","⋫":"⋫","⧐̸":"⧐̸","⋭":"⋭","⊏̸":"⊏̸","⋢":"⋢","⊐̸":"⊐̸","⋣":"⋣","⊂⃒":"⊂⃒","⊈":"⊈","⊁":"⊁","⪰̸":"⪰̸","⋡":"⋡","≿̸":"≿̸","⊃⃒":"⊃⃒","⊉":"⊉","≁":"≁","≄":"≄","≇":"≇","≉":"≉","∤":"∤","𝒩":"𝒩","Ñ":"Ñ","Ν":"Ν","Œ":"Œ","Ó":"Ó","Ô":"Ô","О":"О","Ő":"Ő","𝔒":"𝔒","Ò":"Ò","Ō":"Ō","Ω":"Ω","Ο":"Ο","𝕆":"𝕆","“":"“","‘":"‘","⩔":"⩔","𝒪":"𝒪","Ø":"Ø","Õ":"Õ","⨷":"⨷","Ö":"Ö","‾":"‾","⏞":"⏞","⎴":"⎴","⏜":"⏜","∂":"∂","П":"П","𝔓":"𝔓","Φ":"Φ","Π":"Π","±":"±","ℙ":"ℙ","⪻":"⪻","≺":"≺","⪯":"⪯","≼":"≼","≾":"≾","″":"″","∏":"∏","∝":"∝","𝒫":"𝒫","Ψ":"Ψ",'"':""","𝔔":"𝔔","ℚ":"ℚ","𝒬":"𝒬","⤐":"⤐","®":"®","Ŕ":"Ŕ","⟫":"⟫","↠":"↠","⤖":"⤖","Ř":"Ř","Ŗ":"Ŗ","Р":"Р","ℜ":"ℜ","∋":"∋","⇋":"⇋","⥯":"⥯","Ρ":"Ρ","⟩":"⟩","→":"→","⇥":"⇥","⇄":"⇄","⌉":"⌉","⟧":"⟧","⥝":"⥝","⇂":"⇂","⥕":"⥕","⌋":"⌋","⊢":"⊢","↦":"↦","⥛":"⥛","⊳":"⊳","⧐":"⧐","⊵":"⊵","⥏":"⥏","⥜":"⥜","↾":"↾","⥔":"⥔","⇀":"⇀","⥓":"⥓","ℝ":"ℝ","⥰":"⥰","⇛":"⇛","ℛ":"ℛ","↱":"↱","⧴":"⧴","Щ":"Щ","Ш":"Ш","Ь":"Ь","Ś":"Ś","⪼":"⪼","Š":"Š","Ş":"Ş","Ŝ":"Ŝ","С":"С","𝔖":"𝔖","↑":"↑","Σ":"Σ","∘":"∘","𝕊":"𝕊","√":"√","□":"□","⊓":"⊓","⊏":"⊏","⊑":"⊑","⊐":"⊐","⊒":"⊒","⊔":"⊔","𝒮":"𝒮","⋆":"⋆","⋐":"⋐","⊆":"⊆","≻":"≻","⪰":"⪰","≽":"≽","≿":"≿","∑":"∑","⋑":"⋑","⊃":"⊃","⊇":"⊇","Þ":"Þ","™":"™","Ћ":"Ћ","Ц":"Ц","\t":" ","Τ":"Τ","Ť":"Ť","Ţ":"Ţ","Т":"Т","𝔗":"𝔗","∴":"∴","Θ":"Θ","  ":"  "," ":" ","∼":"∼","≃":"≃","≅":"≅","≈":"≈","𝕋":"𝕋","⃛":"⃛","𝒯":"𝒯","Ŧ":"Ŧ","Ú":"Ú","↟":"↟","⥉":"⥉","Ў":"Ў","Ŭ":"Ŭ","Û":"Û","У":"У","Ű":"Ű","𝔘":"𝔘","Ù":"Ù","Ū":"Ū",_:"_","⏟":"⏟","⎵":"⎵","⏝":"⏝","⋃":"⋃","⊎":"⊎","Ų":"Ų","𝕌":"𝕌","⤒":"⤒","⇅":"⇅","↕":"↕","⥮":"⥮","⊥":"⊥","↥":"↥","↖":"↖","↗":"↗","ϒ":"ϒ","Υ":"Υ","Ů":"Ů","𝒰":"𝒰","Ũ":"Ũ","Ü":"Ü","⊫":"⊫","⫫":"⫫","В":"В","⊩":"⊩","⫦":"⫦","⋁":"⋁","‖":"‖","∣":"∣","|":"|","❘":"❘","≀":"≀"," ":" ","𝔙":"𝔙","𝕍":"𝕍","𝒱":"𝒱","⊪":"⊪","Ŵ":"Ŵ","⋀":"⋀","𝔚":"𝔚","𝕎":"𝕎","𝒲":"𝒲","𝔛":"𝔛","Ξ":"Ξ","𝕏":"𝕏","𝒳":"𝒳","Я":"Я","Ї":"Ї","Ю":"Ю","Ý":"Ý","Ŷ":"Ŷ","Ы":"Ы","𝔜":"𝔜","𝕐":"𝕐","𝒴":"𝒴","Ÿ":"Ÿ","Ж":"Ж","Ź":"Ź","Ž":"Ž","З":"З","Ż":"Ż","Ζ":"Ζ","ℨ":"ℨ","ℤ":"ℤ","𝒵":"𝒵","á":"á","ă":"ă","∾":"∾","∾̳":"∾̳","∿":"∿","â":"â","а":"а","æ":"æ","𝔞":"𝔞","à":"à","ℵ":"ℵ","α":"α","ā":"ā","⨿":"⨿","∧":"∧","⩕":"⩕","⩜":"⩜","⩘":"⩘","⩚":"⩚","∠":"∠","⦤":"⦤","∡":"∡","⦨":"⦨","⦩":"⦩","⦪":"⦪","⦫":"⦫","⦬":"⦬","⦭":"⦭","⦮":"⦮","⦯":"⦯","∟":"∟","⊾":"⊾","⦝":"⦝","∢":"∢","⍼":"⍼","ą":"ą","𝕒":"𝕒","⩰":"⩰","⩯":"⩯","≊":"≊","≋":"≋","'":"'","å":"å","𝒶":"𝒶","*":"*","ã":"ã","ä":"ä","⨑":"⨑","⫭":"⫭","≌":"≌","϶":"϶","‵":"‵","∽":"∽","⋍":"⋍","⊽":"⊽","⌅":"⌅","⎶":"⎶","б":"б","„":"„","⦰":"⦰","β":"β","ℶ":"ℶ","≬":"≬","𝔟":"𝔟","◯":"◯","⨀":"⨀","⨁":"⨁","⨂":"⨂","⨆":"⨆","★":"★","▽":"▽","△":"△","⨄":"⨄","⤍":"⤍","⧫":"⧫","▴":"▴","▾":"▾","◂":"◂","▸":"▸","␣":"␣","▒":"▒","░":"░","▓":"▓","█":"█","=⃥":"=⃥","≡⃥":"≡⃥","⌐":"⌐","𝕓":"𝕓","⋈":"⋈","╗":"╗","╔":"╔","╖":"╖","╓":"╓","═":"═","╦":"╦","╩":"╩","╤":"╤","╧":"╧","╝":"╝","╚":"╚","╜":"╜","╙":"╙","║":"║","╬":"╬","╣":"╣","╠":"╠","╫":"╫","╢":"╢","╟":"╟","⧉":"⧉","╕":"╕","╒":"╒","┐":"┐","┌":"┌","╥":"╥","╨":"╨","┬":"┬","┴":"┴","⊟":"⊟","⊞":"⊞","⊠":"⊠","╛":"╛","╘":"╘","┘":"┘","└":"└","│":"│","╪":"╪","╡":"╡","╞":"╞","┼":"┼","┤":"┤","├":"├","¦":"¦","𝒷":"𝒷","⁏":"⁏","\\":"\","⧅":"⧅","⟈":"⟈","•":"•","⪮":"⪮","ć":"ć","∩":"∩","⩄":"⩄","⩉":"⩉","⩋":"⩋","⩇":"⩇","⩀":"⩀","∩︀":"∩︀","⁁":"⁁","⩍":"⩍","č":"č","ç":"ç","ĉ":"ĉ","⩌":"⩌","⩐":"⩐","ċ":"ċ","⦲":"⦲","¢":"¢","𝔠":"𝔠","ч":"ч","✓":"✓","χ":"χ","○":"○","⧃":"⧃","ˆ":"ˆ","≗":"≗","↺":"↺","↻":"↻","Ⓢ":"Ⓢ","⊛":"⊛","⊚":"⊚","⊝":"⊝","⨐":"⨐","⫯":"⫯","⧂":"⧂","♣":"♣",":":":",",":",","@":"@","∁":"∁","⩭":"⩭","𝕔":"𝕔","℗":"℗","↵":"↵","✗":"✗","𝒸":"𝒸","⫏":"⫏","⫑":"⫑","⫐":"⫐","⫒":"⫒","⋯":"⋯","⤸":"⤸","⤵":"⤵","⋞":"⋞","⋟":"⋟","↶":"↶","⤽":"⤽","∪":"∪","⩈":"⩈","⩆":"⩆","⩊":"⩊","⊍":"⊍","⩅":"⩅","∪︀":"∪︀","↷":"↷","⤼":"⤼","⋎":"⋎","⋏":"⋏","¤":"¤","∱":"∱","⌭":"⌭","⥥":"⥥","†":"†","ℸ":"ℸ","‐":"‐","⤏":"⤏","ď":"ď","д":"д","⇊":"⇊","⩷":"⩷","°":"°","δ":"δ","⦱":"⦱","⥿":"⥿","𝔡":"𝔡","♦":"♦","ϝ":"ϝ","⋲":"⋲","÷":"÷","⋇":"⋇","ђ":"ђ","⌞":"⌞","⌍":"⌍",$:"$","𝕕":"𝕕","≑":"≑","∸":"∸","∔":"∔","⊡":"⊡","⌟":"⌟","⌌":"⌌","𝒹":"𝒹","ѕ":"ѕ","⧶":"⧶","đ":"đ","⋱":"⋱","▿":"▿","⦦":"⦦","џ":"џ","⟿":"⟿","é":"é","⩮":"⩮","ě":"ě","≖":"≖","ê":"ê","≕":"≕","э":"э","ė":"ė","≒":"≒","𝔢":"𝔢","⪚":"⪚","è":"è","⪖":"⪖","⪘":"⪘","⪙":"⪙","⏧":"⏧","ℓ":"ℓ","⪕":"⪕","⪗":"⪗","ē":"ē","∅":"∅"," ":" "," ":" "," ":" ","ŋ":"ŋ"," ":" ","ę":"ę","𝕖":"𝕖","⋕":"⋕","⧣":"⧣","⩱":"⩱","ε":"ε","ϵ":"ϵ","=":"=","≟":"≟","⩸":"⩸","⧥":"⧥","≓":"≓","⥱":"⥱","ℯ":"ℯ","η":"η","ð":"ð","ë":"ë","€":"€","!":"!","ф":"ф","♀":"♀","ffi":"ffi","ff":"ff","ffl":"ffl","𝔣":"𝔣","fi":"fi",fj:"fj","♭":"♭","fl":"fl","▱":"▱","ƒ":"ƒ","𝕗":"𝕗","⋔":"⋔","⫙":"⫙","⨍":"⨍","½":"½","⅓":"⅓","¼":"¼","⅕":"⅕","⅙":"⅙","⅛":"⅛","⅔":"⅔","⅖":"⅖","¾":"¾","⅗":"⅗","⅜":"⅜","⅘":"⅘","⅚":"⅚","⅝":"⅝","⅞":"⅞","⁄":"⁄","⌢":"⌢","𝒻":"𝒻","⪌":"⪌","ǵ":"ǵ","γ":"γ","⪆":"⪆","ğ":"ğ","ĝ":"ĝ","г":"г","ġ":"ġ","⪩":"⪩","⪀":"⪀","⪂":"⪂","⪄":"⪄","⋛︀":"⋛︀","⪔":"⪔","𝔤":"𝔤","ℷ":"ℷ","ѓ":"ѓ","⪒":"⪒","⪥":"⪥","⪤":"⪤","≩":"≩","⪊":"⪊","⪈":"⪈","⋧":"⋧","𝕘":"𝕘","ℊ":"ℊ","⪎":"⪎","⪐":"⪐","⪧":"⪧","⩺":"⩺","⋗":"⋗","⦕":"⦕","⩼":"⩼","⥸":"⥸","≩︀":"≩︀","ъ":"ъ","⥈":"⥈","↭":"↭","ℏ":"ℏ","ĥ":"ĥ","♥":"♥","…":"…","⊹":"⊹","𝔥":"𝔥","⤥":"⤥","⤦":"⤦","⇿":"⇿","∻":"∻","↩":"↩","↪":"↪","𝕙":"𝕙","―":"―","𝒽":"𝒽","ħ":"ħ","⁃":"⁃","í":"í","î":"î","и":"и","е":"е","¡":"¡","𝔦":"𝔦","ì":"ì","⨌":"⨌","∭":"∭","⧜":"⧜","℩":"℩","ij":"ij","ī":"ī","ı":"ı","⊷":"⊷","Ƶ":"Ƶ","℅":"℅","∞":"∞","⧝":"⧝","⊺":"⊺","⨗":"⨗","⨼":"⨼","ё":"ё","į":"į","𝕚":"𝕚","ι":"ι","¿":"¿","𝒾":"𝒾","⋹":"⋹","⋵":"⋵","⋴":"⋴","⋳":"⋳","ĩ":"ĩ","і":"і","ï":"ï","ĵ":"ĵ","й":"й","𝔧":"𝔧","ȷ":"ȷ","𝕛":"𝕛","𝒿":"𝒿","ј":"ј","є":"є","κ":"κ","ϰ":"ϰ","ķ":"ķ","к":"к","𝔨":"𝔨","ĸ":"ĸ","х":"х","ќ":"ќ","𝕜":"𝕜","𝓀":"𝓀","⤛":"⤛","⤎":"⤎","⪋":"⪋","⥢":"⥢","ĺ":"ĺ","⦴":"⦴","λ":"λ","⦑":"⦑","⪅":"⪅","«":"«","⤟":"⤟","⤝":"⤝","↫":"↫","⤹":"⤹","⥳":"⥳","↢":"↢","⪫":"⪫","⤙":"⤙","⪭":"⪭","⪭︀":"⪭︀","⤌":"⤌","❲":"❲","{":"{","[":"[","⦋":"⦋","⦏":"⦏","⦍":"⦍","ľ":"ľ","ļ":"ļ","л":"л","⤶":"⤶","⥧":"⥧","⥋":"⥋","↲":"↲","≤":"≤","⇇":"⇇","⋋":"⋋","⪨":"⪨","⩿":"⩿","⪁":"⪁","⪃":"⪃","⋚︀":"⋚︀","⪓":"⪓","⋖":"⋖","⥼":"⥼","𝔩":"𝔩","⪑":"⪑","⥪":"⥪","▄":"▄","љ":"љ","⥫":"⥫","◺":"◺","ŀ":"ŀ","⎰":"⎰","≨":"≨","⪉":"⪉","⪇":"⪇","⋦":"⋦","⟬":"⟬","⇽":"⇽","⟼":"⟼","↬":"↬","⦅":"⦅","𝕝":"𝕝","⨭":"⨭","⨴":"⨴","∗":"∗","◊":"◊","(":"(","⦓":"⦓","⥭":"⥭","‎":"‎","⊿":"⊿","‹":"‹","𝓁":"𝓁","⪍":"⪍","⪏":"⪏","‚":"‚","ł":"ł","⪦":"⪦","⩹":"⩹","⋉":"⋉","⥶":"⥶","⩻":"⩻","⦖":"⦖","◃":"◃","⥊":"⥊","⥦":"⥦","≨︀":"≨︀","∺":"∺","¯":"¯","♂":"♂","✠":"✠","▮":"▮","⨩":"⨩","м":"м","—":"—","𝔪":"𝔪","℧":"℧","µ":"µ","⫰":"⫰","−":"−","⨪":"⨪","⫛":"⫛","⊧":"⊧","𝕞":"𝕞","𝓂":"𝓂","μ":"μ","⊸":"⊸","⋙̸":"⋙̸","≫⃒":"≫⃒","⇍":"⇍","⇎":"⇎","⋘̸":"⋘̸","≪⃒":"≪⃒","⇏":"⇏","⊯":"⊯","⊮":"⊮","ń":"ń","∠⃒":"∠⃒","⩰̸":"⩰̸","≋̸":"≋̸","ʼn":"ʼn","♮":"♮","⩃":"⩃","ň":"ň","ņ":"ņ","⩭̸":"⩭̸","⩂":"⩂","н":"н","–":"–","⇗":"⇗","⤤":"⤤","≐̸":"≐̸","⤨":"⤨","𝔫":"𝔫","↮":"↮","⫲":"⫲","⋼":"⋼","⋺":"⋺","њ":"њ","≦̸":"≦̸","↚":"↚","‥":"‥","𝕟":"𝕟","¬":"¬","⋹̸":"⋹̸","⋵̸":"⋵̸","⋷":"⋷","⋶":"⋶","⋾":"⋾","⋽":"⋽","⫽⃥":"⫽⃥","∂̸":"∂̸","⨔":"⨔","↛":"↛","⤳̸":"⤳̸","↝̸":"↝̸","𝓃":"𝓃","⊄":"⊄","⫅̸":"⫅̸","⊅":"⊅","⫆̸":"⫆̸","ñ":"ñ","ν":"ν","#":"#","№":"№"," ":" ","⊭":"⊭","⤄":"⤄","≍⃒":"≍⃒","⊬":"⊬","≥⃒":"≥⃒",">⃒":">⃒","⧞":"⧞","⤂":"⤂","≤⃒":"≤⃒","<⃒":"<⃒","⊴⃒":"⊴⃒","⤃":"⤃","⊵⃒":"⊵⃒","∼⃒":"∼⃒","⇖":"⇖","⤣":"⤣","⤧":"⤧","ó":"ó","ô":"ô","о":"о","ő":"ő","⨸":"⨸","⦼":"⦼","œ":"œ","⦿":"⦿","𝔬":"𝔬","˛":"˛","ò":"ò","⧁":"⧁","⦵":"⦵","⦾":"⦾","⦻":"⦻","⧀":"⧀","ō":"ō","ω":"ω","ο":"ο","⦶":"⦶","𝕠":"𝕠","⦷":"⦷","⦹":"⦹","∨":"∨","⩝":"⩝","ℴ":"ℴ","ª":"ª","º":"º","⊶":"⊶","⩖":"⩖","⩗":"⩗","⩛":"⩛","ø":"ø","⊘":"⊘","õ":"õ","⨶":"⨶","ö":"ö","⌽":"⌽","¶":"¶","⫳":"⫳","⫽":"⫽","п":"п","%":"%",".":".","‰":"‰","‱":"‱","𝔭":"𝔭","φ":"φ","ϕ":"ϕ","☎":"☎","π":"π","ϖ":"ϖ","ℎ":"ℎ","+":"+","⨣":"⨣","⨢":"⨢","⨥":"⨥","⩲":"⩲","⨦":"⨦","⨧":"⨧","⨕":"⨕","𝕡":"𝕡","£":"£","⪳":"⪳","⪷":"⪷","⪹":"⪹","⪵":"⪵","⋨":"⋨","′":"′","⌮":"⌮","⌒":"⌒","⌓":"⌓","⊰":"⊰","𝓅":"𝓅","ψ":"ψ"," ":" ","𝔮":"𝔮","𝕢":"𝕢","⁗":"⁗","𝓆":"𝓆","⨖":"⨖","?":"?","⤜":"⤜","⥤":"⥤","∽̱":"∽̱","ŕ":"ŕ","⦳":"⦳","⦒":"⦒","⦥":"⦥","»":"»","⥵":"⥵","⤠":"⤠","⤳":"⤳","⤞":"⤞","⥅":"⥅","⥴":"⥴","↣":"↣","↝":"↝","⤚":"⤚","∶":"∶","❳":"❳","}":"}","]":"]","⦌":"⦌","⦎":"⦎","⦐":"⦐","ř":"ř","ŗ":"ŗ","р":"р","⤷":"⤷","⥩":"⥩","↳":"↳","▭":"▭","⥽":"⥽","𝔯":"𝔯","⥬":"⥬","ρ":"ρ","ϱ":"ϱ","⇉":"⇉","⋌":"⋌","˚":"˚","‏":"‏","⎱":"⎱","⫮":"⫮","⟭":"⟭","⇾":"⇾","⦆":"⦆","𝕣":"𝕣","⨮":"⨮","⨵":"⨵",")":")","⦔":"⦔","⨒":"⨒","›":"›","𝓇":"𝓇","⋊":"⋊","▹":"▹","⧎":"⧎","⥨":"⥨","℞":"℞","ś":"ś","⪴":"⪴","⪸":"⪸","š":"š","ş":"ş","ŝ":"ŝ","⪶":"⪶","⪺":"⪺","⋩":"⋩","⨓":"⨓","с":"с","⋅":"⋅","⩦":"⩦","⇘":"⇘","§":"§",";":";","⤩":"⤩","✶":"✶","𝔰":"𝔰","♯":"♯","щ":"щ","ш":"ш","­":"­","σ":"σ","ς":"ς","⩪":"⩪","⪞":"⪞","⪠":"⪠","⪝":"⪝","⪟":"⪟","≆":"≆","⨤":"⨤","⥲":"⥲","⨳":"⨳","⧤":"⧤","⌣":"⌣","⪪":"⪪","⪬":"⪬","⪬︀":"⪬︀","ь":"ь","/":"/","⧄":"⧄","⌿":"⌿","𝕤":"𝕤","♠":"♠","⊓︀":"⊓︀","⊔︀":"⊔︀","𝓈":"𝓈","☆":"☆","⊂":"⊂","⫅":"⫅","⪽":"⪽","⫃":"⫃","⫁":"⫁","⫋":"⫋","⊊":"⊊","⪿":"⪿","⥹":"⥹","⫇":"⫇","⫕":"⫕","⫓":"⫓","♪":"♪","¹":"¹","²":"²","³":"³","⫆":"⫆","⪾":"⪾","⫘":"⫘","⫄":"⫄","⟉":"⟉","⫗":"⫗","⥻":"⥻","⫂":"⫂","⫌":"⫌","⊋":"⊋","⫀":"⫀","⫈":"⫈","⫔":"⫔","⫖":"⫖","⇙":"⇙","⤪":"⤪","ß":"ß","⌖":"⌖","τ":"τ","ť":"ť","ţ":"ţ","т":"т","⌕":"⌕","𝔱":"𝔱","θ":"θ","ϑ":"ϑ","þ":"þ","×":"×","⨱":"⨱","⨰":"⨰","⌶":"⌶","⫱":"⫱","𝕥":"𝕥","⫚":"⫚","‴":"‴","▵":"▵","≜":"≜","◬":"◬","⨺":"⨺","⨹":"⨹","⧍":"⧍","⨻":"⨻","⏢":"⏢","𝓉":"𝓉","ц":"ц","ћ":"ћ","ŧ":"ŧ","⥣":"⥣","ú":"ú","ў":"ў","ŭ":"ŭ","û":"û","у":"у","ű":"ű","⥾":"⥾","𝔲":"𝔲","ù":"ù","▀":"▀","⌜":"⌜","⌏":"⌏","◸":"◸","ū":"ū","ų":"ų","𝕦":"𝕦","υ":"υ","⇈":"⇈","⌝":"⌝","⌎":"⌎","ů":"ů","◹":"◹","𝓊":"𝓊","⋰":"⋰","ũ":"ũ","ü":"ü","⦧":"⦧","⫨":"⫨","⫩":"⫩","⦜":"⦜","⊊︀":"⊊︀","⫋︀":"⫋︀","⊋︀":"⊋︀","⫌︀":"⫌︀","в":"в","⊻":"⊻","≚":"≚","⋮":"⋮","𝔳":"𝔳","𝕧":"𝕧","𝓋":"𝓋","⦚":"⦚","ŵ":"ŵ","⩟":"⩟","≙":"≙","℘":"℘","𝔴":"𝔴","𝕨":"𝕨","𝓌":"𝓌","𝔵":"𝔵","ξ":"ξ","⋻":"⋻","𝕩":"𝕩","𝓍":"𝓍","ý":"ý","я":"я","ŷ":"ŷ","ы":"ы","¥":"¥","𝔶":"𝔶","ї":"ї","𝕪":"𝕪","𝓎":"𝓎","ю":"ю","ÿ":"ÿ","ź":"ź","ž":"ž","з":"з","ż":"ż","ζ":"ζ","𝔷":"𝔷","ж":"ж","⇝":"⇝","𝕫":"𝕫","𝓏":"𝓏","‍":"‍","‌":"‌"}}}; + +/***/ }), + +/***/ "./node_modules/html-entities/lib/numeric-unicode-map.js": +/*!***************************************************************!*\ + !*** ./node_modules/html-entities/lib/numeric-unicode-map.js ***! + \***************************************************************/ +/***/ (function(__unused_webpack_module, exports) { + +"use strict"; +Object.defineProperty(exports, "__esModule", ({value:true}));exports.numericUnicodeMap={0:65533,128:8364,130:8218,131:402,132:8222,133:8230,134:8224,135:8225,136:710,137:8240,138:352,139:8249,140:338,142:381,145:8216,146:8217,147:8220,148:8221,149:8226,150:8211,151:8212,152:732,153:8482,154:353,155:8250,156:339,158:382,159:376}; + +/***/ }), + +/***/ "./node_modules/html-entities/lib/surrogate-pairs.js": +/*!***********************************************************!*\ + !*** ./node_modules/html-entities/lib/surrogate-pairs.js ***! + \***********************************************************/ +/***/ (function(__unused_webpack_module, exports) { + +"use strict"; +Object.defineProperty(exports, "__esModule", ({value:true}));exports.fromCodePoint=String.fromCodePoint||function(astralCodePoint){return String.fromCharCode(Math.floor((astralCodePoint-65536)/1024)+55296,(astralCodePoint-65536)%1024+56320)};exports.getCodePoint=String.prototype.codePointAt?function(input,position){return input.codePointAt(position)}:function(input,position){return(input.charCodeAt(position)-55296)*1024+input.charCodeAt(position+1)-56320+65536};exports.highSurrogateFrom=55296;exports.highSurrogateTo=56319; + +/***/ }), + +/***/ "./node_modules/i18n-js/app/assets/javascripts/i18n.js": +/*!*************************************************************!*\ + !*** ./node_modules/i18n-js/app/assets/javascripts/i18n.js ***! + \*************************************************************/ +/***/ (function(module, exports, __webpack_require__) { + +var __WEBPACK_AMD_DEFINE_RESULT__;// I18n.js +// ======= +// +// This small library provides the Rails I18n API on the Javascript. +// You don't actually have to use Rails (or even Ruby) to use I18n.js. +// Just make sure you export all translations in an object like this: +// +// I18n.translations.en = { +// hello: "Hello World" +// }; +// +// See tests for specific formatting like numbers and dates. +// + +// Using UMD pattern from +// https://github.com/umdjs/umd#regular-module +// `returnExports.js` version +;(function (root, factory) { + if (true) { + // AMD. Register as an anonymous module. + !(__WEBPACK_AMD_DEFINE_RESULT__ = (function(){ return factory(root);}).call(exports, __webpack_require__, exports, module), + __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else {} +}(this, function(global) { + "use strict"; + + // Use previously defined object if exists in current scope + var I18n = global && global.I18n || {}; + + // Just cache the Array#slice function. + var slice = Array.prototype.slice; + + // Apply number padding. + var padding = function(number) { + return ("0" + number.toString()).substr(-2); + }; + + // Improved toFixed number rounding function with support for unprecise floating points + // JavaScript's standard toFixed function does not round certain numbers correctly (for example 0.105 with precision 2). + var toFixed = function(number, precision) { + return decimalAdjust('round', number, -precision).toFixed(precision); + }; + + // Is a given variable an object? + // Borrowed from Underscore.js + var isObject = function(obj) { + var type = typeof obj; + return type === 'function' || type === 'object' + }; + + var isFunction = function(func) { + var type = typeof func; + return type === 'function' + }; + + // Check if value is different than undefined and null; + var isSet = function(value) { + return typeof(value) !== 'undefined' && value !== null; + }; + + // Is a given value an array? + // Borrowed from Underscore.js + var isArray = function(val) { + if (Array.isArray) { + return Array.isArray(val); + } + return Object.prototype.toString.call(val) === '[object Array]'; + }; + + var isString = function(val) { + return typeof val === 'string' || Object.prototype.toString.call(val) === '[object String]'; + }; + + var isNumber = function(val) { + return typeof val === 'number' || Object.prototype.toString.call(val) === '[object Number]'; + }; + + var isBoolean = function(val) { + return val === true || val === false; + }; + + var isNull = function(val) { + return val === null; + }; + + var decimalAdjust = function(type, value, exp) { + // If the exp is undefined or zero... + if (typeof exp === 'undefined' || +exp === 0) { + return Math[type](value); + } + value = +value; + exp = +exp; + // If the value is not a number or the exp is not an integer... + if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) { + return NaN; + } + // Shift + value = value.toString().split('e'); + value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp))); + // Shift back + value = value.toString().split('e'); + return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)); + }; + + var lazyEvaluate = function(message, scope) { + if (isFunction(message)) { + return message(scope); + } else { + return message; + } + }; + + var merge = function (dest, obj) { + var key, value; + for (key in obj) if (obj.hasOwnProperty(key)) { + value = obj[key]; + if (isString(value) || isNumber(value) || isBoolean(value) || isArray(value) || isNull(value)) { + dest[key] = value; + } else { + if (dest[key] == null) dest[key] = {}; + merge(dest[key], value); + } + } + return dest; + }; + + // Set default days/months translations. + var DATE = { + day_names: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] + , abbr_day_names: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] + , month_names: [null, "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] + , abbr_month_names: [null, "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + , meridian: ["AM", "PM"] + }; + + // Set default number format. + var NUMBER_FORMAT = { + precision: 3 + , separator: "." + , delimiter: "," + , strip_insignificant_zeros: false + }; + + // Set default currency format. + var CURRENCY_FORMAT = { + unit: "$" + , precision: 2 + , format: "%u%n" + , sign_first: true + , delimiter: "," + , separator: "." + }; + + // Set default percentage format. + var PERCENTAGE_FORMAT = { + unit: "%" + , precision: 3 + , format: "%n%u" + , separator: "." + , delimiter: "" + }; + + // Set default size units. + var SIZE_UNITS = [null, "kb", "mb", "gb", "tb"]; + + // Other default options + var DEFAULT_OPTIONS = { + // Set default locale. This locale will be used when fallback is enabled and + // the translation doesn't exist in a particular locale. + defaultLocale: "en" + // Set the current locale to `en`. + , locale: "en" + // Set the translation key separator. + , defaultSeparator: "." + // Set the placeholder format. Accepts `{{placeholder}}` and `%{placeholder}`. + , placeholder: /(?:\{\{|%\{)(.*?)(?:\}\}?)/gm + // Set if engine should fallback to the default locale when a translation + // is missing. + , fallbacks: false + // Set the default translation object. + , translations: {} + // Set missing translation behavior. 'message' will display a message + // that the translation is missing, 'guess' will try to guess the string + , missingBehaviour: 'message' + // if you use missingBehaviour with 'message', but want to know that the + // string is actually missing for testing purposes, you can prefix the + // guessed string by setting the value here. By default, no prefix! + , missingTranslationPrefix: '' + }; + + // Set default locale. This locale will be used when fallback is enabled and + // the translation doesn't exist in a particular locale. + I18n.reset = function() { + var key; + for (key in DEFAULT_OPTIONS) { + this[key] = DEFAULT_OPTIONS[key]; + } + }; + + // Much like `reset`, but only assign options if not already assigned + I18n.initializeOptions = function() { + var key; + for (key in DEFAULT_OPTIONS) if (!isSet(this[key])) { + this[key] = DEFAULT_OPTIONS[key]; + } + }; + I18n.initializeOptions(); + + // Return a list of all locales that must be tried before returning the + // missing translation message. By default, this will consider the inline option, + // current locale and fallback locale. + // + // I18n.locales.get("de-DE"); + // // ["de-DE", "de", "en"] + // + // You can define custom rules for any locale. Just make sure you return a array + // containing all locales. + // + // // Default the Wookie locale to English. + // I18n.locales["wk"] = function(locale) { + // return ["en"]; + // }; + // + I18n.locales = {}; + + // Retrieve locales based on inline locale, current locale or default to + // I18n's detection. + I18n.locales.get = function(locale) { + var result = this[locale] || this[I18n.locale] || this["default"]; + + if (isFunction(result)) { + result = result(locale); + } + + if (isArray(result) === false) { + result = [result]; + } + + return result; + }; + + // The default locale list. + I18n.locales["default"] = function(locale) { + var locales = [] + , list = [] + ; + + // Handle the inline locale option that can be provided to + // the `I18n.t` options. + if (locale) { + locales.push(locale); + } + + // Add the current locale to the list. + if (!locale && I18n.locale) { + locales.push(I18n.locale); + } + + // Add the default locale if fallback strategy is enabled. + if (I18n.fallbacks && I18n.defaultLocale) { + locales.push(I18n.defaultLocale); + } + + // Locale code format 1: + // According to RFC4646 (http://www.ietf.org/rfc/rfc4646.txt) + // language codes for Traditional Chinese should be `zh-Hant` + // + // But due to backward compatibility + // We use older version of IETF language tag + // @see http://www.w3.org/TR/html401/struct/dirlang.html + // @see http://en.wikipedia.org/wiki/IETF_language_tag + // + // Format: `language-code = primary-code ( "-" subcode )*` + // + // primary-code uses ISO639-1 + // @see http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes + // @see http://www.iso.org/iso/home/standards/language_codes.htm + // + // subcode uses ISO 3166-1 alpha-2 + // @see http://en.wikipedia.org/wiki/ISO_3166 + // @see http://www.iso.org/iso/country_codes.htm + // + // @note + // subcode can be in upper case or lower case + // defining it in upper case is a convention only + + + // Locale code format 2: + // Format: `code = primary-code ( "-" region-code )*` + // primary-code uses ISO 639-1 + // script-code uses ISO 15924 + // region-code uses ISO 3166-1 alpha-2 + // Example: zh-Hant-TW, en-HK, zh-Hant-CN + // + // It is similar to RFC4646 (or actually the same), + // but seems to be limited to language, script, region + + // Compute each locale with its country code. + // So this will return an array containing + // `de-DE` and `de` + // or + // `zh-hans-tw`, `zh-hans`, `zh` + // locales. + locales.forEach(function(locale) { + var localeParts = locale.split("-"); + var firstFallback = null; + var secondFallback = null; + if (localeParts.length === 3) { + firstFallback = [ + localeParts[0], + localeParts[1] + ].join("-"); + secondFallback = localeParts[0]; + } + else if (localeParts.length === 2) { + firstFallback = localeParts[0]; + } + + if (list.indexOf(locale) === -1) { + list.push(locale); + } + + if (! I18n.fallbacks) { + return; + } + + [ + firstFallback, + secondFallback + ].forEach(function(nullableFallbackLocale) { + // We don't want null values + if (typeof nullableFallbackLocale === "undefined") { return; } + if (nullableFallbackLocale === null) { return; } + // We don't want duplicate values + // + // Comparing with `locale` first is faster than + // checking whether value's presence in the list + if (nullableFallbackLocale === locale) { return; } + if (list.indexOf(nullableFallbackLocale) !== -1) { return; } + + list.push(nullableFallbackLocale); + }); + }); + + // No locales set? English it is. + if (!locales.length) { + locales.push("en"); + } + + return list; + }; + + // Hold pluralization rules. + I18n.pluralization = {}; + + // Return the pluralizer for a specific locale. + // If no specify locale is found, then I18n's default will be used. + I18n.pluralization.get = function(locale) { + return this[locale] || this[I18n.locale] || this["default"]; + }; + + // The default pluralizer rule. + // It detects the `zero`, `one`, and `other` scopes. + I18n.pluralization["default"] = function(count) { + switch (count) { + case 0: return ["zero", "other"]; + case 1: return ["one"]; + default: return ["other"]; + } + }; + + // Return current locale. If no locale has been set, then + // the current locale will be the default locale. + I18n.currentLocale = function() { + return this.locale || this.defaultLocale; + }; + + // Check if value is different than undefined and null; + I18n.isSet = isSet; + + // Find and process the translation using the provided scope and options. + // This is used internally by some functions and should not be used as an + // public API. + I18n.lookup = function(scope, options) { + options = options || {}; + + var locales = this.locales.get(options.locale).slice() + , locale + , scopes + , fullScope + , translations + ; + + fullScope = this.getFullScope(scope, options); + + while (locales.length) { + locale = locales.shift(); + scopes = fullScope.split(options.separator || this.defaultSeparator); + translations = this.translations[locale]; + + if (!translations) { + continue; + } + while (scopes.length) { + translations = translations[scopes.shift()]; + + if (translations === undefined || translations === null) { + break; + } + } + + if (translations !== undefined && translations !== null) { + return translations; + } + } + + if (isSet(options.defaultValue)) { + return lazyEvaluate(options.defaultValue, scope); + } + }; + + // lookup pluralization rule key into translations + I18n.pluralizationLookupWithoutFallback = function(count, locale, translations) { + var pluralizer = this.pluralization.get(locale) + , pluralizerKeys = pluralizer(count) + , pluralizerKey + , message; + + if (isObject(translations)) { + while (pluralizerKeys.length) { + pluralizerKey = pluralizerKeys.shift(); + if (isSet(translations[pluralizerKey])) { + message = translations[pluralizerKey]; + break; + } + } + } + + return message; + }; + + // Lookup dedicated to pluralization + I18n.pluralizationLookup = function(count, scope, options) { + options = options || {}; + var locales = this.locales.get(options.locale).slice() + , locale + , scopes + , translations + , message + ; + scope = this.getFullScope(scope, options); + + while (locales.length) { + locale = locales.shift(); + scopes = scope.split(options.separator || this.defaultSeparator); + translations = this.translations[locale]; + + if (!translations) { + continue; + } + + while (scopes.length) { + translations = translations[scopes.shift()]; + if (!isObject(translations)) { + break; + } + if (scopes.length === 0) { + message = this.pluralizationLookupWithoutFallback(count, locale, translations); + } + } + if (typeof message !== "undefined" && message !== null) { + break; + } + } + + if (typeof message === "undefined" || message === null) { + if (isSet(options.defaultValue)) { + if (isObject(options.defaultValue)) { + message = this.pluralizationLookupWithoutFallback(count, options.locale, options.defaultValue); + } else { + message = options.defaultValue; + } + translations = options.defaultValue; + } + } + + return { message: message, translations: translations }; + }; + + // Rails changed the way the meridian is stored. + // It started with `date.meridian` returning an array, + // then it switched to `time.am` and `time.pm`. + // This function abstracts this difference and returns + // the correct meridian or the default value when none is provided. + I18n.meridian = function() { + var time = this.lookup("time"); + var date = this.lookup("date"); + + if (time && time.am && time.pm) { + return [time.am, time.pm]; + } else if (date && date.meridian) { + return date.meridian; + } else { + return DATE.meridian; + } + }; + + // Merge serveral hash options, checking if value is set before + // overwriting any value. The precedence is from left to right. + // + // I18n.prepareOptions({name: "John Doe"}, {name: "Mary Doe", role: "user"}); + // #=> {name: "John Doe", role: "user"} + // + I18n.prepareOptions = function() { + var args = slice.call(arguments) + , options = {} + , subject + ; + + while (args.length) { + subject = args.shift(); + + if (typeof(subject) != "object") { + continue; + } + + for (var attr in subject) { + if (!subject.hasOwnProperty(attr)) { + continue; + } + + if (isSet(options[attr])) { + continue; + } + + options[attr] = subject[attr]; + } + } + + return options; + }; + + // Generate a list of translation options for default fallbacks. + // `defaultValue` is also deleted from options as it is returned as part of + // the translationOptions array. + I18n.createTranslationOptions = function(scope, options) { + var translationOptions = [{scope: scope}]; + + // Defaults should be an array of hashes containing either + // fallback scopes or messages + if (isSet(options.defaults)) { + translationOptions = translationOptions.concat(options.defaults); + } + + // Maintain support for defaultValue. Since it is always a message + // insert it in to the translation options as such. + if (isSet(options.defaultValue)) { + translationOptions.push({ message: options.defaultValue }); + } + + return translationOptions; + }; + + // Translate the given scope with the provided options. + I18n.translate = function(scope, options) { + options = options || {}; + + var translationOptions = this.createTranslationOptions(scope, options); + + var translation; + var usedScope = scope; + + var optionsWithoutDefault = this.prepareOptions(options) + delete optionsWithoutDefault.defaultValue + + // Iterate through the translation options until a translation + // or message is found. + var translationFound = + translationOptions.some(function(translationOption) { + if (isSet(translationOption.scope)) { + usedScope = translationOption.scope; + translation = this.lookup(usedScope, optionsWithoutDefault); + } else if (isSet(translationOption.message)) { + translation = lazyEvaluate(translationOption.message, scope); + } + + if (translation !== undefined && translation !== null) { + return true; + } + }, this); + + if (!translationFound) { + return this.missingTranslation(scope, options); + } + + if (typeof(translation) === "string") { + translation = this.interpolate(translation, options); + } else if (isArray(translation)) { + translation = translation.map(function(t) { + return (typeof(t) === "string" ? this.interpolate(t, options) : t); + }, this); + } else if (isObject(translation) && isSet(options.count)) { + translation = this.pluralize(options.count, usedScope, options); + } + + return translation; + }; + + // This function interpolates the all variables in the given message. + I18n.interpolate = function(message, options) { + if (message == null) { + return message; + } + + options = options || {}; + var matches = message.match(this.placeholder) + , placeholder + , value + , name + , regex + ; + + if (!matches) { + return message; + } + + while (matches.length) { + placeholder = matches.shift(); + name = placeholder.replace(this.placeholder, "$1"); + + if (isSet(options[name])) { + value = options[name].toString().replace(/\$/gm, "_#$#_"); + } else if (name in options) { + value = this.nullPlaceholder(placeholder, message, options); + } else { + value = this.missingPlaceholder(placeholder, message, options); + } + + regex = new RegExp(placeholder.replace(/{/gm, "\\{").replace(/}/gm, "\\}")); + message = message.replace(regex, value); + } + + return message.replace(/_#\$#_/g, "$"); + }; + + // Pluralize the given scope using the `count` value. + // The pluralized translation may have other placeholders, + // which will be retrieved from `options`. + I18n.pluralize = function(count, scope, options) { + options = this.prepareOptions({count: String(count)}, options) + var pluralizer, result; + + result = this.pluralizationLookup(count, scope, options); + if (typeof result.translations === "undefined" || result.translations == null) { + return this.missingTranslation(scope, options); + } + + if (typeof result.message !== "undefined" && result.message != null) { + return this.interpolate(result.message, options); + } + else { + pluralizer = this.pluralization.get(options.locale); + return this.missingTranslation(scope + '.' + pluralizer(count)[0], options); + } + }; + + // Return a missing translation message for the given parameters. + I18n.missingTranslation = function(scope, options) { + //guess intended string + if(this.missingBehaviour === 'guess'){ + //get only the last portion of the scope + var s = scope.split('.').slice(-1)[0]; + //replace underscore with space && camelcase with space and lowercase letter + return (this.missingTranslationPrefix.length > 0 ? this.missingTranslationPrefix : '') + + s.replace(/_/g,' ').replace(/([a-z])([A-Z])/g, + function(match, p1, p2) {return p1 + ' ' + p2.toLowerCase()} ); + } + + var localeForTranslation = (options != null && options.locale != null) ? options.locale : this.currentLocale(); + var fullScope = this.getFullScope(scope, options); + var fullScopeWithLocale = [localeForTranslation, fullScope].join(options.separator || this.defaultSeparator); + + return '[missing "' + fullScopeWithLocale + '" translation]'; + }; + + // Return a missing placeholder message for given parameters + I18n.missingPlaceholder = function(placeholder, message, options) { + return "[missing " + placeholder + " value]"; + }; + + I18n.nullPlaceholder = function() { + return I18n.missingPlaceholder.apply(I18n, arguments); + }; + + // Format number using localization rules. + // The options will be retrieved from the `number.format` scope. + // If this isn't present, then the following options will be used: + // + // - `precision`: `3` + // - `separator`: `"."` + // - `delimiter`: `","` + // - `strip_insignificant_zeros`: `false` + // + // You can also override these options by providing the `options` argument. + // + I18n.toNumber = function(number, options) { + options = this.prepareOptions( + options + , this.lookup("number.format") + , NUMBER_FORMAT + ); + + var negative = number < 0 + , string = toFixed(Math.abs(number), options.precision).toString() + , parts = string.split(".") + , precision + , buffer = [] + , formattedNumber + , format = options.format || "%n" + , sign = negative ? "-" : "" + ; + + number = parts[0]; + precision = parts[1]; + + while (number.length > 0) { + buffer.unshift(number.substr(Math.max(0, number.length - 3), 3)); + number = number.substr(0, number.length -3); + } + + formattedNumber = buffer.join(options.delimiter); + + if (options.strip_insignificant_zeros && precision) { + precision = precision.replace(/0+$/, ""); + } + + if (options.precision > 0 && precision) { + formattedNumber += options.separator + precision; + } + + if (options.sign_first) { + format = "%s" + format; + } + else { + format = format.replace("%n", "%s%n"); + } + + formattedNumber = format + .replace("%u", options.unit) + .replace("%n", formattedNumber) + .replace("%s", sign) + ; + + return formattedNumber; + }; + + // Format currency with localization rules. + // The options will be retrieved from the `number.currency.format` and + // `number.format` scopes, in that order. + // + // Any missing option will be retrieved from the `I18n.toNumber` defaults and + // the following options: + // + // - `unit`: `"$"` + // - `precision`: `2` + // - `format`: `"%u%n"` + // - `delimiter`: `","` + // - `separator`: `"."` + // + // You can also override these options by providing the `options` argument. + // + I18n.toCurrency = function(number, options) { + options = this.prepareOptions( + options + , this.lookup("number.currency.format", options) + , this.lookup("number.format", options) + , CURRENCY_FORMAT + ); + + return this.toNumber(number, options); + }; + + // Localize several values. + // You can provide the following scopes: `currency`, `number`, or `percentage`. + // If you provide a scope that matches the `/^(date|time)/` regular expression + // then the `value` will be converted by using the `I18n.toTime` function. + // + // It will default to the value's `toString` function. + // + I18n.localize = function(scope, value, options) { + options || (options = {}); + + switch (scope) { + case "currency": + return this.toCurrency(value, options); + case "number": + scope = this.lookup("number.format", options); + return this.toNumber(value, scope); + case "percentage": + return this.toPercentage(value, options); + default: + var localizedValue; + + if (scope.match(/^(date|time)/)) { + localizedValue = this.toTime(scope, value, options); + } else { + localizedValue = value.toString(); + } + + return this.interpolate(localizedValue, options); + } + }; + + // Parse a given `date` string into a JavaScript Date object. + // This function is time zone aware. + // + // The following string formats are recognized: + // + // yyyy-mm-dd + // yyyy-mm-dd[ T]hh:mm::ss + // yyyy-mm-dd[ T]hh:mm::ss + // yyyy-mm-dd[ T]hh:mm::ssZ + // yyyy-mm-dd[ T]hh:mm::ss+0000 + // yyyy-mm-dd[ T]hh:mm::ss+00:00 + // yyyy-mm-dd[ T]hh:mm::ss.123Z + // + I18n.parseDate = function(date) { + var matches, convertedDate, fraction; + // A date input of `null` or `undefined` will be returned as-is + if (date == null) { + return date; + } + // we have a date, so just return it. + if (typeof(date) === "object") { + return date; + } + + matches = date.toString().match(/(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2}):(\d{2})([\.,]\d{1,3})?)?(Z|\+00:?00)?/); + + if (matches) { + for (var i = 1; i <= 6; i++) { + matches[i] = parseInt(matches[i], 10) || 0; + } + + // month starts on 0 + matches[2] -= 1; + + fraction = matches[7] ? 1000 * ("0" + matches[7]) : null; + + if (matches[8]) { + convertedDate = new Date(Date.UTC(matches[1], matches[2], matches[3], matches[4], matches[5], matches[6], fraction)); + } else { + convertedDate = new Date(matches[1], matches[2], matches[3], matches[4], matches[5], matches[6], fraction); + } + } else if (typeof(date) == "number") { + // UNIX timestamp + convertedDate = new Date(); + convertedDate.setTime(date); + } else if (date.match(/([A-Z][a-z]{2}) ([A-Z][a-z]{2}) (\d+) (\d+:\d+:\d+) ([+-]\d+) (\d+)/)) { + // This format `Wed Jul 20 13:03:39 +0000 2011` is parsed by + // webkit/firefox, but not by IE, so we must parse it manually. + convertedDate = new Date(); + convertedDate.setTime(Date.parse([ + RegExp.$1, RegExp.$2, RegExp.$3, RegExp.$6, RegExp.$4, RegExp.$5 + ].join(" "))); + } else if (date.match(/\d+ \d+:\d+:\d+ [+-]\d+ \d+/)) { + // a valid javascript format with timezone info + convertedDate = new Date(); + convertedDate.setTime(Date.parse(date)); + } else { + // an arbitrary javascript string + convertedDate = new Date(); + convertedDate.setTime(Date.parse(date)); + } + + return convertedDate; + }; + + // Formats time according to the directives in the given format string. + // The directives begins with a percent (%) character. Any text not listed as a + // directive will be passed through to the output string. + // + // The accepted formats are: + // + // %a - The abbreviated weekday name (Sun) + // %A - The full weekday name (Sunday) + // %b - The abbreviated month name (Jan) + // %B - The full month name (January) + // %c - The preferred local date and time representation + // %d - Day of the month (01..31) + // %-d - Day of the month (1..31) + // %H - Hour of the day, 24-hour clock (00..23) + // %-H/%k - Hour of the day, 24-hour clock (0..23) + // %I - Hour of the day, 12-hour clock (01..12) + // %-I/%l - Hour of the day, 12-hour clock (1..12) + // %m - Month of the year (01..12) + // %-m - Month of the year (1..12) + // %M - Minute of the hour (00..59) + // %-M - Minute of the hour (0..59) + // %p - Meridian indicator (AM or PM) + // %P - Meridian indicator (am or pm) + // %S - Second of the minute (00..60) + // %-S - Second of the minute (0..60) + // %w - Day of the week (Sunday is 0, 0..6) + // %y - Year without a century (00..99) + // %-y - Year without a century (0..99) + // %Y - Year with century + // %z/%Z - Timezone offset (+0545) + // + I18n.strftime = function(date, format, options) { + var options = this.lookup("date", options) + , meridianOptions = I18n.meridian() + ; + + if (!options) { + options = {}; + } + + options = this.prepareOptions(options, DATE); + + if (isNaN(date.getTime())) { + throw new Error('I18n.strftime() requires a valid date object, but received an invalid date.'); + } + + var weekDay = date.getDay() + , day = date.getDate() + , year = date.getFullYear() + , month = date.getMonth() + 1 + , hour = date.getHours() + , hour12 = hour + , meridian = hour > 11 ? 1 : 0 + , secs = date.getSeconds() + , mins = date.getMinutes() + , offset = date.getTimezoneOffset() + , absOffsetHours = Math.floor(Math.abs(offset / 60)) + , absOffsetMinutes = Math.abs(offset) - (absOffsetHours * 60) + , timezoneoffset = (offset > 0 ? "-" : "+") + + (absOffsetHours.toString().length < 2 ? "0" + absOffsetHours : absOffsetHours) + + (absOffsetMinutes.toString().length < 2 ? "0" + absOffsetMinutes : absOffsetMinutes) + ; + + if (hour12 > 12) { + hour12 = hour12 - 12; + } else if (hour12 === 0) { + hour12 = 12; + } + + format = format.replace("%a", options.abbr_day_names[weekDay]); + format = format.replace("%A", options.day_names[weekDay]); + format = format.replace("%b", options.abbr_month_names[month]); + format = format.replace("%B", options.month_names[month]); + format = format.replace("%d", padding(day)); + format = format.replace("%e", day); + format = format.replace("%-d", day); + format = format.replace("%H", padding(hour)); + format = format.replace("%-H", hour); + format = format.replace("%k", hour); + format = format.replace("%I", padding(hour12)); + format = format.replace("%-I", hour12); + format = format.replace("%l", hour12); + format = format.replace("%m", padding(month)); + format = format.replace("%-m", month); + format = format.replace("%M", padding(mins)); + format = format.replace("%-M", mins); + format = format.replace("%p", meridianOptions[meridian]); + format = format.replace("%P", meridianOptions[meridian].toLowerCase()); + format = format.replace("%S", padding(secs)); + format = format.replace("%-S", secs); + format = format.replace("%w", weekDay); + format = format.replace("%y", padding(year)); + format = format.replace("%-y", padding(year).replace(/^0+/, "")); + format = format.replace("%Y", year); + format = format.replace("%z", timezoneoffset); + format = format.replace("%Z", timezoneoffset); + + return format; + }; + + // Convert the given dateString into a formatted date. + I18n.toTime = function(scope, dateString, options) { + var date = this.parseDate(dateString) + , format = this.lookup(scope, options) + ; + + // A date input of `null` or `undefined` will be returned as-is + if (date == null) { + return date; + } + + var date_string = date.toString() + if (date_string.match(/invalid/i)) { + return date_string; + } + + if (!format) { + return date_string; + } + + return this.strftime(date, format, options); + }; + + // Convert a number into a formatted percentage value. + I18n.toPercentage = function(number, options) { + options = this.prepareOptions( + options + , this.lookup("number.percentage.format", options) + , this.lookup("number.format", options) + , PERCENTAGE_FORMAT + ); + + return this.toNumber(number, options); + }; + + // Convert a number into a readable size representation. + I18n.toHumanSize = function(number, options) { + var kb = 1024 + , size = number + , iterations = 0 + , unit + , precision + , fullScope + ; + + while (size >= kb && iterations < 4) { + size = size / kb; + iterations += 1; + } + + if (iterations === 0) { + fullScope = this.getFullScope("number.human.storage_units.units.byte", options); + unit = this.t(fullScope, {count: size}); + precision = 0; + } else { + fullScope = this.getFullScope("number.human.storage_units.units." + SIZE_UNITS[iterations], options); + unit = this.t(fullScope); + precision = (size - Math.floor(size) === 0) ? 0 : 1; + } + + options = this.prepareOptions( + options + , {unit: unit, precision: precision, format: "%n%u", delimiter: ""} + ); + + return this.toNumber(size, options); + }; + + I18n.getFullScope = function(scope, options) { + options = options || {}; + + // Deal with the scope as an array. + if (isArray(scope)) { + scope = scope.join(options.separator || this.defaultSeparator); + } + + // Deal with the scope option provided through the second argument. + // + // I18n.t('hello', {scope: 'greetings'}); + // + if (options.scope) { + scope = [options.scope, scope].join(options.separator || this.defaultSeparator); + } + + return scope; + }; + /** + * Merge obj1 with obj2 (shallow merge), without modifying inputs + * @param {Object} obj1 + * @param {Object} obj2 + * @returns {Object} Merged values of obj1 and obj2 + * + * In order to support ES3, `Object.prototype.hasOwnProperty.call` is used + * Idea is from: + * https://stackoverflow.com/questions/8157700/object-has-no-hasownproperty-method-i-e-its-undefined-ie8 + */ + I18n.extend = function ( obj1, obj2 ) { + if (typeof(obj1) === "undefined" && typeof(obj2) === "undefined") { + return {}; + } + return merge(obj1, obj2); + }; + + // Set aliases, so we can save some typing. + I18n.t = I18n.translate.bind(I18n); + I18n.l = I18n.localize.bind(I18n); + I18n.p = I18n.pluralize.bind(I18n); + + return I18n; +})); + + +/***/ }), + +/***/ "./node_modules/punycode/punycode.js": +/*!*******************************************!*\ + !*** ./node_modules/punycode/punycode.js ***! + \*******************************************/ +/***/ (function(module, exports, __webpack_require__) { + +/* module decorator */ module = __webpack_require__.nmd(module); +var __WEBPACK_AMD_DEFINE_RESULT__;/*! https://mths.be/punycode v1.3.2 by @mathias */ +;(function(root) { + + /** Detect free variables */ + var freeExports = true && exports && + !exports.nodeType && exports; + var freeModule = true && module && + !module.nodeType && module; + var freeGlobal = typeof __webpack_require__.g == 'object' && __webpack_require__.g; + if ( + freeGlobal.global === freeGlobal || + freeGlobal.window === freeGlobal || + freeGlobal.self === freeGlobal + ) { + root = freeGlobal; + } + + /** + * The `punycode` object. + * @name punycode + * @type Object + */ + var punycode, + + /** Highest positive signed 32-bit float value */ + maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1 + + /** Bootstring parameters */ + base = 36, + tMin = 1, + tMax = 26, + skew = 38, + damp = 700, + initialBias = 72, + initialN = 128, // 0x80 + delimiter = '-', // '\x2D' + + /** Regular expressions */ + regexPunycode = /^xn--/, + regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars + regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators + + /** Error messages */ + errors = { + 'overflow': 'Overflow: input needs wider integers to process', + 'not-basic': 'Illegal input >= 0x80 (not a basic code point)', + 'invalid-input': 'Invalid input' + }, + + /** Convenience shortcuts */ + baseMinusTMin = base - tMin, + floor = Math.floor, + stringFromCharCode = String.fromCharCode, + + /** Temporary variable */ + key; + + /*--------------------------------------------------------------------------*/ + + /** + * A generic error utility function. + * @private + * @param {String} type The error type. + * @returns {Error} Throws a `RangeError` with the applicable error message. + */ + function error(type) { + throw RangeError(errors[type]); + } + + /** + * A generic `Array#map` utility function. + * @private + * @param {Array} array The array to iterate over. + * @param {Function} callback The function that gets called for every array + * item. + * @returns {Array} A new array of values returned by the callback function. + */ + function map(array, fn) { + var length = array.length; + var result = []; + while (length--) { + result[length] = fn(array[length]); + } + return result; + } + + /** + * A simple `Array#map`-like wrapper to work with domain name strings or email + * addresses. + * @private + * @param {String} domain The domain name or email address. + * @param {Function} callback The function that gets called for every + * character. + * @returns {Array} A new string of characters returned by the callback + * function. + */ + function mapDomain(string, fn) { + var parts = string.split('@'); + var result = ''; + if (parts.length > 1) { + // In email addresses, only the domain name should be punycoded. Leave + // the local part (i.e. everything up to `@`) intact. + result = parts[0] + '@'; + string = parts[1]; + } + // Avoid `split(regex)` for IE8 compatibility. See #17. + string = string.replace(regexSeparators, '\x2E'); + var labels = string.split('.'); + var encoded = map(labels, fn).join('.'); + return result + encoded; + } + + /** + * Creates an array containing the numeric code points of each Unicode + * character in the string. While JavaScript uses UCS-2 internally, + * this function will convert a pair of surrogate halves (each of which + * UCS-2 exposes as separate characters) into a single code point, + * matching UTF-16. + * @see `punycode.ucs2.encode` + * @see + * @memberOf punycode.ucs2 + * @name decode + * @param {String} string The Unicode input string (UCS-2). + * @returns {Array} The new array of code points. + */ + function ucs2decode(string) { + var output = [], + counter = 0, + length = string.length, + value, + extra; + while (counter < length) { + value = string.charCodeAt(counter++); + if (value >= 0xD800 && value <= 0xDBFF && counter < length) { + // high surrogate, and there is a next character + extra = string.charCodeAt(counter++); + if ((extra & 0xFC00) == 0xDC00) { // low surrogate + output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); + } else { + // unmatched surrogate; only append this code unit, in case the next + // code unit is the high surrogate of a surrogate pair + output.push(value); + counter--; + } + } else { + output.push(value); + } + } + return output; + } + + /** + * Creates a string based on an array of numeric code points. + * @see `punycode.ucs2.decode` + * @memberOf punycode.ucs2 + * @name encode + * @param {Array} codePoints The array of numeric code points. + * @returns {String} The new Unicode string (UCS-2). + */ + function ucs2encode(array) { + return map(array, function(value) { + var output = ''; + if (value > 0xFFFF) { + value -= 0x10000; + output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); + value = 0xDC00 | value & 0x3FF; + } + output += stringFromCharCode(value); + return output; + }).join(''); + } + + /** + * Converts a basic code point into a digit/integer. + * @see `digitToBasic()` + * @private + * @param {Number} codePoint The basic numeric code point value. + * @returns {Number} The numeric value of a basic code point (for use in + * representing integers) in the range `0` to `base - 1`, or `base` if + * the code point does not represent a value. + */ + function basicToDigit(codePoint) { + if (codePoint - 48 < 10) { + return codePoint - 22; + } + if (codePoint - 65 < 26) { + return codePoint - 65; + } + if (codePoint - 97 < 26) { + return codePoint - 97; + } + return base; + } + + /** + * Converts a digit/integer into a basic code point. + * @see `basicToDigit()` + * @private + * @param {Number} digit The numeric value of a basic code point. + * @returns {Number} The basic code point whose value (when used for + * representing integers) is `digit`, which needs to be in the range + * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is + * used; else, the lowercase form is used. The behavior is undefined + * if `flag` is non-zero and `digit` has no uppercase form. + */ + function digitToBasic(digit, flag) { + // 0..25 map to ASCII a..z or A..Z + // 26..35 map to ASCII 0..9 + return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5); + } + + /** + * Bias adaptation function as per section 3.4 of RFC 3492. + * http://tools.ietf.org/html/rfc3492#section-3.4 + * @private + */ + function adapt(delta, numPoints, firstTime) { + var k = 0; + delta = firstTime ? floor(delta / damp) : delta >> 1; + delta += floor(delta / numPoints); + for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) { + delta = floor(delta / baseMinusTMin); + } + return floor(k + (baseMinusTMin + 1) * delta / (delta + skew)); + } + + /** + * Converts a Punycode string of ASCII-only symbols to a string of Unicode + * symbols. + * @memberOf punycode + * @param {String} input The Punycode string of ASCII-only symbols. + * @returns {String} The resulting string of Unicode symbols. + */ + function decode(input) { + // Don't use UCS-2 + var output = [], + inputLength = input.length, + out, + i = 0, + n = initialN, + bias = initialBias, + basic, + j, + index, + oldi, + w, + k, + digit, + t, + /** Cached calculation results */ + baseMinusT; + + // Handle the basic code points: let `basic` be the number of input code + // points before the last delimiter, or `0` if there is none, then copy + // the first basic code points to the output. + + basic = input.lastIndexOf(delimiter); + if (basic < 0) { + basic = 0; + } + + for (j = 0; j < basic; ++j) { + // if it's not a basic code point + if (input.charCodeAt(j) >= 0x80) { + error('not-basic'); + } + output.push(input.charCodeAt(j)); + } + + // Main decoding loop: start just after the last delimiter if any basic code + // points were copied; start at the beginning otherwise. + + for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) { + + // `index` is the index of the next character to be consumed. + // Decode a generalized variable-length integer into `delta`, + // which gets added to `i`. The overflow checking is easier + // if we increase `i` as we go, then subtract off its starting + // value at the end to obtain `delta`. + for (oldi = i, w = 1, k = base; /* no condition */; k += base) { + + if (index >= inputLength) { + error('invalid-input'); + } + + digit = basicToDigit(input.charCodeAt(index++)); + + if (digit >= base || digit > floor((maxInt - i) / w)) { + error('overflow'); + } + + i += digit * w; + t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); + + if (digit < t) { + break; + } + + baseMinusT = base - t; + if (w > floor(maxInt / baseMinusT)) { + error('overflow'); + } + + w *= baseMinusT; + + } + + out = output.length + 1; + bias = adapt(i - oldi, out, oldi == 0); + + // `i` was supposed to wrap around from `out` to `0`, + // incrementing `n` each time, so we'll fix that now: + if (floor(i / out) > maxInt - n) { + error('overflow'); + } + + n += floor(i / out); + i %= out; + + // Insert `n` at position `i` of the output + output.splice(i++, 0, n); + + } + + return ucs2encode(output); + } + + /** + * Converts a string of Unicode symbols (e.g. a domain name label) to a + * Punycode string of ASCII-only symbols. + * @memberOf punycode + * @param {String} input The string of Unicode symbols. + * @returns {String} The resulting Punycode string of ASCII-only symbols. + */ + function encode(input) { + var n, + delta, + handledCPCount, + basicLength, + bias, + j, + m, + q, + k, + t, + currentValue, + output = [], + /** `inputLength` will hold the number of code points in `input`. */ + inputLength, + /** Cached calculation results */ + handledCPCountPlusOne, + baseMinusT, + qMinusT; + + // Convert the input in UCS-2 to Unicode + input = ucs2decode(input); + + // Cache the length + inputLength = input.length; + + // Initialize the state + n = initialN; + delta = 0; + bias = initialBias; + + // Handle the basic code points + for (j = 0; j < inputLength; ++j) { + currentValue = input[j]; + if (currentValue < 0x80) { + output.push(stringFromCharCode(currentValue)); + } + } + + handledCPCount = basicLength = output.length; + + // `handledCPCount` is the number of code points that have been handled; + // `basicLength` is the number of basic code points. + + // Finish the basic string - if it is not empty - with a delimiter + if (basicLength) { + output.push(delimiter); + } + + // Main encoding loop: + while (handledCPCount < inputLength) { + + // All non-basic code points < n have been handled already. Find the next + // larger one: + for (m = maxInt, j = 0; j < inputLength; ++j) { + currentValue = input[j]; + if (currentValue >= n && currentValue < m) { + m = currentValue; + } + } + + // Increase `delta` enough to advance the decoder's state to , + // but guard against overflow + handledCPCountPlusOne = handledCPCount + 1; + if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) { + error('overflow'); + } + + delta += (m - n) * handledCPCountPlusOne; + n = m; + + for (j = 0; j < inputLength; ++j) { + currentValue = input[j]; + + if (currentValue < n && ++delta > maxInt) { + error('overflow'); + } + + if (currentValue == n) { + // Represent delta as a generalized variable-length integer + for (q = delta, k = base; /* no condition */; k += base) { + t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); + if (q < t) { + break; + } + qMinusT = q - t; + baseMinusT = base - t; + output.push( + stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0)) + ); + q = floor(qMinusT / baseMinusT); + } + + output.push(stringFromCharCode(digitToBasic(q, 0))); + bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength); + delta = 0; + ++handledCPCount; + } + } + + ++delta; + ++n; + + } + return output.join(''); + } + + /** + * Converts a Punycode string representing a domain name or an email address + * to Unicode. Only the Punycoded parts of the input will be converted, i.e. + * it doesn't matter if you call it on a string that has already been + * converted to Unicode. + * @memberOf punycode + * @param {String} input The Punycoded domain name or email address to + * convert to Unicode. + * @returns {String} The Unicode representation of the given Punycode + * string. + */ + function toUnicode(input) { + return mapDomain(input, function(string) { + return regexPunycode.test(string) + ? decode(string.slice(4).toLowerCase()) + : string; + }); + } + + /** + * Converts a Unicode string representing a domain name or an email address to + * Punycode. Only the non-ASCII parts of the domain name will be converted, + * i.e. it doesn't matter if you call it with a domain that's already in + * ASCII. + * @memberOf punycode + * @param {String} input The domain name or email address to convert, as a + * Unicode string. + * @returns {String} The Punycode representation of the given domain name or + * email address. + */ + function toASCII(input) { + return mapDomain(input, function(string) { + return regexNonASCII.test(string) + ? 'xn--' + encode(string) + : string; + }); + } + + /*--------------------------------------------------------------------------*/ + + /** Define the public API */ + punycode = { + /** + * A string representing the current Punycode.js version number. + * @memberOf punycode + * @type String + */ + 'version': '1.3.2', + /** + * An object of methods to convert from JavaScript's internal character + * representation (UCS-2) to Unicode code points, and back. + * @see + * @memberOf punycode + * @type Object + */ + 'ucs2': { + 'decode': ucs2decode, + 'encode': ucs2encode + }, + 'decode': decode, + 'encode': encode, + 'toASCII': toASCII, + 'toUnicode': toUnicode + }; + + /** Expose `punycode` */ + // Some AMD build optimizers, like r.js, check for specific condition patterns + // like the following: + if ( + true + ) { + !(__WEBPACK_AMD_DEFINE_RESULT__ = (function() { + return punycode; + }).call(exports, __webpack_require__, exports, module), + __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else {} + +}(this)); + + +/***/ }), + +/***/ "./node_modules/querystring/decode.js": +/*!********************************************!*\ + !*** ./node_modules/querystring/decode.js ***! + \********************************************/ +/***/ (function(module) { + +"use strict"; +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +// If obj.hasOwnProperty has been overridden, then calling +// obj.hasOwnProperty(prop) will break. +// See: https://github.com/joyent/node/issues/1707 +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + +module.exports = function(qs, sep, eq, options) { + sep = sep || '&'; + eq = eq || '='; + var obj = {}; + + if (typeof qs !== 'string' || qs.length === 0) { + return obj; + } + + var regexp = /\+/g; + qs = qs.split(sep); + + var maxKeys = 1000; + if (options && typeof options.maxKeys === 'number') { + maxKeys = options.maxKeys; + } + + var len = qs.length; + // maxKeys <= 0 means that we should not limit keys count + if (maxKeys > 0 && len > maxKeys) { + len = maxKeys; + } + + for (var i = 0; i < len; ++i) { + var x = qs[i].replace(regexp, '%20'), + idx = x.indexOf(eq), + kstr, vstr, k, v; + + if (idx >= 0) { + kstr = x.substr(0, idx); + vstr = x.substr(idx + 1); + } else { + kstr = x; + vstr = ''; + } + + k = decodeURIComponent(kstr); + v = decodeURIComponent(vstr); + + if (!hasOwnProperty(obj, k)) { + obj[k] = v; + } else if (Array.isArray(obj[k])) { + obj[k].push(v); + } else { + obj[k] = [obj[k], v]; + } + } + + return obj; +}; + + +/***/ }), + +/***/ "./node_modules/querystring/encode.js": +/*!********************************************!*\ + !*** ./node_modules/querystring/encode.js ***! + \********************************************/ +/***/ (function(module) { + +"use strict"; +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +var stringifyPrimitive = function(v) { + switch (typeof v) { + case 'string': + return v; + + case 'boolean': + return v ? 'true' : 'false'; + + case 'number': + return isFinite(v) ? v : ''; + + default: + return ''; + } +}; + +module.exports = function(obj, sep, eq, name) { + sep = sep || '&'; + eq = eq || '='; + if (obj === null) { + obj = undefined; + } + + if (typeof obj === 'object') { + return Object.keys(obj).map(function(k) { + var ks = encodeURIComponent(stringifyPrimitive(k)) + eq; + if (Array.isArray(obj[k])) { + return obj[k].map(function(v) { + return ks + encodeURIComponent(stringifyPrimitive(v)); + }).join(sep); + } else { + return ks + encodeURIComponent(stringifyPrimitive(obj[k])); + } + }).join(sep); + + } + + if (!name) return ''; + return encodeURIComponent(stringifyPrimitive(name)) + eq + + encodeURIComponent(stringifyPrimitive(obj)); +}; + + +/***/ }), + +/***/ "./node_modules/querystring/index.js": +/*!*******************************************!*\ + !*** ./node_modules/querystring/index.js ***! + \*******************************************/ +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { + +"use strict"; + + +exports.decode = exports.parse = __webpack_require__(/*! ./decode */ "./node_modules/querystring/decode.js"); +exports.encode = exports.stringify = __webpack_require__(/*! ./encode */ "./node_modules/querystring/encode.js"); + + +/***/ }), + +/***/ "./node_modules/regenerator-runtime/runtime.js": +/*!*****************************************************!*\ + !*** ./node_modules/regenerator-runtime/runtime.js ***! + \*****************************************************/ +/***/ (function(module) { + +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var runtime = (function (exports) { + "use strict"; + + var Op = Object.prototype; + var hasOwn = Op.hasOwnProperty; + var undefined; // More compressible than void 0. + var $Symbol = typeof Symbol === "function" ? Symbol : {}; + var iteratorSymbol = $Symbol.iterator || "@@iterator"; + var asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator"; + var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag"; + + function define(obj, key, value) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + return obj[key]; + } + try { + // IE 8 has a broken Object.defineProperty that only works on DOM objects. + define({}, ""); + } catch (err) { + define = function(obj, key, value) { + return obj[key] = value; + }; + } + + function wrap(innerFn, outerFn, self, tryLocsList) { + // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator. + var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator; + var generator = Object.create(protoGenerator.prototype); + var context = new Context(tryLocsList || []); + + // The ._invoke method unifies the implementations of the .next, + // .throw, and .return methods. + generator._invoke = makeInvokeMethod(innerFn, self, context); + + return generator; + } + exports.wrap = wrap; + + // Try/catch helper to minimize deoptimizations. Returns a completion + // record like context.tryEntries[i].completion. This interface could + // have been (and was previously) designed to take a closure to be + // invoked without arguments, but in all the cases we care about we + // already have an existing method we want to call, so there's no need + // to create a new function object. We can even get away with assuming + // the method takes exactly one argument, since that happens to be true + // in every case, so we don't have to touch the arguments object. The + // only additional allocation required is the completion record, which + // has a stable shape and so hopefully should be cheap to allocate. + function tryCatch(fn, obj, arg) { + try { + return { type: "normal", arg: fn.call(obj, arg) }; + } catch (err) { + return { type: "throw", arg: err }; + } + } + + var GenStateSuspendedStart = "suspendedStart"; + var GenStateSuspendedYield = "suspendedYield"; + var GenStateExecuting = "executing"; + var GenStateCompleted = "completed"; + + // Returning this object from the innerFn has the same effect as + // breaking out of the dispatch switch statement. + var ContinueSentinel = {}; + + // Dummy constructor functions that we use as the .constructor and + // .constructor.prototype properties for functions that return Generator + // objects. For full spec compliance, you may wish to configure your + // minifier not to mangle the names of these two functions. + function Generator() {} + function GeneratorFunction() {} + function GeneratorFunctionPrototype() {} + + // This is a polyfill for %IteratorPrototype% for environments that + // don't natively support it. + var IteratorPrototype = {}; + define(IteratorPrototype, iteratorSymbol, function () { + return this; + }); + + var getProto = Object.getPrototypeOf; + var NativeIteratorPrototype = getProto && getProto(getProto(values([]))); + if (NativeIteratorPrototype && + NativeIteratorPrototype !== Op && + hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) { + // This environment has a native %IteratorPrototype%; use it instead + // of the polyfill. + IteratorPrototype = NativeIteratorPrototype; + } + + var Gp = GeneratorFunctionPrototype.prototype = + Generator.prototype = Object.create(IteratorPrototype); + GeneratorFunction.prototype = GeneratorFunctionPrototype; + define(Gp, "constructor", GeneratorFunctionPrototype); + define(GeneratorFunctionPrototype, "constructor", GeneratorFunction); + GeneratorFunction.displayName = define( + GeneratorFunctionPrototype, + toStringTagSymbol, + "GeneratorFunction" + ); + + // Helper for defining the .next, .throw, and .return methods of the + // Iterator interface in terms of a single ._invoke method. + function defineIteratorMethods(prototype) { + ["next", "throw", "return"].forEach(function(method) { + define(prototype, method, function(arg) { + return this._invoke(method, arg); + }); + }); + } + + exports.isGeneratorFunction = function(genFun) { + var ctor = typeof genFun === "function" && genFun.constructor; + return ctor + ? ctor === GeneratorFunction || + // For the native GeneratorFunction constructor, the best we can + // do is to check its .name property. + (ctor.displayName || ctor.name) === "GeneratorFunction" + : false; + }; + + exports.mark = function(genFun) { + if (Object.setPrototypeOf) { + Object.setPrototypeOf(genFun, GeneratorFunctionPrototype); + } else { + genFun.__proto__ = GeneratorFunctionPrototype; + define(genFun, toStringTagSymbol, "GeneratorFunction"); + } + genFun.prototype = Object.create(Gp); + return genFun; + }; + + // Within the body of any async function, `await x` is transformed to + // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test + // `hasOwn.call(value, "__await")` to determine if the yielded value is + // meant to be awaited. + exports.awrap = function(arg) { + return { __await: arg }; + }; + + function AsyncIterator(generator, PromiseImpl) { + function invoke(method, arg, resolve, reject) { + var record = tryCatch(generator[method], generator, arg); + if (record.type === "throw") { + reject(record.arg); + } else { + var result = record.arg; + var value = result.value; + if (value && + typeof value === "object" && + hasOwn.call(value, "__await")) { + return PromiseImpl.resolve(value.__await).then(function(value) { + invoke("next", value, resolve, reject); + }, function(err) { + invoke("throw", err, resolve, reject); + }); + } + + return PromiseImpl.resolve(value).then(function(unwrapped) { + // When a yielded Promise is resolved, its final value becomes + // the .value of the Promise<{value,done}> result for the + // current iteration. + result.value = unwrapped; + resolve(result); + }, function(error) { + // If a rejected Promise was yielded, throw the rejection back + // into the async generator function so it can be handled there. + return invoke("throw", error, resolve, reject); + }); + } + } + + var previousPromise; + + function enqueue(method, arg) { + function callInvokeWithMethodAndArg() { + return new PromiseImpl(function(resolve, reject) { + invoke(method, arg, resolve, reject); + }); + } + + return previousPromise = + // If enqueue has been called before, then we want to wait until + // all previous Promises have been resolved before calling invoke, + // so that results are always delivered in the correct order. If + // enqueue has not been called before, then it is important to + // call invoke immediately, without waiting on a callback to fire, + // so that the async generator function has the opportunity to do + // any necessary setup in a predictable way. This predictability + // is why the Promise constructor synchronously invokes its + // executor callback, and why async functions synchronously + // execute code before the first await. Since we implement simple + // async functions in terms of async generators, it is especially + // important to get this right, even though it requires care. + previousPromise ? previousPromise.then( + callInvokeWithMethodAndArg, + // Avoid propagating failures to Promises returned by later + // invocations of the iterator. + callInvokeWithMethodAndArg + ) : callInvokeWithMethodAndArg(); + } + + // Define the unified helper method that is used to implement .next, + // .throw, and .return (see defineIteratorMethods). + this._invoke = enqueue; + } + + defineIteratorMethods(AsyncIterator.prototype); + define(AsyncIterator.prototype, asyncIteratorSymbol, function () { + return this; + }); + exports.AsyncIterator = AsyncIterator; + + // Note that simple async functions are implemented on top of + // AsyncIterator objects; they just return a Promise for the value of + // the final result produced by the iterator. + exports.async = function(innerFn, outerFn, self, tryLocsList, PromiseImpl) { + if (PromiseImpl === void 0) PromiseImpl = Promise; + + var iter = new AsyncIterator( + wrap(innerFn, outerFn, self, tryLocsList), + PromiseImpl + ); + + return exports.isGeneratorFunction(outerFn) + ? iter // If outerFn is a generator, return the full iterator. + : iter.next().then(function(result) { + return result.done ? result.value : iter.next(); + }); + }; + + function makeInvokeMethod(innerFn, self, context) { + var state = GenStateSuspendedStart; + + return function invoke(method, arg) { + if (state === GenStateExecuting) { + throw new Error("Generator is already running"); + } + + if (state === GenStateCompleted) { + if (method === "throw") { + throw arg; + } + + // Be forgiving, per 25.3.3.3.3 of the spec: + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume + return doneResult(); + } + + context.method = method; + context.arg = arg; + + while (true) { + var delegate = context.delegate; + if (delegate) { + var delegateResult = maybeInvokeDelegate(delegate, context); + if (delegateResult) { + if (delegateResult === ContinueSentinel) continue; + return delegateResult; + } + } + + if (context.method === "next") { + // Setting context._sent for legacy support of Babel's + // function.sent implementation. + context.sent = context._sent = context.arg; + + } else if (context.method === "throw") { + if (state === GenStateSuspendedStart) { + state = GenStateCompleted; + throw context.arg; + } + + context.dispatchException(context.arg); + + } else if (context.method === "return") { + context.abrupt("return", context.arg); + } + + state = GenStateExecuting; + + var record = tryCatch(innerFn, self, context); + if (record.type === "normal") { + // If an exception is thrown from innerFn, we leave state === + // GenStateExecuting and loop back for another invocation. + state = context.done + ? GenStateCompleted + : GenStateSuspendedYield; + + if (record.arg === ContinueSentinel) { + continue; + } + + return { + value: record.arg, + done: context.done + }; + + } else if (record.type === "throw") { + state = GenStateCompleted; + // Dispatch the exception by looping back around to the + // context.dispatchException(context.arg) call above. + context.method = "throw"; + context.arg = record.arg; + } + } + }; + } + + // Call delegate.iterator[context.method](context.arg) and handle the + // result, either by returning a { value, done } result from the + // delegate iterator, or by modifying context.method and context.arg, + // setting context.delegate to null, and returning the ContinueSentinel. + function maybeInvokeDelegate(delegate, context) { + var method = delegate.iterator[context.method]; + if (method === undefined) { + // A .throw or .return when the delegate iterator has no .throw + // method always terminates the yield* loop. + context.delegate = null; + + if (context.method === "throw") { + // Note: ["return"] must be used for ES3 parsing compatibility. + if (delegate.iterator["return"]) { + // If the delegate iterator has a return method, give it a + // chance to clean up. + context.method = "return"; + context.arg = undefined; + maybeInvokeDelegate(delegate, context); + + if (context.method === "throw") { + // If maybeInvokeDelegate(context) changed context.method from + // "return" to "throw", let that override the TypeError below. + return ContinueSentinel; + } + } + + context.method = "throw"; + context.arg = new TypeError( + "The iterator does not provide a 'throw' method"); + } + + return ContinueSentinel; + } + + var record = tryCatch(method, delegate.iterator, context.arg); + + if (record.type === "throw") { + context.method = "throw"; + context.arg = record.arg; + context.delegate = null; + return ContinueSentinel; + } + + var info = record.arg; + + if (! info) { + context.method = "throw"; + context.arg = new TypeError("iterator result is not an object"); + context.delegate = null; + return ContinueSentinel; + } + + if (info.done) { + // Assign the result of the finished delegate to the temporary + // variable specified by delegate.resultName (see delegateYield). + context[delegate.resultName] = info.value; + + // Resume execution at the desired location (see delegateYield). + context.next = delegate.nextLoc; + + // If context.method was "throw" but the delegate handled the + // exception, let the outer generator proceed normally. If + // context.method was "next", forget context.arg since it has been + // "consumed" by the delegate iterator. If context.method was + // "return", allow the original .return call to continue in the + // outer generator. + if (context.method !== "return") { + context.method = "next"; + context.arg = undefined; + } + + } else { + // Re-yield the result returned by the delegate method. + return info; + } + + // The delegate iterator is finished, so forget it and continue with + // the outer generator. + context.delegate = null; + return ContinueSentinel; + } + + // Define Generator.prototype.{next,throw,return} in terms of the + // unified ._invoke helper method. + defineIteratorMethods(Gp); + + define(Gp, toStringTagSymbol, "Generator"); + + // A Generator should always return itself as the iterator object when the + // @@iterator function is called on it. Some browsers' implementations of the + // iterator prototype chain incorrectly implement this, causing the Generator + // object to not be returned from this call. This ensures that doesn't happen. + // See https://github.com/facebook/regenerator/issues/274 for more details. + define(Gp, iteratorSymbol, function() { + return this; + }); + + define(Gp, "toString", function() { + return "[object Generator]"; + }); + + function pushTryEntry(locs) { + var entry = { tryLoc: locs[0] }; + + if (1 in locs) { + entry.catchLoc = locs[1]; + } + + if (2 in locs) { + entry.finallyLoc = locs[2]; + entry.afterLoc = locs[3]; + } + + this.tryEntries.push(entry); + } + + function resetTryEntry(entry) { + var record = entry.completion || {}; + record.type = "normal"; + delete record.arg; + entry.completion = record; + } + + function Context(tryLocsList) { + // The root entry object (effectively a try statement without a catch + // or a finally block) gives us a place to store values thrown from + // locations where there is no enclosing try statement. + this.tryEntries = [{ tryLoc: "root" }]; + tryLocsList.forEach(pushTryEntry, this); + this.reset(true); + } + + exports.keys = function(object) { + var keys = []; + for (var key in object) { + keys.push(key); + } + keys.reverse(); + + // Rather than returning an object with a next method, we keep + // things simple and return the next function itself. + return function next() { + while (keys.length) { + var key = keys.pop(); + if (key in object) { + next.value = key; + next.done = false; + return next; + } + } + + // To avoid creating an additional object, we just hang the .value + // and .done properties off the next function object itself. This + // also ensures that the minifier will not anonymize the function. + next.done = true; + return next; + }; + }; + + function values(iterable) { + if (iterable) { + var iteratorMethod = iterable[iteratorSymbol]; + if (iteratorMethod) { + return iteratorMethod.call(iterable); + } + + if (typeof iterable.next === "function") { + return iterable; + } + + if (!isNaN(iterable.length)) { + var i = -1, next = function next() { + while (++i < iterable.length) { + if (hasOwn.call(iterable, i)) { + next.value = iterable[i]; + next.done = false; + return next; + } + } + + next.value = undefined; + next.done = true; + + return next; + }; + + return next.next = next; + } + } + + // Return an iterator with no values. + return { next: doneResult }; + } + exports.values = values; + + function doneResult() { + return { value: undefined, done: true }; + } + + Context.prototype = { + constructor: Context, + + reset: function(skipTempReset) { + this.prev = 0; + this.next = 0; + // Resetting context._sent for legacy support of Babel's + // function.sent implementation. + this.sent = this._sent = undefined; + this.done = false; + this.delegate = null; + + this.method = "next"; + this.arg = undefined; + + this.tryEntries.forEach(resetTryEntry); + + if (!skipTempReset) { + for (var name in this) { + // Not sure about the optimal order of these conditions: + if (name.charAt(0) === "t" && + hasOwn.call(this, name) && + !isNaN(+name.slice(1))) { + this[name] = undefined; + } + } + } + }, + + stop: function() { + this.done = true; + + var rootEntry = this.tryEntries[0]; + var rootRecord = rootEntry.completion; + if (rootRecord.type === "throw") { + throw rootRecord.arg; + } + + return this.rval; + }, + + dispatchException: function(exception) { + if (this.done) { + throw exception; + } + + var context = this; + function handle(loc, caught) { + record.type = "throw"; + record.arg = exception; + context.next = loc; + + if (caught) { + // If the dispatched exception was caught by a catch block, + // then let that catch block handle the exception normally. + context.method = "next"; + context.arg = undefined; + } + + return !! caught; + } + + for (var i = this.tryEntries.length - 1; i >= 0; --i) { + var entry = this.tryEntries[i]; + var record = entry.completion; + + if (entry.tryLoc === "root") { + // Exception thrown outside of any try block that could handle + // it, so set the completion value of the entire function to + // throw the exception. + return handle("end"); + } + + if (entry.tryLoc <= this.prev) { + var hasCatch = hasOwn.call(entry, "catchLoc"); + var hasFinally = hasOwn.call(entry, "finallyLoc"); + + if (hasCatch && hasFinally) { + if (this.prev < entry.catchLoc) { + return handle(entry.catchLoc, true); + } else if (this.prev < entry.finallyLoc) { + return handle(entry.finallyLoc); + } + + } else if (hasCatch) { + if (this.prev < entry.catchLoc) { + return handle(entry.catchLoc, true); + } + + } else if (hasFinally) { + if (this.prev < entry.finallyLoc) { + return handle(entry.finallyLoc); + } + + } else { + throw new Error("try statement without catch or finally"); + } + } + } + }, + + abrupt: function(type, arg) { + for (var i = this.tryEntries.length - 1; i >= 0; --i) { + var entry = this.tryEntries[i]; + if (entry.tryLoc <= this.prev && + hasOwn.call(entry, "finallyLoc") && + this.prev < entry.finallyLoc) { + var finallyEntry = entry; + break; + } + } + + if (finallyEntry && + (type === "break" || + type === "continue") && + finallyEntry.tryLoc <= arg && + arg <= finallyEntry.finallyLoc) { + // Ignore the finally entry if control is not jumping to a + // location outside the try/catch block. + finallyEntry = null; + } + + var record = finallyEntry ? finallyEntry.completion : {}; + record.type = type; + record.arg = arg; + + if (finallyEntry) { + this.method = "next"; + this.next = finallyEntry.finallyLoc; + return ContinueSentinel; + } + + return this.complete(record); + }, + + complete: function(record, afterLoc) { + if (record.type === "throw") { + throw record.arg; + } + + if (record.type === "break" || + record.type === "continue") { + this.next = record.arg; + } else if (record.type === "return") { + this.rval = this.arg = record.arg; + this.method = "return"; + this.next = "end"; + } else if (record.type === "normal" && afterLoc) { + this.next = afterLoc; + } + + return ContinueSentinel; + }, + + finish: function(finallyLoc) { + for (var i = this.tryEntries.length - 1; i >= 0; --i) { + var entry = this.tryEntries[i]; + if (entry.finallyLoc === finallyLoc) { + this.complete(entry.completion, entry.afterLoc); + resetTryEntry(entry); + return ContinueSentinel; + } + } + }, + + "catch": function(tryLoc) { + for (var i = this.tryEntries.length - 1; i >= 0; --i) { + var entry = this.tryEntries[i]; + if (entry.tryLoc === tryLoc) { + var record = entry.completion; + if (record.type === "throw") { + var thrown = record.arg; + resetTryEntry(entry); + } + return thrown; + } + } + + // The context.catch method must only be called with a location + // argument that corresponds to a known catch block. + throw new Error("illegal catch attempt"); + }, + + delegateYield: function(iterable, resultName, nextLoc) { + this.delegate = { + iterator: values(iterable), + resultName: resultName, + nextLoc: nextLoc + }; + + if (this.method === "next") { + // Deliberately forget the last sent value so that we don't + // accidentally pass it on to the delegate. + this.arg = undefined; + } + + return ContinueSentinel; + } + }; + + // Regardless of whether this script is executing as a CommonJS module + // or not, return the runtime object so that we can declare the variable + // regeneratorRuntime in the outer scope, which allows this module to be + // injected easily by `bin/regenerator --include-runtime script.js`. + return exports; + +}( + // If this script is executing as a CommonJS module, use module.exports + // as the regeneratorRuntime namespace. Otherwise create a new empty + // object. Either way, the resulting object will be used to initialize + // the regeneratorRuntime variable at the top of this file. + true ? module.exports : 0 +)); + +try { + regeneratorRuntime = runtime; +} catch (accidentalStrictMode) { + // This module should not be running in strict mode, so the above + // assignment should always work unless something is misconfigured. Just + // in case runtime.js accidentally runs in strict mode, in modern engines + // we can explicitly access globalThis. In older engines we can escape + // strict mode using a global Function call. This could conceivably fail + // if a Content Security Policy forbids using Function, but in that case + // the proper solution is to fix the accidental strict mode problem. If + // you've misconfigured your bundler to force strict mode and applied a + // CSP to forbid Function, and you're not willing to fix either of those + // problems, please detail your unique predicament in a GitHub issue. + if (typeof globalThis === "object") { + globalThis.regeneratorRuntime = runtime; + } else { + Function("r", "regeneratorRuntime = r")(runtime); + } +} + + +/***/ }), + +/***/ "./node_modules/url/url.js": +/*!*********************************!*\ + !*** ./node_modules/url/url.js ***! + \*********************************/ +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { + +"use strict"; +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +var punycode = __webpack_require__(/*! punycode */ "./node_modules/punycode/punycode.js"); +var util = __webpack_require__(/*! ./util */ "./node_modules/url/util.js"); + +exports.parse = urlParse; +exports.resolve = urlResolve; +exports.resolveObject = urlResolveObject; +exports.format = urlFormat; + +exports.Url = Url; + +function Url() { + this.protocol = null; + this.slashes = null; + this.auth = null; + this.host = null; + this.port = null; + this.hostname = null; + this.hash = null; + this.search = null; + this.query = null; + this.pathname = null; + this.path = null; + this.href = null; +} + +// Reference: RFC 3986, RFC 1808, RFC 2396 + +// define these here so at least they only have to be +// compiled once on the first module load. +var protocolPattern = /^([a-z0-9.+-]+:)/i, + portPattern = /:[0-9]*$/, + + // Special case for a simple path URL + simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/, + + // RFC 2396: characters reserved for delimiting URLs. + // We actually just auto-escape these. + delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'], + + // RFC 2396: characters not allowed for various reasons. + unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims), + + // Allowed by RFCs, but cause of XSS attacks. Always escape these. + autoEscape = ['\''].concat(unwise), + // Characters that are never ever allowed in a hostname. + // Note that any invalid chars are also handled, but these + // are the ones that are *expected* to be seen, so we fast-path + // them. + nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape), + hostEndingChars = ['/', '?', '#'], + hostnameMaxLen = 255, + hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/, + hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/, + // protocols that can allow "unsafe" and "unwise" chars. + unsafeProtocol = { + 'javascript': true, + 'javascript:': true + }, + // protocols that never have a hostname. + hostlessProtocol = { + 'javascript': true, + 'javascript:': true + }, + // protocols that always contain a // bit. + slashedProtocol = { + 'http': true, + 'https': true, + 'ftp': true, + 'gopher': true, + 'file': true, + 'http:': true, + 'https:': true, + 'ftp:': true, + 'gopher:': true, + 'file:': true + }, + querystring = __webpack_require__(/*! querystring */ "./node_modules/querystring/index.js"); + +function urlParse(url, parseQueryString, slashesDenoteHost) { + if (url && util.isObject(url) && url instanceof Url) return url; + + var u = new Url; + u.parse(url, parseQueryString, slashesDenoteHost); + return u; +} + +Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) { + if (!util.isString(url)) { + throw new TypeError("Parameter 'url' must be a string, not " + typeof url); + } + + // Copy chrome, IE, opera backslash-handling behavior. + // Back slashes before the query string get converted to forward slashes + // See: https://code.google.com/p/chromium/issues/detail?id=25916 + var queryIndex = url.indexOf('?'), + splitter = + (queryIndex !== -1 && queryIndex < url.indexOf('#')) ? '?' : '#', + uSplit = url.split(splitter), + slashRegex = /\\/g; + uSplit[0] = uSplit[0].replace(slashRegex, '/'); + url = uSplit.join(splitter); + + var rest = url; + + // trim before proceeding. + // This is to support parse stuff like " http://foo.com \n" + rest = rest.trim(); + + if (!slashesDenoteHost && url.split('#').length === 1) { + // Try fast path regexp + var simplePath = simplePathPattern.exec(rest); + if (simplePath) { + this.path = rest; + this.href = rest; + this.pathname = simplePath[1]; + if (simplePath[2]) { + this.search = simplePath[2]; + if (parseQueryString) { + this.query = querystring.parse(this.search.substr(1)); + } else { + this.query = this.search.substr(1); + } + } else if (parseQueryString) { + this.search = ''; + this.query = {}; + } + return this; + } + } + + var proto = protocolPattern.exec(rest); + if (proto) { + proto = proto[0]; + var lowerProto = proto.toLowerCase(); + this.protocol = lowerProto; + rest = rest.substr(proto.length); + } + + // figure out if it's got a host + // user@server is *always* interpreted as a hostname, and url + // resolution will treat //foo/bar as host=foo,path=bar because that's + // how the browser resolves relative URLs. + if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) { + var slashes = rest.substr(0, 2) === '//'; + if (slashes && !(proto && hostlessProtocol[proto])) { + rest = rest.substr(2); + this.slashes = true; + } + } + + if (!hostlessProtocol[proto] && + (slashes || (proto && !slashedProtocol[proto]))) { + + // there's a hostname. + // the first instance of /, ?, ;, or # ends the host. + // + // If there is an @ in the hostname, then non-host chars *are* allowed + // to the left of the last @ sign, unless some host-ending character + // comes *before* the @-sign. + // URLs are obnoxious. + // + // ex: + // http://a@b@c/ => user:a@b host:c + // http://a@b?@c => user:a host:c path:/?@c + + // v0.12 TODO(isaacs): This is not quite how Chrome does things. + // Review our test case against browsers more comprehensively. + + // find the first instance of any hostEndingChars + var hostEnd = -1; + for (var i = 0; i < hostEndingChars.length; i++) { + var hec = rest.indexOf(hostEndingChars[i]); + if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) + hostEnd = hec; + } + + // at this point, either we have an explicit point where the + // auth portion cannot go past, or the last @ char is the decider. + var auth, atSign; + if (hostEnd === -1) { + // atSign can be anywhere. + atSign = rest.lastIndexOf('@'); + } else { + // atSign must be in auth portion. + // http://a@b/c@d => host:b auth:a path:/c@d + atSign = rest.lastIndexOf('@', hostEnd); + } + + // Now we have a portion which is definitely the auth. + // Pull that off. + if (atSign !== -1) { + auth = rest.slice(0, atSign); + rest = rest.slice(atSign + 1); + this.auth = decodeURIComponent(auth); + } + + // the host is the remaining to the left of the first non-host char + hostEnd = -1; + for (var i = 0; i < nonHostChars.length; i++) { + var hec = rest.indexOf(nonHostChars[i]); + if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) + hostEnd = hec; + } + // if we still have not hit it, then the entire thing is a host. + if (hostEnd === -1) + hostEnd = rest.length; + + this.host = rest.slice(0, hostEnd); + rest = rest.slice(hostEnd); + + // pull out port. + this.parseHost(); + + // we've indicated that there is a hostname, + // so even if it's empty, it has to be present. + this.hostname = this.hostname || ''; + + // if hostname begins with [ and ends with ] + // assume that it's an IPv6 address. + var ipv6Hostname = this.hostname[0] === '[' && + this.hostname[this.hostname.length - 1] === ']'; + + // validate a little. + if (!ipv6Hostname) { + var hostparts = this.hostname.split(/\./); + for (var i = 0, l = hostparts.length; i < l; i++) { + var part = hostparts[i]; + if (!part) continue; + if (!part.match(hostnamePartPattern)) { + var newpart = ''; + for (var j = 0, k = part.length; j < k; j++) { + if (part.charCodeAt(j) > 127) { + // we replace non-ASCII char with a temporary placeholder + // we need this to make sure size of hostname is not + // broken by replacing non-ASCII by nothing + newpart += 'x'; + } else { + newpart += part[j]; + } + } + // we test again with ASCII char only + if (!newpart.match(hostnamePartPattern)) { + var validParts = hostparts.slice(0, i); + var notHost = hostparts.slice(i + 1); + var bit = part.match(hostnamePartStart); + if (bit) { + validParts.push(bit[1]); + notHost.unshift(bit[2]); + } + if (notHost.length) { + rest = '/' + notHost.join('.') + rest; + } + this.hostname = validParts.join('.'); + break; + } + } + } + } + + if (this.hostname.length > hostnameMaxLen) { + this.hostname = ''; + } else { + // hostnames are always lower case. + this.hostname = this.hostname.toLowerCase(); + } + + if (!ipv6Hostname) { + // IDNA Support: Returns a punycoded representation of "domain". + // It only converts parts of the domain name that + // have non-ASCII characters, i.e. it doesn't matter if + // you call it with a domain that already is ASCII-only. + this.hostname = punycode.toASCII(this.hostname); + } + + var p = this.port ? ':' + this.port : ''; + var h = this.hostname || ''; + this.host = h + p; + this.href += this.host; + + // strip [ and ] from the hostname + // the host field still retains them, though + if (ipv6Hostname) { + this.hostname = this.hostname.substr(1, this.hostname.length - 2); + if (rest[0] !== '/') { + rest = '/' + rest; + } + } + } + + // now rest is set to the post-host stuff. + // chop off any delim chars. + if (!unsafeProtocol[lowerProto]) { + + // First, make 100% sure that any "autoEscape" chars get + // escaped, even if encodeURIComponent doesn't think they + // need to be. + for (var i = 0, l = autoEscape.length; i < l; i++) { + var ae = autoEscape[i]; + if (rest.indexOf(ae) === -1) + continue; + var esc = encodeURIComponent(ae); + if (esc === ae) { + esc = escape(ae); + } + rest = rest.split(ae).join(esc); + } + } + + + // chop off from the tail first. + var hash = rest.indexOf('#'); + if (hash !== -1) { + // got a fragment string. + this.hash = rest.substr(hash); + rest = rest.slice(0, hash); + } + var qm = rest.indexOf('?'); + if (qm !== -1) { + this.search = rest.substr(qm); + this.query = rest.substr(qm + 1); + if (parseQueryString) { + this.query = querystring.parse(this.query); + } + rest = rest.slice(0, qm); + } else if (parseQueryString) { + // no query string, but parseQueryString still requested + this.search = ''; + this.query = {}; + } + if (rest) this.pathname = rest; + if (slashedProtocol[lowerProto] && + this.hostname && !this.pathname) { + this.pathname = '/'; + } + + //to support http.request + if (this.pathname || this.search) { + var p = this.pathname || ''; + var s = this.search || ''; + this.path = p + s; + } + + // finally, reconstruct the href based on what has been validated. + this.href = this.format(); + return this; +}; + +// format a parsed object into a url string +function urlFormat(obj) { + // ensure it's an object, and not a string url. + // If it's an obj, this is a no-op. + // this way, you can call url_format() on strings + // to clean up potentially wonky urls. + if (util.isString(obj)) obj = urlParse(obj); + if (!(obj instanceof Url)) return Url.prototype.format.call(obj); + return obj.format(); +} + +Url.prototype.format = function() { + var auth = this.auth || ''; + if (auth) { + auth = encodeURIComponent(auth); + auth = auth.replace(/%3A/i, ':'); + auth += '@'; + } + + var protocol = this.protocol || '', + pathname = this.pathname || '', + hash = this.hash || '', + host = false, + query = ''; + + if (this.host) { + host = auth + this.host; + } else if (this.hostname) { + host = auth + (this.hostname.indexOf(':') === -1 ? + this.hostname : + '[' + this.hostname + ']'); + if (this.port) { + host += ':' + this.port; + } + } + + if (this.query && + util.isObject(this.query) && + Object.keys(this.query).length) { + query = querystring.stringify(this.query); + } + + var search = this.search || (query && ('?' + query)) || ''; + + if (protocol && protocol.substr(-1) !== ':') protocol += ':'; + + // only the slashedProtocols get the //. Not mailto:, xmpp:, etc. + // unless they had them to begin with. + if (this.slashes || + (!protocol || slashedProtocol[protocol]) && host !== false) { + host = '//' + (host || ''); + if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname; + } else if (!host) { + host = ''; + } + + if (hash && hash.charAt(0) !== '#') hash = '#' + hash; + if (search && search.charAt(0) !== '?') search = '?' + search; + + pathname = pathname.replace(/[?#]/g, function(match) { + return encodeURIComponent(match); + }); + search = search.replace('#', '%23'); + + return protocol + host + pathname + search + hash; +}; + +function urlResolve(source, relative) { + return urlParse(source, false, true).resolve(relative); +} + +Url.prototype.resolve = function(relative) { + return this.resolveObject(urlParse(relative, false, true)).format(); +}; + +function urlResolveObject(source, relative) { + if (!source) return relative; + return urlParse(source, false, true).resolveObject(relative); +} + +Url.prototype.resolveObject = function(relative) { + if (util.isString(relative)) { + var rel = new Url(); + rel.parse(relative, false, true); + relative = rel; + } + + var result = new Url(); + var tkeys = Object.keys(this); + for (var tk = 0; tk < tkeys.length; tk++) { + var tkey = tkeys[tk]; + result[tkey] = this[tkey]; + } + + // hash is always overridden, no matter what. + // even href="" will remove it. + result.hash = relative.hash; + + // if the relative url is empty, then there's nothing left to do here. + if (relative.href === '') { + result.href = result.format(); + return result; + } + + // hrefs like //foo/bar always cut to the protocol. + if (relative.slashes && !relative.protocol) { + // take everything except the protocol from relative + var rkeys = Object.keys(relative); + for (var rk = 0; rk < rkeys.length; rk++) { + var rkey = rkeys[rk]; + if (rkey !== 'protocol') + result[rkey] = relative[rkey]; + } + + //urlParse appends trailing / to urls like http://www.example.com + if (slashedProtocol[result.protocol] && + result.hostname && !result.pathname) { + result.path = result.pathname = '/'; + } + + result.href = result.format(); + return result; + } + + if (relative.protocol && relative.protocol !== result.protocol) { + // if it's a known url protocol, then changing + // the protocol does weird things + // first, if it's not file:, then we MUST have a host, + // and if there was a path + // to begin with, then we MUST have a path. + // if it is file:, then the host is dropped, + // because that's known to be hostless. + // anything else is assumed to be absolute. + if (!slashedProtocol[relative.protocol]) { + var keys = Object.keys(relative); + for (var v = 0; v < keys.length; v++) { + var k = keys[v]; + result[k] = relative[k]; + } + result.href = result.format(); + return result; + } + + result.protocol = relative.protocol; + if (!relative.host && !hostlessProtocol[relative.protocol]) { + var relPath = (relative.pathname || '').split('/'); + while (relPath.length && !(relative.host = relPath.shift())); + if (!relative.host) relative.host = ''; + if (!relative.hostname) relative.hostname = ''; + if (relPath[0] !== '') relPath.unshift(''); + if (relPath.length < 2) relPath.unshift(''); + result.pathname = relPath.join('/'); + } else { + result.pathname = relative.pathname; + } + result.search = relative.search; + result.query = relative.query; + result.host = relative.host || ''; + result.auth = relative.auth; + result.hostname = relative.hostname || relative.host; + result.port = relative.port; + // to support http.request + if (result.pathname || result.search) { + var p = result.pathname || ''; + var s = result.search || ''; + result.path = p + s; + } + result.slashes = result.slashes || relative.slashes; + result.href = result.format(); + return result; + } + + var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'), + isRelAbs = ( + relative.host || + relative.pathname && relative.pathname.charAt(0) === '/' + ), + mustEndAbs = (isRelAbs || isSourceAbs || + (result.host && relative.pathname)), + removeAllDots = mustEndAbs, + srcPath = result.pathname && result.pathname.split('/') || [], + relPath = relative.pathname && relative.pathname.split('/') || [], + psychotic = result.protocol && !slashedProtocol[result.protocol]; + + // if the url is a non-slashed url, then relative + // links like ../.. should be able + // to crawl up to the hostname, as well. This is strange. + // result.protocol has already been set by now. + // Later on, put the first path part into the host field. + if (psychotic) { + result.hostname = ''; + result.port = null; + if (result.host) { + if (srcPath[0] === '') srcPath[0] = result.host; + else srcPath.unshift(result.host); + } + result.host = ''; + if (relative.protocol) { + relative.hostname = null; + relative.port = null; + if (relative.host) { + if (relPath[0] === '') relPath[0] = relative.host; + else relPath.unshift(relative.host); + } + relative.host = null; + } + mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === ''); + } + + if (isRelAbs) { + // it's absolute. + result.host = (relative.host || relative.host === '') ? + relative.host : result.host; + result.hostname = (relative.hostname || relative.hostname === '') ? + relative.hostname : result.hostname; + result.search = relative.search; + result.query = relative.query; + srcPath = relPath; + // fall through to the dot-handling below. + } else if (relPath.length) { + // it's relative + // throw away the existing file, and take the new path instead. + if (!srcPath) srcPath = []; + srcPath.pop(); + srcPath = srcPath.concat(relPath); + result.search = relative.search; + result.query = relative.query; + } else if (!util.isNullOrUndefined(relative.search)) { + // just pull out the search. + // like href='?foo'. + // Put this after the other two cases because it simplifies the booleans + if (psychotic) { + result.hostname = result.host = srcPath.shift(); + //occationaly the auth can get stuck only in host + //this especially happens in cases like + //url.resolveObject('mailto:local1@domain1', 'local2@domain2') + var authInHost = result.host && result.host.indexOf('@') > 0 ? + result.host.split('@') : false; + if (authInHost) { + result.auth = authInHost.shift(); + result.host = result.hostname = authInHost.shift(); + } + } + result.search = relative.search; + result.query = relative.query; + //to support http.request + if (!util.isNull(result.pathname) || !util.isNull(result.search)) { + result.path = (result.pathname ? result.pathname : '') + + (result.search ? result.search : ''); + } + result.href = result.format(); + return result; + } + + if (!srcPath.length) { + // no path at all. easy. + // we've already handled the other stuff above. + result.pathname = null; + //to support http.request + if (result.search) { + result.path = '/' + result.search; + } else { + result.path = null; + } + result.href = result.format(); + return result; + } + + // if a url ENDs in . or .., then it must get a trailing slash. + // however, if it ends in anything else non-slashy, + // then it must NOT get a trailing slash. + var last = srcPath.slice(-1)[0]; + var hasTrailingSlash = ( + (result.host || relative.host || srcPath.length > 1) && + (last === '.' || last === '..') || last === ''); + + // strip single dots, resolve double dots to parent dir + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = srcPath.length; i >= 0; i--) { + last = srcPath[i]; + if (last === '.') { + srcPath.splice(i, 1); + } else if (last === '..') { + srcPath.splice(i, 1); + up++; + } else if (up) { + srcPath.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (!mustEndAbs && !removeAllDots) { + for (; up--; up) { + srcPath.unshift('..'); + } + } + + if (mustEndAbs && srcPath[0] !== '' && + (!srcPath[0] || srcPath[0].charAt(0) !== '/')) { + srcPath.unshift(''); + } + + if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) { + srcPath.push(''); + } + + var isAbsolute = srcPath[0] === '' || + (srcPath[0] && srcPath[0].charAt(0) === '/'); + + // put the host back + if (psychotic) { + result.hostname = result.host = isAbsolute ? '' : + srcPath.length ? srcPath.shift() : ''; + //occationaly the auth can get stuck only in host + //this especially happens in cases like + //url.resolveObject('mailto:local1@domain1', 'local2@domain2') + var authInHost = result.host && result.host.indexOf('@') > 0 ? + result.host.split('@') : false; + if (authInHost) { + result.auth = authInHost.shift(); + result.host = result.hostname = authInHost.shift(); + } + } + + mustEndAbs = mustEndAbs || (result.host && srcPath.length); + + if (mustEndAbs && !isAbsolute) { + srcPath.unshift(''); + } + + if (!srcPath.length) { + result.pathname = null; + result.path = null; + } else { + result.pathname = srcPath.join('/'); + } + + //to support request.http + if (!util.isNull(result.pathname) || !util.isNull(result.search)) { + result.path = (result.pathname ? result.pathname : '') + + (result.search ? result.search : ''); + } + result.auth = relative.auth || result.auth; + result.slashes = result.slashes || relative.slashes; + result.href = result.format(); + return result; +}; + +Url.prototype.parseHost = function() { + var host = this.host; + var port = portPattern.exec(host); + if (port) { + port = port[0]; + if (port !== ':') { + this.port = port.substr(1); + } + host = host.substr(0, host.length - port.length); + } + if (host) this.hostname = host; +}; + + +/***/ }), + +/***/ "./node_modules/url/util.js": +/*!**********************************!*\ + !*** ./node_modules/url/util.js ***! + \**********************************/ +/***/ (function(module) { + +"use strict"; + + +module.exports = { + isString: function(arg) { + return typeof(arg) === 'string'; + }, + isObject: function(arg) { + return typeof(arg) === 'object' && arg !== null; + }, + isNull: function(arg) { + return arg === null; + }, + isNullOrUndefined: function(arg) { + return arg == null; + } +}; + + +/***/ }), + +/***/ "./node_modules/webpack-dev-server/client/clients/WebSocketClient.js": +/*!***************************************************************************!*\ + !*** ./node_modules/webpack-dev-server/client/clients/WebSocketClient.js ***! + \***************************************************************************/ +/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": function() { return /* binding */ WebSocketClient; } +/* harmony export */ }); +/* harmony import */ var _utils_log_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils/log.js */ "./node_modules/webpack-dev-server/client/utils/log.js"); +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + + + +var WebSocketClient = /*#__PURE__*/function () { + function WebSocketClient(url) { + _classCallCheck(this, WebSocketClient); + + this.client = new WebSocket(url); + + this.client.onerror = function (error) { + _utils_log_js__WEBPACK_IMPORTED_MODULE_0__.log.error(error); + }; + } + + _createClass(WebSocketClient, [{ + key: "onOpen", + value: function onOpen(f) { + this.client.onopen = f; + } + }, { + key: "onClose", + value: function onClose(f) { + this.client.onclose = f; + } // call f with the message string as the first argument + + }, { + key: "onMessage", + value: function onMessage(f) { + this.client.onmessage = function (e) { + f(e.data); + }; + } + }]); + + return WebSocketClient; +}(); + + + +/***/ }), + +/***/ "./node_modules/webpack-dev-server/client/index.js?protocol=wss%3A&hostname=demo.armandphilippot.test&port=8080&pathname=%2Fws&logging=info": +/*!**************************************************************************************************************************************************!*\ + !*** ./node_modules/webpack-dev-server/client/index.js?protocol=wss%3A&hostname=demo.armandphilippot.test&port=8080&pathname=%2Fws&logging=info ***! + \**************************************************************************************************************************************************/ +/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +var __resourceQuery = "?protocol=wss%3A&hostname=demo.armandphilippot.test&port=8080&pathname=%2Fws&logging=info"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var webpack_hot_log_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! webpack/hot/log.js */ "./node_modules/webpack/hot/log.js"); +/* harmony import */ var webpack_hot_log_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(webpack_hot_log_js__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _modules_strip_ansi_index_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./modules/strip-ansi/index.js */ "./node_modules/webpack-dev-server/client/modules/strip-ansi/index.js"); +/* harmony import */ var _modules_strip_ansi_index_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_modules_strip_ansi_index_js__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _utils_parseURL_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utils/parseURL.js */ "./node_modules/webpack-dev-server/client/utils/parseURL.js"); +/* harmony import */ var _socket_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./socket.js */ "./node_modules/webpack-dev-server/client/socket.js"); +/* harmony import */ var _overlay_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./overlay.js */ "./node_modules/webpack-dev-server/client/overlay.js"); +/* harmony import */ var _utils_log_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./utils/log.js */ "./node_modules/webpack-dev-server/client/utils/log.js"); +/* harmony import */ var _utils_sendMessage_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./utils/sendMessage.js */ "./node_modules/webpack-dev-server/client/utils/sendMessage.js"); +/* harmony import */ var _utils_reloadApp_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./utils/reloadApp.js */ "./node_modules/webpack-dev-server/client/utils/reloadApp.js"); +/* harmony import */ var _utils_createSocketURL_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./utils/createSocketURL.js */ "./node_modules/webpack-dev-server/client/utils/createSocketURL.js"); +/* global __resourceQuery, __webpack_hash__ */ + + + + + + + + + +var status = { + isUnloading: false, + // TODO Workaround for webpack v4, `__webpack_hash__` is not replaced without HotModuleReplacement + // eslint-disable-next-line camelcase + currentHash: true ? __webpack_require__.h() : 0 +}; // console.log(__webpack_hash__); + +var options = { + hot: false, + liveReload: false, + progress: false, + overlay: false +}; +var parsedResourceQuery = (0,_utils_parseURL_js__WEBPACK_IMPORTED_MODULE_2__["default"])(__resourceQuery); + +if (parsedResourceQuery.hot === "true") { + options.hot = true; + _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.info("Hot Module Replacement enabled."); +} + +if (parsedResourceQuery["live-reload"] === "true") { + options.liveReload = true; + _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.info("Live Reloading enabled."); +} + +if (parsedResourceQuery.logging) { + options.logging = parsedResourceQuery.logging; +} + +function setAllLogLevel(level) { + // This is needed because the HMR logger operate separately from dev server logger + webpack_hot_log_js__WEBPACK_IMPORTED_MODULE_0___default().setLogLevel(level === "verbose" || level === "log" ? "info" : level); + (0,_utils_log_js__WEBPACK_IMPORTED_MODULE_5__.setLogLevel)(level); +} + +if (options.logging) { + setAllLogLevel(options.logging); +} + +self.addEventListener("beforeunload", function () { + status.isUnloading = true; +}); +var onSocketMessage = { + hot: function hot() { + if (parsedResourceQuery.hot === "false") { + return; + } + + options.hot = true; + _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.info("Hot Module Replacement enabled."); + }, + liveReload: function liveReload() { + if (parsedResourceQuery["live-reload"] === "false") { + return; + } + + options.liveReload = true; + _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.info("Live Reloading enabled."); + }, + invalid: function invalid() { + _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.info("App updated. Recompiling..."); // Fixes #1042. overlay doesn't clear if errors are fixed but warnings remain. + + if (options.overlay) { + (0,_overlay_js__WEBPACK_IMPORTED_MODULE_4__.hide)(); + } + + (0,_utils_sendMessage_js__WEBPACK_IMPORTED_MODULE_6__["default"])("Invalid"); + }, + hash: function hash(_hash) { + status.previousHash = status.currentHash; + status.currentHash = _hash; + }, + logging: setAllLogLevel, + overlay: function overlay(value) { + if (typeof document === "undefined") { + return; + } + + options.overlay = value; + }, + progress: function progress(_progress) { + options.progress = _progress; + }, + "progress-update": function progressUpdate(data) { + if (options.progress) { + _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.info("".concat(data.pluginName ? "[".concat(data.pluginName, "] ") : "").concat(data.percent, "% - ").concat(data.msg, ".")); + } + + (0,_utils_sendMessage_js__WEBPACK_IMPORTED_MODULE_6__["default"])("Progress", data); + }, + "still-ok": function stillOk() { + _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.info("Nothing changed."); + + if (options.overlay) { + (0,_overlay_js__WEBPACK_IMPORTED_MODULE_4__.hide)(); + } + + (0,_utils_sendMessage_js__WEBPACK_IMPORTED_MODULE_6__["default"])("StillOk"); + }, + ok: function ok() { + (0,_utils_sendMessage_js__WEBPACK_IMPORTED_MODULE_6__["default"])("Ok"); + + if (options.overlay) { + (0,_overlay_js__WEBPACK_IMPORTED_MODULE_4__.hide)(); + } + + (0,_utils_reloadApp_js__WEBPACK_IMPORTED_MODULE_7__["default"])(options, status); + }, + // TODO: remove in v5 in favor of 'static-changed' + "content-changed": function contentChanged(file) { + _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.info("".concat(file ? "\"".concat(file, "\"") : "Content", " from static directory was changed. Reloading...")); + self.location.reload(); + }, + "static-changed": function staticChanged(file) { + _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.info("".concat(file ? "\"".concat(file, "\"") : "Content", " from static directory was changed. Reloading...")); + self.location.reload(); + }, + warnings: function warnings(_warnings) { + _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.warn("Warnings while compiling."); + + var printableWarnings = _warnings.map(function (error) { + var _formatProblem = (0,_overlay_js__WEBPACK_IMPORTED_MODULE_4__.formatProblem)("warning", error), + header = _formatProblem.header, + body = _formatProblem.body; + + return "".concat(header, "\n").concat(_modules_strip_ansi_index_js__WEBPACK_IMPORTED_MODULE_1___default()(body)); + }); + + (0,_utils_sendMessage_js__WEBPACK_IMPORTED_MODULE_6__["default"])("Warnings", printableWarnings); + + for (var i = 0; i < printableWarnings.length; i++) { + _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.warn(printableWarnings[i]); + } + + var needShowOverlayForWarnings = typeof options.overlay === "boolean" ? options.overlay : options.overlay && options.overlay.warnings; + + if (needShowOverlayForWarnings) { + (0,_overlay_js__WEBPACK_IMPORTED_MODULE_4__.show)("warning", _warnings); + } + + (0,_utils_reloadApp_js__WEBPACK_IMPORTED_MODULE_7__["default"])(options, status); + }, + errors: function errors(_errors) { + _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.error("Errors while compiling. Reload prevented."); + + var printableErrors = _errors.map(function (error) { + var _formatProblem2 = (0,_overlay_js__WEBPACK_IMPORTED_MODULE_4__.formatProblem)("error", error), + header = _formatProblem2.header, + body = _formatProblem2.body; + + return "".concat(header, "\n").concat(_modules_strip_ansi_index_js__WEBPACK_IMPORTED_MODULE_1___default()(body)); + }); + + (0,_utils_sendMessage_js__WEBPACK_IMPORTED_MODULE_6__["default"])("Errors", printableErrors); + + for (var i = 0; i < printableErrors.length; i++) { + _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.error(printableErrors[i]); + } + + var needShowOverlayForErrors = typeof options.overlay === "boolean" ? options.overlay : options.overlay && options.overlay.errors; + + if (needShowOverlayForErrors) { + (0,_overlay_js__WEBPACK_IMPORTED_MODULE_4__.show)("error", _errors); + } + }, + error: function error(_error) { + _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.error(_error); + }, + close: function close() { + _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.info("Disconnected!"); + + if (options.overlay) { + (0,_overlay_js__WEBPACK_IMPORTED_MODULE_4__.hide)(); + } + + (0,_utils_sendMessage_js__WEBPACK_IMPORTED_MODULE_6__["default"])("Close"); + } +}; +var socketURL = (0,_utils_createSocketURL_js__WEBPACK_IMPORTED_MODULE_8__["default"])(parsedResourceQuery); +(0,_socket_js__WEBPACK_IMPORTED_MODULE_3__["default"])(socketURL, onSocketMessage); + +/***/ }), + +/***/ "./node_modules/webpack-dev-server/client/modules/logger/index.js": +/*!************************************************************************!*\ + !*** ./node_modules/webpack-dev-server/client/modules/logger/index.js ***! + \************************************************************************/ +/***/ (function(__unused_webpack_module, exports) { + +/******/ (function() { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ({ + +/***/ "./client-src/modules/logger/SyncBailHookFake.js": +/*!*******************************************************!*\ + !*** ./client-src/modules/logger/SyncBailHookFake.js ***! + \*******************************************************/ +/***/ (function(module) { + + +/** + * Client stub for tapable SyncBailHook + */ + +module.exports = function clientTapableSyncBailHook() { + return { + call: function call() {} + }; +}; + +/***/ }), + +/***/ "./node_modules/webpack/lib/logging/Logger.js": +/*!****************************************************!*\ + !*** ./node_modules/webpack/lib/logging/Logger.js ***! + \****************************************************/ +/***/ (function(__unused_webpack_module, exports) { + +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + + +function _toConsumableArray(arr) { + return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); +} + +function _nonIterableSpread() { + throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); +} + +function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); +} + +function _iterableToArray(iter) { + if (typeof (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }) !== "undefined" && iter[(typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }).iterator] != null || iter["@@iterator"] != null) return Array.from(iter); +} + +function _arrayWithoutHoles(arr) { + if (Array.isArray(arr)) return _arrayLikeToArray(arr); +} + +function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; + + for (var i = 0, arr2 = new Array(len); i < len; i++) { + arr2[i] = arr[i]; + } + + return arr2; +} + +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +} + +function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } +} + +function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; +} + +var LogType = Object.freeze({ + error: "error", + // message, c style arguments + warn: "warn", + // message, c style arguments + info: "info", + // message, c style arguments + log: "log", + // message, c style arguments + debug: "debug", + // message, c style arguments + trace: "trace", + // no arguments + group: "group", + // [label] + groupCollapsed: "groupCollapsed", + // [label] + groupEnd: "groupEnd", + // [label] + profile: "profile", + // [profileName] + profileEnd: "profileEnd", + // [profileName] + time: "time", + // name, time as [seconds, nanoseconds] + clear: "clear", + // no arguments + status: "status" // message, arguments + +}); +exports.LogType = LogType; +/** @typedef {typeof LogType[keyof typeof LogType]} LogTypeEnum */ + +var LOG_SYMBOL = (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; })("webpack logger raw log method"); +var TIMERS_SYMBOL = (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; })("webpack logger times"); +var TIMERS_AGGREGATES_SYMBOL = (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; })("webpack logger aggregated times"); + +var WebpackLogger = /*#__PURE__*/function () { + /** + * @param {function(LogTypeEnum, any[]=): void} log log function + * @param {function(string | function(): string): WebpackLogger} getChildLogger function to create child logger + */ + function WebpackLogger(log, getChildLogger) { + _classCallCheck(this, WebpackLogger); + + this[LOG_SYMBOL] = log; + this.getChildLogger = getChildLogger; + } + + _createClass(WebpackLogger, [{ + key: "error", + value: function error() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + this[LOG_SYMBOL](LogType.error, args); + } + }, { + key: "warn", + value: function warn() { + for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + this[LOG_SYMBOL](LogType.warn, args); + } + }, { + key: "info", + value: function info() { + for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { + args[_key3] = arguments[_key3]; + } + + this[LOG_SYMBOL](LogType.info, args); + } + }, { + key: "log", + value: function log() { + for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { + args[_key4] = arguments[_key4]; + } + + this[LOG_SYMBOL](LogType.log, args); + } + }, { + key: "debug", + value: function debug() { + for (var _len5 = arguments.length, args = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { + args[_key5] = arguments[_key5]; + } + + this[LOG_SYMBOL](LogType.debug, args); + } + }, { + key: "assert", + value: function assert(assertion) { + if (!assertion) { + for (var _len6 = arguments.length, args = new Array(_len6 > 1 ? _len6 - 1 : 0), _key6 = 1; _key6 < _len6; _key6++) { + args[_key6 - 1] = arguments[_key6]; + } + + this[LOG_SYMBOL](LogType.error, args); + } + } + }, { + key: "trace", + value: function trace() { + this[LOG_SYMBOL](LogType.trace, ["Trace"]); + } + }, { + key: "clear", + value: function clear() { + this[LOG_SYMBOL](LogType.clear); + } + }, { + key: "status", + value: function status() { + for (var _len7 = arguments.length, args = new Array(_len7), _key7 = 0; _key7 < _len7; _key7++) { + args[_key7] = arguments[_key7]; + } + + this[LOG_SYMBOL](LogType.status, args); + } + }, { + key: "group", + value: function group() { + for (var _len8 = arguments.length, args = new Array(_len8), _key8 = 0; _key8 < _len8; _key8++) { + args[_key8] = arguments[_key8]; + } + + this[LOG_SYMBOL](LogType.group, args); + } + }, { + key: "groupCollapsed", + value: function groupCollapsed() { + for (var _len9 = arguments.length, args = new Array(_len9), _key9 = 0; _key9 < _len9; _key9++) { + args[_key9] = arguments[_key9]; + } + + this[LOG_SYMBOL](LogType.groupCollapsed, args); + } + }, { + key: "groupEnd", + value: function groupEnd() { + for (var _len10 = arguments.length, args = new Array(_len10), _key10 = 0; _key10 < _len10; _key10++) { + args[_key10] = arguments[_key10]; + } + + this[LOG_SYMBOL](LogType.groupEnd, args); + } + }, { + key: "profile", + value: function profile(label) { + this[LOG_SYMBOL](LogType.profile, [label]); + } + }, { + key: "profileEnd", + value: function profileEnd(label) { + this[LOG_SYMBOL](LogType.profileEnd, [label]); + } + }, { + key: "time", + value: function time(label) { + this[TIMERS_SYMBOL] = this[TIMERS_SYMBOL] || new Map(); + this[TIMERS_SYMBOL].set(label, process.hrtime()); + } + }, { + key: "timeLog", + value: function timeLog(label) { + var prev = this[TIMERS_SYMBOL] && this[TIMERS_SYMBOL].get(label); + + if (!prev) { + throw new Error("No such label '".concat(label, "' for WebpackLogger.timeLog()")); + } + + var time = process.hrtime(prev); + this[LOG_SYMBOL](LogType.time, [label].concat(_toConsumableArray(time))); + } + }, { + key: "timeEnd", + value: function timeEnd(label) { + var prev = this[TIMERS_SYMBOL] && this[TIMERS_SYMBOL].get(label); + + if (!prev) { + throw new Error("No such label '".concat(label, "' for WebpackLogger.timeEnd()")); + } + + var time = process.hrtime(prev); + this[TIMERS_SYMBOL].delete(label); + this[LOG_SYMBOL](LogType.time, [label].concat(_toConsumableArray(time))); + } + }, { + key: "timeAggregate", + value: function timeAggregate(label) { + var prev = this[TIMERS_SYMBOL] && this[TIMERS_SYMBOL].get(label); + + if (!prev) { + throw new Error("No such label '".concat(label, "' for WebpackLogger.timeAggregate()")); + } + + var time = process.hrtime(prev); + this[TIMERS_SYMBOL].delete(label); + this[TIMERS_AGGREGATES_SYMBOL] = this[TIMERS_AGGREGATES_SYMBOL] || new Map(); + var current = this[TIMERS_AGGREGATES_SYMBOL].get(label); + + if (current !== undefined) { + if (time[1] + current[1] > 1e9) { + time[0] += current[0] + 1; + time[1] = time[1] - 1e9 + current[1]; + } else { + time[0] += current[0]; + time[1] += current[1]; + } + } + + this[TIMERS_AGGREGATES_SYMBOL].set(label, time); + } + }, { + key: "timeAggregateEnd", + value: function timeAggregateEnd(label) { + if (this[TIMERS_AGGREGATES_SYMBOL] === undefined) return; + var time = this[TIMERS_AGGREGATES_SYMBOL].get(label); + if (time === undefined) return; + this[LOG_SYMBOL](LogType.time, [label].concat(_toConsumableArray(time))); + } + }]); + + return WebpackLogger; +}(); + +exports.Logger = WebpackLogger; + +/***/ }), + +/***/ "./node_modules/webpack/lib/logging/createConsoleLogger.js": +/*!*****************************************************************!*\ + !*** ./node_modules/webpack/lib/logging/createConsoleLogger.js ***! + \*****************************************************************/ +/***/ (function(module, __unused_webpack_exports, __nested_webpack_require_10262__) { + +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + + +function _toConsumableArray(arr) { + return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); +} + +function _nonIterableSpread() { + throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); +} + +function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); +} + +function _iterableToArray(iter) { + if (typeof (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }) !== "undefined" && iter[(typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }).iterator] != null || iter["@@iterator"] != null) return Array.from(iter); +} + +function _arrayWithoutHoles(arr) { + if (Array.isArray(arr)) return _arrayLikeToArray(arr); +} + +function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; + + for (var i = 0, arr2 = new Array(len); i < len; i++) { + arr2[i] = arr[i]; + } + + return arr2; +} + +var _require = __nested_webpack_require_10262__(/*! ./Logger */ "./node_modules/webpack/lib/logging/Logger.js"), + LogType = _require.LogType; +/** @typedef {import("../../declarations/WebpackOptions").FilterItemTypes} FilterItemTypes */ + +/** @typedef {import("../../declarations/WebpackOptions").FilterTypes} FilterTypes */ + +/** @typedef {import("./Logger").LogTypeEnum} LogTypeEnum */ + +/** @typedef {function(string): boolean} FilterFunction */ + +/** + * @typedef {Object} LoggerConsole + * @property {function(): void} clear + * @property {function(): void} trace + * @property {(...args: any[]) => void} info + * @property {(...args: any[]) => void} log + * @property {(...args: any[]) => void} warn + * @property {(...args: any[]) => void} error + * @property {(...args: any[]) => void=} debug + * @property {(...args: any[]) => void=} group + * @property {(...args: any[]) => void=} groupCollapsed + * @property {(...args: any[]) => void=} groupEnd + * @property {(...args: any[]) => void=} status + * @property {(...args: any[]) => void=} profile + * @property {(...args: any[]) => void=} profileEnd + * @property {(...args: any[]) => void=} logTime + */ + +/** + * @typedef {Object} LoggerOptions + * @property {false|true|"none"|"error"|"warn"|"info"|"log"|"verbose"} level loglevel + * @property {FilterTypes|boolean} debug filter for debug logging + * @property {LoggerConsole} console the console to log to + */ + +/** + * @param {FilterItemTypes} item an input item + * @returns {FilterFunction} filter function + */ + + +var filterToFunction = function filterToFunction(item) { + if (typeof item === "string") { + var regExp = new RegExp("[\\\\/]".concat(item.replace( // eslint-disable-next-line no-useless-escape + /[-[\]{}()*+?.\\^$|]/g, "\\$&"), "([\\\\/]|$|!|\\?)")); + return function (ident) { + return regExp.test(ident); + }; + } + + if (item && typeof item === "object" && typeof item.test === "function") { + return function (ident) { + return item.test(ident); + }; + } + + if (typeof item === "function") { + return item; + } + + if (typeof item === "boolean") { + return function () { + return item; + }; + } +}; +/** + * @enum {number} + */ + + +var LogLevel = { + none: 6, + false: 6, + error: 5, + warn: 4, + info: 3, + log: 2, + true: 2, + verbose: 1 +}; +/** + * @param {LoggerOptions} options options object + * @returns {function(string, LogTypeEnum, any[]): void} logging function + */ + +module.exports = function (_ref) { + var _ref$level = _ref.level, + level = _ref$level === void 0 ? "info" : _ref$level, + _ref$debug = _ref.debug, + debug = _ref$debug === void 0 ? false : _ref$debug, + console = _ref.console; + var debugFilters = typeof debug === "boolean" ? [function () { + return debug; + }] : + /** @type {FilterItemTypes[]} */ + [].concat(debug).map(filterToFunction); + /** @type {number} */ + + var loglevel = LogLevel["".concat(level)] || 0; + /** + * @param {string} name name of the logger + * @param {LogTypeEnum} type type of the log entry + * @param {any[]} args arguments of the log entry + * @returns {void} + */ + + var logger = function logger(name, type, args) { + var labeledArgs = function labeledArgs() { + if (Array.isArray(args)) { + if (args.length > 0 && typeof args[0] === "string") { + return ["[".concat(name, "] ").concat(args[0])].concat(_toConsumableArray(args.slice(1))); + } else { + return ["[".concat(name, "]")].concat(_toConsumableArray(args)); + } + } else { + return []; + } + }; + + var debug = debugFilters.some(function (f) { + return f(name); + }); + + switch (type) { + case LogType.debug: + if (!debug) return; // eslint-disable-next-line node/no-unsupported-features/node-builtins + + if (typeof console.debug === "function") { + // eslint-disable-next-line node/no-unsupported-features/node-builtins + console.debug.apply(console, _toConsumableArray(labeledArgs())); + } else { + console.log.apply(console, _toConsumableArray(labeledArgs())); + } + + break; + + case LogType.log: + if (!debug && loglevel > LogLevel.log) return; + console.log.apply(console, _toConsumableArray(labeledArgs())); + break; + + case LogType.info: + if (!debug && loglevel > LogLevel.info) return; + console.info.apply(console, _toConsumableArray(labeledArgs())); + break; + + case LogType.warn: + if (!debug && loglevel > LogLevel.warn) return; + console.warn.apply(console, _toConsumableArray(labeledArgs())); + break; + + case LogType.error: + if (!debug && loglevel > LogLevel.error) return; + console.error.apply(console, _toConsumableArray(labeledArgs())); + break; + + case LogType.trace: + if (!debug) return; + console.trace(); + break; + + case LogType.groupCollapsed: + if (!debug && loglevel > LogLevel.log) return; + + if (!debug && loglevel > LogLevel.verbose) { + // eslint-disable-next-line node/no-unsupported-features/node-builtins + if (typeof console.groupCollapsed === "function") { + // eslint-disable-next-line node/no-unsupported-features/node-builtins + console.groupCollapsed.apply(console, _toConsumableArray(labeledArgs())); + } else { + console.log.apply(console, _toConsumableArray(labeledArgs())); + } + + break; + } + + // falls through + + case LogType.group: + if (!debug && loglevel > LogLevel.log) return; // eslint-disable-next-line node/no-unsupported-features/node-builtins + + if (typeof console.group === "function") { + // eslint-disable-next-line node/no-unsupported-features/node-builtins + console.group.apply(console, _toConsumableArray(labeledArgs())); + } else { + console.log.apply(console, _toConsumableArray(labeledArgs())); + } + + break; + + case LogType.groupEnd: + if (!debug && loglevel > LogLevel.log) return; // eslint-disable-next-line node/no-unsupported-features/node-builtins + + if (typeof console.groupEnd === "function") { + // eslint-disable-next-line node/no-unsupported-features/node-builtins + console.groupEnd(); + } + + break; + + case LogType.time: + { + if (!debug && loglevel > LogLevel.log) return; + var ms = args[1] * 1000 + args[2] / 1000000; + var msg = "[".concat(name, "] ").concat(args[0], ": ").concat(ms, " ms"); + + if (typeof console.logTime === "function") { + console.logTime(msg); + } else { + console.log(msg); + } + + break; + } + + case LogType.profile: + // eslint-disable-next-line node/no-unsupported-features/node-builtins + if (typeof console.profile === "function") { + // eslint-disable-next-line node/no-unsupported-features/node-builtins + console.profile.apply(console, _toConsumableArray(labeledArgs())); + } + + break; + + case LogType.profileEnd: + // eslint-disable-next-line node/no-unsupported-features/node-builtins + if (typeof console.profileEnd === "function") { + // eslint-disable-next-line node/no-unsupported-features/node-builtins + console.profileEnd.apply(console, _toConsumableArray(labeledArgs())); + } + + break; + + case LogType.clear: + if (!debug && loglevel > LogLevel.log) return; // eslint-disable-next-line node/no-unsupported-features/node-builtins + + if (typeof console.clear === "function") { + // eslint-disable-next-line node/no-unsupported-features/node-builtins + console.clear(); + } + + break; + + case LogType.status: + if (!debug && loglevel > LogLevel.info) return; + + if (typeof console.status === "function") { + if (args.length === 0) { + console.status(); + } else { + console.status.apply(console, _toConsumableArray(labeledArgs())); + } + } else { + if (args.length !== 0) { + console.info.apply(console, _toConsumableArray(labeledArgs())); + } + } + + break; + + default: + throw new Error("Unexpected LogType ".concat(type)); + } + }; + + return logger; +}; + +/***/ }), + +/***/ "./node_modules/webpack/lib/logging/runtime.js": +/*!*****************************************************!*\ + !*** ./node_modules/webpack/lib/logging/runtime.js ***! + \*****************************************************/ +/***/ (function(__unused_webpack_module, exports, __nested_webpack_require_20349__) { + +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + + +function _extends() { + _extends = Object.assign || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; + }; + + return _extends.apply(this, arguments); +} + +var SyncBailHook = __nested_webpack_require_20349__(/*! tapable/lib/SyncBailHook */ "./client-src/modules/logger/SyncBailHookFake.js"); + +var _require = __nested_webpack_require_20349__(/*! ./Logger */ "./node_modules/webpack/lib/logging/Logger.js"), + Logger = _require.Logger; + +var createConsoleLogger = __nested_webpack_require_20349__(/*! ./createConsoleLogger */ "./node_modules/webpack/lib/logging/createConsoleLogger.js"); +/** @type {createConsoleLogger.LoggerOptions} */ + + +var currentDefaultLoggerOptions = { + level: "info", + debug: false, + console: console +}; +var currentDefaultLogger = createConsoleLogger(currentDefaultLoggerOptions); +/** + * @param {string} name name of the logger + * @returns {Logger} a logger + */ + +exports.getLogger = function (name) { + return new Logger(function (type, args) { + if (exports.hooks.log.call(name, type, args) === undefined) { + currentDefaultLogger(name, type, args); + } + }, function (childName) { + return exports.getLogger("".concat(name, "/").concat(childName)); + }); +}; +/** + * @param {createConsoleLogger.LoggerOptions} options new options, merge with old options + * @returns {void} + */ + + +exports.configureDefaultLogger = function (options) { + _extends(currentDefaultLoggerOptions, options); + + currentDefaultLogger = createConsoleLogger(currentDefaultLoggerOptions); +}; + +exports.hooks = { + log: new SyncBailHook(["origin", "type", "args"]) +}; + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __nested_webpack_require_22465__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __nested_webpack_require_22465__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ !function() { +/******/ // define getter functions for harmony exports +/******/ __nested_webpack_require_22465__.d = function(exports, definition) { +/******/ for(var key in definition) { +/******/ if(__nested_webpack_require_22465__.o(definition, key) && !__nested_webpack_require_22465__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ }(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ !function() { +/******/ __nested_webpack_require_22465__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } +/******/ }(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ !function() { +/******/ // define __esModule on exports +/******/ __nested_webpack_require_22465__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ }(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. +!function() { +/*!********************************************!*\ + !*** ./client-src/modules/logger/index.js ***! + \********************************************/ +__nested_webpack_require_22465__.r(__webpack_exports__); +/* harmony export */ __nested_webpack_require_22465__.d(__webpack_exports__, { +/* harmony export */ "default": function() { return /* reexport default export from named module */ webpack_lib_logging_runtime_js__WEBPACK_IMPORTED_MODULE_0__; } +/* harmony export */ }); +/* harmony import */ var webpack_lib_logging_runtime_js__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_22465__(/*! webpack/lib/logging/runtime.js */ "./node_modules/webpack/lib/logging/runtime.js"); + +}(); +var __webpack_export_target__ = exports; +for(var i in __webpack_exports__) __webpack_export_target__[i] = __webpack_exports__[i]; +if(__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, "__esModule", { value: true }); +/******/ })() +; + +/***/ }), + +/***/ "./node_modules/webpack-dev-server/client/modules/strip-ansi/index.js": +/*!****************************************************************************!*\ + !*** ./node_modules/webpack-dev-server/client/modules/strip-ansi/index.js ***! + \****************************************************************************/ +/***/ (function(__unused_webpack_module, exports) { + +/******/ (function() { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ({ + +/***/ "./node_modules/strip-ansi/index.js": +/*!******************************************!*\ + !*** ./node_modules/strip-ansi/index.js ***! + \******************************************/ +/***/ (function(__unused_webpack___webpack_module__, __webpack_exports__, __nested_webpack_require_368__) { + +__nested_webpack_require_368__.r(__webpack_exports__); +/* harmony export */ __nested_webpack_require_368__.d(__webpack_exports__, { +/* harmony export */ "default": function() { return /* binding */ stripAnsi; } +/* harmony export */ }); +/* harmony import */ var ansi_regex__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_368__(/*! ansi-regex */ "./node_modules/strip-ansi/node_modules/ansi-regex/index.js"); + +function stripAnsi(string) { + if (typeof string !== 'string') { + throw new TypeError("Expected a `string`, got `".concat(typeof string, "`")); + } + + return string.replace((0,ansi_regex__WEBPACK_IMPORTED_MODULE_0__["default"])(), ''); +} + +/***/ }), + +/***/ "./node_modules/strip-ansi/node_modules/ansi-regex/index.js": +/*!******************************************************************!*\ + !*** ./node_modules/strip-ansi/node_modules/ansi-regex/index.js ***! + \******************************************************************/ +/***/ (function(__unused_webpack___webpack_module__, __webpack_exports__, __nested_webpack_require_1387__) { + +__nested_webpack_require_1387__.r(__webpack_exports__); +/* harmony export */ __nested_webpack_require_1387__.d(__webpack_exports__, { +/* harmony export */ "default": function() { return /* binding */ ansiRegex; } +/* harmony export */ }); +function ansiRegex() { + var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, + _ref$onlyFirst = _ref.onlyFirst, + onlyFirst = _ref$onlyFirst === void 0 ? false : _ref$onlyFirst; + + var pattern = ["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))'].join('|'); + return new RegExp(pattern, onlyFirst ? undefined : 'g'); +} + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __nested_webpack_require_2352__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __nested_webpack_require_2352__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ !function() { +/******/ // define getter functions for harmony exports +/******/ __nested_webpack_require_2352__.d = function(exports, definition) { +/******/ for(var key in definition) { +/******/ if(__nested_webpack_require_2352__.o(definition, key) && !__nested_webpack_require_2352__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ }(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ !function() { +/******/ __nested_webpack_require_2352__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } +/******/ }(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ !function() { +/******/ // define __esModule on exports +/******/ __nested_webpack_require_2352__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ }(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. +!function() { +/*!************************************************!*\ + !*** ./client-src/modules/strip-ansi/index.js ***! + \************************************************/ +__nested_webpack_require_2352__.r(__webpack_exports__); +/* harmony import */ var strip_ansi__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_2352__(/*! strip-ansi */ "./node_modules/strip-ansi/index.js"); + +/* harmony default export */ __webpack_exports__["default"] = (strip_ansi__WEBPACK_IMPORTED_MODULE_0__["default"]); +}(); +var __webpack_export_target__ = exports; +for(var i in __webpack_exports__) __webpack_export_target__[i] = __webpack_exports__[i]; +if(__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, "__esModule", { value: true }); +/******/ })() +; + +/***/ }), + +/***/ "./node_modules/webpack-dev-server/client/overlay.js": +/*!***********************************************************!*\ + !*** ./node_modules/webpack-dev-server/client/overlay.js ***! + \***********************************************************/ +/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "formatProblem": function() { return /* binding */ formatProblem; }, +/* harmony export */ "show": function() { return /* binding */ show; }, +/* harmony export */ "hide": function() { return /* binding */ hide; } +/* harmony export */ }); +/* harmony import */ var ansi_html_community__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ansi-html-community */ "./node_modules/ansi-html-community/index.js"); +/* harmony import */ var ansi_html_community__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(ansi_html_community__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var html_entities__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! html-entities */ "./node_modules/html-entities/lib/index.js"); +/* harmony import */ var html_entities__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(html_entities__WEBPACK_IMPORTED_MODULE_1__); +// The error overlay is inspired (and mostly copied) from Create React App (https://github.com/facebookincubator/create-react-app) +// They, in turn, got inspired by webpack-hot-middleware (https://github.com/glenjamin/webpack-hot-middleware). + + +var colors = { + reset: ["transparent", "transparent"], + black: "181818", + red: "E36049", + green: "B3CB74", + yellow: "FFD080", + blue: "7CAFC2", + magenta: "7FACCA", + cyan: "C3C2EF", + lightgrey: "EBE7E3", + darkgrey: "6D7891" +}; +var iframeContainerElement; +var containerElement; +var onLoadQueue = []; +ansi_html_community__WEBPACK_IMPORTED_MODULE_0___default().setColors(colors); + +function createContainer() { + iframeContainerElement = document.createElement("iframe"); + iframeContainerElement.id = "webpack-dev-server-client-overlay"; + iframeContainerElement.src = "about:blank"; + iframeContainerElement.style.position = "fixed"; + iframeContainerElement.style.left = 0; + iframeContainerElement.style.top = 0; + iframeContainerElement.style.right = 0; + iframeContainerElement.style.bottom = 0; + iframeContainerElement.style.width = "100vw"; + iframeContainerElement.style.height = "100vh"; + iframeContainerElement.style.border = "none"; + iframeContainerElement.style.zIndex = 9999999999; + + iframeContainerElement.onload = function () { + containerElement = iframeContainerElement.contentDocument.createElement("div"); + containerElement.id = "webpack-dev-server-client-overlay-div"; + containerElement.style.position = "fixed"; + containerElement.style.boxSizing = "border-box"; + containerElement.style.left = 0; + containerElement.style.top = 0; + containerElement.style.right = 0; + containerElement.style.bottom = 0; + containerElement.style.width = "100vw"; + containerElement.style.height = "100vh"; + containerElement.style.backgroundColor = "rgba(0, 0, 0, 0.85)"; + containerElement.style.color = "#E8E8E8"; + containerElement.style.fontFamily = "Menlo, Consolas, monospace"; + containerElement.style.fontSize = "large"; + containerElement.style.padding = "2rem"; + containerElement.style.lineHeight = "1.2"; + containerElement.style.whiteSpace = "pre-wrap"; + containerElement.style.overflow = "auto"; + var headerElement = document.createElement("span"); + headerElement.innerText = "Compiled with problems:"; + var closeButtonElement = document.createElement("button"); + closeButtonElement.innerText = "X"; + closeButtonElement.style.background = "transparent"; + closeButtonElement.style.border = "none"; + closeButtonElement.style.fontSize = "20px"; + closeButtonElement.style.fontWeight = "bold"; + closeButtonElement.style.color = "white"; + closeButtonElement.style.cursor = "pointer"; + closeButtonElement.style.cssFloat = "right"; + closeButtonElement.style.styleFloat = "right"; + closeButtonElement.addEventListener("click", function () { + hide(); + }); + containerElement.appendChild(headerElement); + containerElement.appendChild(closeButtonElement); + containerElement.appendChild(document.createElement("br")); + containerElement.appendChild(document.createElement("br")); + iframeContainerElement.contentDocument.body.appendChild(containerElement); + onLoadQueue.forEach(function (onLoad) { + onLoad(containerElement); + }); + onLoadQueue = []; + iframeContainerElement.onload = null; + }; + + document.body.appendChild(iframeContainerElement); +} + +function ensureOverlayExists(callback) { + if (containerElement) { + // Everything is ready, call the callback right away. + callback(containerElement); + return; + } + + onLoadQueue.push(callback); + + if (iframeContainerElement) { + return; + } + + createContainer(); +} // Successful compilation. + + +function hide() { + if (!iframeContainerElement) { + return; + } // Clean up and reset internal state. + + + document.body.removeChild(iframeContainerElement); + iframeContainerElement = null; + containerElement = null; +} + +function formatProblem(type, item) { + var header = type === "warning" ? "WARNING" : "ERROR"; + var body = ""; + + if (typeof item === "string") { + body += item; + } else { + var file = item.file || ""; // eslint-disable-next-line no-nested-ternary + + var moduleName = item.moduleName ? item.moduleName.indexOf("!") !== -1 ? "".concat(item.moduleName.replace(/^(\s|\S)*!/, ""), " (").concat(item.moduleName, ")") : "".concat(item.moduleName) : ""; + var loc = item.loc; + header += "".concat(moduleName || file ? " in ".concat(moduleName ? "".concat(moduleName).concat(file ? " (".concat(file, ")") : "") : file).concat(loc ? " ".concat(loc) : "") : ""); + body += item.message || ""; + } + + return { + header: header, + body: body + }; +} // Compilation with errors (e.g. syntax error or missing modules). + + +function show(type, messages) { + ensureOverlayExists(function () { + messages.forEach(function (message) { + var entryElement = document.createElement("div"); + var typeElement = document.createElement("span"); + + var _formatProblem = formatProblem(type, message), + header = _formatProblem.header, + body = _formatProblem.body; + + typeElement.innerText = header; + typeElement.style.color = "#".concat(colors.red); // Make it look similar to our terminal. + + var text = ansi_html_community__WEBPACK_IMPORTED_MODULE_0___default()((0,html_entities__WEBPACK_IMPORTED_MODULE_1__.encode)(body)); + var messageTextNode = document.createElement("div"); + messageTextNode.innerHTML = text; + entryElement.appendChild(typeElement); + entryElement.appendChild(document.createElement("br")); + entryElement.appendChild(document.createElement("br")); + entryElement.appendChild(messageTextNode); + entryElement.appendChild(document.createElement("br")); + entryElement.appendChild(document.createElement("br")); + containerElement.appendChild(entryElement); + }); + }); +} + + + +/***/ }), + +/***/ "./node_modules/webpack-dev-server/client/socket.js": +/*!**********************************************************!*\ + !*** ./node_modules/webpack-dev-server/client/socket.js ***! + \**********************************************************/ +/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _clients_WebSocketClient_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./clients/WebSocketClient.js */ "./node_modules/webpack-dev-server/client/clients/WebSocketClient.js"); +/* provided dependency */ var __webpack_dev_server_client__ = __webpack_require__(/*! ./node_modules/webpack-dev-server/client/clients/WebSocketClient.js */ "./node_modules/webpack-dev-server/client/clients/WebSocketClient.js"); +/* global __webpack_dev_server_client__ */ + // this WebsocketClient is here as a default fallback, in case the client is not injected + +/* eslint-disable camelcase */ + +var Client = // eslint-disable-next-line camelcase, no-nested-ternary +typeof __webpack_dev_server_client__ !== "undefined" ? // eslint-disable-next-line camelcase +typeof __webpack_dev_server_client__.default !== "undefined" ? __webpack_dev_server_client__.default : __webpack_dev_server_client__ : _clients_WebSocketClient_js__WEBPACK_IMPORTED_MODULE_0__["default"]; +/* eslint-enable camelcase */ + +var retries = 0; +var client = null; + +var socket = function initSocket(url, handlers) { + client = new Client(url); + client.onOpen(function () { + retries = 0; + }); + client.onClose(function () { + if (retries === 0) { + handlers.close(); + } // Try to reconnect. + + + client = null; // After 10 retries stop trying, to prevent logspam. + + if (retries <= 10) { + // Exponentially increase timeout to reconnect. + // Respectfully copied from the package `got`. + // eslint-disable-next-line no-mixed-operators, no-restricted-properties + var retryInMs = 1000 * Math.pow(2, retries) + Math.random() * 100; + retries += 1; + setTimeout(function () { + socket(url, handlers); + }, retryInMs); + } + }); + client.onMessage(function (data) { + var message = JSON.parse(data); + + if (handlers[message.type]) { + handlers[message.type](message.data); + } + }); +}; + +/* harmony default export */ __webpack_exports__["default"] = (socket); + +/***/ }), + +/***/ "./node_modules/webpack-dev-server/client/utils/createSocketURL.js": +/*!*************************************************************************!*\ + !*** ./node_modules/webpack-dev-server/client/utils/createSocketURL.js ***! + \*************************************************************************/ +/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var url__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! url */ "./node_modules/url/url.js"); + // We handle legacy API that is Node.js specific, and a newer API that implements the same WHATWG URL Standard used by web browsers +// Please look at https://nodejs.org/api/url.html#url_url_strings_and_url_objects + +function createSocketURL(parsedURL) { + var hostname = parsedURL.hostname; // Node.js module parses it as `::` + // `new URL(urlString, [baseURLstring])` parses it as '[::]' + + var isInAddrAny = hostname === "0.0.0.0" || hostname === "::" || hostname === "[::]"; // why do we need this check? + // hostname n/a for file protocol (example, when using electron, ionic) + // see: https://github.com/webpack/webpack-dev-server/pull/384 + + if (isInAddrAny && self.location.hostname && self.location.protocol.indexOf("http") === 0) { + hostname = self.location.hostname; + } + + var socketURLProtocol = parsedURL.protocol || self.location.protocol; // When https is used in the app, secure web sockets are always necessary because the browser doesn't accept non-secure web sockets. + + if (socketURLProtocol === "auto:" || hostname && isInAddrAny && self.location.protocol === "https:") { + socketURLProtocol = self.location.protocol; + } + + socketURLProtocol = socketURLProtocol.replace(/^(?:http|.+-extension|file)/i, "ws"); + var socketURLAuth = ""; // `new URL(urlString, [baseURLstring])` doesn't have `auth` property + // Parse authentication credentials in case we need them + + if (parsedURL.username) { + socketURLAuth = parsedURL.username; // Since HTTP basic authentication does not allow empty username, + // we only include password if the username is not empty. + + if (parsedURL.password) { + // Result: : + socketURLAuth = socketURLAuth.concat(":", parsedURL.password); + } + } // In case the host is a raw IPv6 address, it can be enclosed in + // the brackets as the brackets are needed in the final URL string. + // Need to remove those as url.format blindly adds its own set of brackets + // if the host string contains colons. That would lead to non-working + // double brackets (e.g. [[::]]) host + // + // All of these web socket url params are optionally passed in through resourceQuery, + // so we need to fall back to the default if they are not provided + + + var socketURLHostname = (hostname || self.location.hostname || "localhost").replace(/^\[(.*)\]$/, "$1"); + var socketURLPort = parsedURL.port; + + if (!socketURLPort || socketURLPort === "0") { + socketURLPort = self.location.port; + } // If path is provided it'll be passed in via the resourceQuery as a + // query param so it has to be parsed out of the querystring in order for the + // client to open the socket to the correct location. + + + var socketURLPathname = "/ws"; + + if (parsedURL.pathname && !parsedURL.fromCurrentScript) { + socketURLPathname = parsedURL.pathname; + } + + return url__WEBPACK_IMPORTED_MODULE_0__.format({ + protocol: socketURLProtocol, + auth: socketURLAuth, + hostname: socketURLHostname, + port: socketURLPort, + pathname: socketURLPathname, + slashes: true + }); +} + +/* harmony default export */ __webpack_exports__["default"] = (createSocketURL); + +/***/ }), + +/***/ "./node_modules/webpack-dev-server/client/utils/getCurrentScriptSource.js": +/*!********************************************************************************!*\ + !*** ./node_modules/webpack-dev-server/client/utils/getCurrentScriptSource.js ***! + \********************************************************************************/ +/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +function getCurrentScriptSource() { + // `document.currentScript` is the most accurate way to find the current script, + // but is not supported in all browsers. + if (document.currentScript) { + return document.currentScript.getAttribute("src"); + } // Fallback to getting all scripts running in the document. + + + var scriptElements = document.scripts || []; + var scriptElementsWithSrc = Array.prototype.filter.call(scriptElements, function (element) { + return element.getAttribute("src"); + }); + + if (scriptElementsWithSrc.length > 0) { + var currentScript = scriptElementsWithSrc[scriptElementsWithSrc.length - 1]; + return currentScript.getAttribute("src"); + } // Fail as there was no script to use. + + + throw new Error("[webpack-dev-server] Failed to get current script source."); +} + +/* harmony default export */ __webpack_exports__["default"] = (getCurrentScriptSource); + +/***/ }), + +/***/ "./node_modules/webpack-dev-server/client/utils/log.js": +/*!*************************************************************!*\ + !*** ./node_modules/webpack-dev-server/client/utils/log.js ***! + \*************************************************************/ +/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "log": function() { return /* binding */ log; }, +/* harmony export */ "setLogLevel": function() { return /* binding */ setLogLevel; } +/* harmony export */ }); +/* harmony import */ var _modules_logger_index_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../modules/logger/index.js */ "./node_modules/webpack-dev-server/client/modules/logger/index.js"); +/* harmony import */ var _modules_logger_index_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_modules_logger_index_js__WEBPACK_IMPORTED_MODULE_0__); + +var name = "webpack-dev-server"; // default level is set on the client side, so it does not need +// to be set by the CLI or API + +var defaultLevel = "info"; + +function setLogLevel(level) { + _modules_logger_index_js__WEBPACK_IMPORTED_MODULE_0___default().configureDefaultLogger({ + level: level + }); +} + +setLogLevel(defaultLevel); +var log = _modules_logger_index_js__WEBPACK_IMPORTED_MODULE_0___default().getLogger(name); + + +/***/ }), + +/***/ "./node_modules/webpack-dev-server/client/utils/parseURL.js": +/*!******************************************************************!*\ + !*** ./node_modules/webpack-dev-server/client/utils/parseURL.js ***! + \******************************************************************/ +/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var url__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! url */ "./node_modules/url/url.js"); +/* harmony import */ var _getCurrentScriptSource_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./getCurrentScriptSource.js */ "./node_modules/webpack-dev-server/client/utils/getCurrentScriptSource.js"); + + + +function parseURL(resourceQuery) { + var options = {}; + + if (typeof resourceQuery === "string" && resourceQuery !== "") { + var searchParams = resourceQuery.substr(1).split("&"); + + for (var i = 0; i < searchParams.length; i++) { + var pair = searchParams[i].split("="); + options[pair[0]] = decodeURIComponent(pair[1]); + } + } else { + // Else, get the url from the - - - diff --git a/htdocs/legal-notice.html b/htdocs/legal-notice.html deleted file mode 100644 index 7b6c2bc..0000000 --- a/htdocs/legal-notice.html +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - Legal Notice | Demo | Armand Philippot - - - - -
- - -
-
-
- -

- Armand Philippot -

-

Front-end developer

-
- -
-
- -
- - - - - diff --git a/htdocs/license.html b/htdocs/license.html deleted file mode 100644 index 15ea458..0000000 --- a/htdocs/license.html +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - License | Demo | Armand Philippot - - - - -
- - -
-
-
- -

- Armand Philippot -

-

Front-end developer

-
- -
-
- -
- - - - - diff --git a/htdocs/mentions-legales.html b/htdocs/mentions-legales.html deleted file mode 100644 index ebfdf52..0000000 --- a/htdocs/mentions-legales.html +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - Mentions légales | Demo | Armand Philippot - - - - -
- - -
-
-
- -

- Armand Philippot -

-

Front-end developer

-
- -
-
- -
- - - - - diff --git a/htdocs/projects/js-small-apps b/htdocs/projects/js-small-apps deleted file mode 160000 index e06cb6d..0000000 --- a/htdocs/projects/js-small-apps +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e06cb6d423997411f56897ded251913c5b9568ef diff --git a/htdocs/projects/react-small-apps b/htdocs/projects/react-small-apps deleted file mode 160000 index d3dd2db..0000000 --- a/htdocs/projects/react-small-apps +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d3dd2db1c46a2bf4d1f649536219f9d0ce9ced71 diff --git a/htdocs/src/fonts/Inter/Inter.woff2 b/htdocs/src/fonts/Inter/Inter.woff2 deleted file mode 100644 index 365eedc..0000000 Binary files a/htdocs/src/fonts/Inter/Inter.woff2 and /dev/null differ diff --git a/htdocs/src/fonts/Inter/LICENSE.txt b/htdocs/src/fonts/Inter/LICENSE.txt deleted file mode 100644 index ff80f8c..0000000 --- a/htdocs/src/fonts/Inter/LICENSE.txt +++ /dev/null @@ -1,94 +0,0 @@ -Copyright (c) 2016-2020 The Inter Project Authors. -"Inter" is trademark of Rasmus Andersson. -https://github.com/rsms/inter - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION AND CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/htdocs/src/fonts/Kanit/Kanit-Bold.woff b/htdocs/src/fonts/Kanit/Kanit-Bold.woff deleted file mode 100644 index 7602cee..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-Bold.woff and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/Kanit-Bold.woff2 b/htdocs/src/fonts/Kanit/Kanit-Bold.woff2 deleted file mode 100644 index 8494234..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-Bold.woff2 and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/Kanit-BoldItalic.woff b/htdocs/src/fonts/Kanit/Kanit-BoldItalic.woff deleted file mode 100644 index af36319..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-BoldItalic.woff and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/Kanit-BoldItalic.woff2 b/htdocs/src/fonts/Kanit/Kanit-BoldItalic.woff2 deleted file mode 100644 index 61f04ef..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-BoldItalic.woff2 and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/Kanit-Italic.woff b/htdocs/src/fonts/Kanit/Kanit-Italic.woff deleted file mode 100644 index 462f295..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-Italic.woff and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/Kanit-Italic.woff2 b/htdocs/src/fonts/Kanit/Kanit-Italic.woff2 deleted file mode 100644 index 1723709..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-Italic.woff2 and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/Kanit-Light.woff b/htdocs/src/fonts/Kanit/Kanit-Light.woff deleted file mode 100644 index 872ce1b..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-Light.woff and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/Kanit-Light.woff2 b/htdocs/src/fonts/Kanit/Kanit-Light.woff2 deleted file mode 100644 index 6b35eda..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-Light.woff2 and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/Kanit-LightItalic.woff b/htdocs/src/fonts/Kanit/Kanit-LightItalic.woff deleted file mode 100644 index b81b7b3..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-LightItalic.woff and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/Kanit-LightItalic.woff2 b/htdocs/src/fonts/Kanit/Kanit-LightItalic.woff2 deleted file mode 100644 index f933a2c..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-LightItalic.woff2 and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/Kanit-Medium.woff b/htdocs/src/fonts/Kanit/Kanit-Medium.woff deleted file mode 100644 index d5b3510..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-Medium.woff and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/Kanit-Medium.woff2 b/htdocs/src/fonts/Kanit/Kanit-Medium.woff2 deleted file mode 100644 index a012e98..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-Medium.woff2 and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/Kanit-MediumItalic.woff b/htdocs/src/fonts/Kanit/Kanit-MediumItalic.woff deleted file mode 100644 index 6859f58..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-MediumItalic.woff and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/Kanit-MediumItalic.woff2 b/htdocs/src/fonts/Kanit/Kanit-MediumItalic.woff2 deleted file mode 100644 index 8af9298..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-MediumItalic.woff2 and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/Kanit-Regular.woff b/htdocs/src/fonts/Kanit/Kanit-Regular.woff deleted file mode 100644 index eec0b42..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-Regular.woff and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/Kanit-Regular.woff2 b/htdocs/src/fonts/Kanit/Kanit-Regular.woff2 deleted file mode 100644 index 9b925bf..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-Regular.woff2 and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/Kanit-SemiBold.woff b/htdocs/src/fonts/Kanit/Kanit-SemiBold.woff deleted file mode 100644 index dacdb5b..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-SemiBold.woff and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/Kanit-SemiBold.woff2 b/htdocs/src/fonts/Kanit/Kanit-SemiBold.woff2 deleted file mode 100644 index 52d4527..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-SemiBold.woff2 and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/Kanit-SemiBoldItalic.woff b/htdocs/src/fonts/Kanit/Kanit-SemiBoldItalic.woff deleted file mode 100644 index ebf0137..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-SemiBoldItalic.woff and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/Kanit-SemiBoldItalic.woff2 b/htdocs/src/fonts/Kanit/Kanit-SemiBoldItalic.woff2 deleted file mode 100644 index 300d77a..0000000 Binary files a/htdocs/src/fonts/Kanit/Kanit-SemiBoldItalic.woff2 and /dev/null differ diff --git a/htdocs/src/fonts/Kanit/OFL.txt b/htdocs/src/fonts/Kanit/OFL.txt deleted file mode 100644 index 0f48ea4..0000000 --- a/htdocs/src/fonts/Kanit/OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2020 The Kanit Project Authors (https://github.com/cadsondemak/kanit) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/htdocs/src/images/armand-philippot-logo.svg b/htdocs/src/images/armand-philippot-logo.svg deleted file mode 100644 index 17a245e..0000000 --- a/htdocs/src/images/armand-philippot-logo.svg +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/htdocs/src/images/armand-philippot.jpg b/htdocs/src/images/armand-philippot.jpg deleted file mode 100644 index 2c8ef50..0000000 Binary files a/htdocs/src/images/armand-philippot.jpg and /dev/null differ diff --git a/htdocs/src/images/cc-by-sa.svg b/htdocs/src/images/cc-by-sa.svg deleted file mode 100644 index b1fc892..0000000 --- a/htdocs/src/images/cc-by-sa.svg +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - image/svg+xml - - - - - - - - - - - - - diff --git a/htdocs/src/images/favicon/android-chrome-192x192.png b/htdocs/src/images/favicon/android-chrome-192x192.png deleted file mode 100644 index 2def6f5..0000000 Binary files a/htdocs/src/images/favicon/android-chrome-192x192.png and /dev/null differ diff --git a/htdocs/src/images/favicon/android-chrome-512x512.png b/htdocs/src/images/favicon/android-chrome-512x512.png deleted file mode 100644 index 514e060..0000000 Binary files a/htdocs/src/images/favicon/android-chrome-512x512.png and /dev/null differ diff --git a/htdocs/src/images/favicon/apple-touch-icon.png b/htdocs/src/images/favicon/apple-touch-icon.png deleted file mode 100644 index e4b7ae0..0000000 Binary files a/htdocs/src/images/favicon/apple-touch-icon.png and /dev/null differ diff --git a/htdocs/src/images/favicon/browserconfig.xml b/htdocs/src/images/favicon/browserconfig.xml deleted file mode 100644 index f9c2e67..0000000 --- a/htdocs/src/images/favicon/browserconfig.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - #2b5797 - - - diff --git a/htdocs/src/images/favicon/favicon-16x16.png b/htdocs/src/images/favicon/favicon-16x16.png deleted file mode 100644 index 525a1b3..0000000 Binary files a/htdocs/src/images/favicon/favicon-16x16.png and /dev/null differ diff --git a/htdocs/src/images/favicon/favicon-32x32.png b/htdocs/src/images/favicon/favicon-32x32.png deleted file mode 100644 index c4b80ad..0000000 Binary files a/htdocs/src/images/favicon/favicon-32x32.png and /dev/null differ diff --git a/htdocs/src/images/favicon/favicon.ico b/htdocs/src/images/favicon/favicon.ico deleted file mode 100644 index d1d41a8..0000000 Binary files a/htdocs/src/images/favicon/favicon.ico and /dev/null differ diff --git a/htdocs/src/images/favicon/mstile-144x144.png b/htdocs/src/images/favicon/mstile-144x144.png deleted file mode 100644 index f68a989..0000000 Binary files a/htdocs/src/images/favicon/mstile-144x144.png and /dev/null differ diff --git a/htdocs/src/images/favicon/mstile-150x150.png b/htdocs/src/images/favicon/mstile-150x150.png deleted file mode 100644 index b36424a..0000000 Binary files a/htdocs/src/images/favicon/mstile-150x150.png and /dev/null differ diff --git a/htdocs/src/images/favicon/mstile-310x150.png b/htdocs/src/images/favicon/mstile-310x150.png deleted file mode 100644 index f395692..0000000 Binary files a/htdocs/src/images/favicon/mstile-310x150.png and /dev/null differ diff --git a/htdocs/src/images/favicon/mstile-310x310.png b/htdocs/src/images/favicon/mstile-310x310.png deleted file mode 100644 index 3c7fd66..0000000 Binary files a/htdocs/src/images/favicon/mstile-310x310.png and /dev/null differ diff --git a/htdocs/src/images/favicon/mstile-70x70.png b/htdocs/src/images/favicon/mstile-70x70.png deleted file mode 100644 index 12d226b..0000000 Binary files a/htdocs/src/images/favicon/mstile-70x70.png and /dev/null differ diff --git a/htdocs/src/images/favicon/safari-pinned-tab.svg b/htdocs/src/images/favicon/safari-pinned-tab.svg deleted file mode 100644 index 2354f78..0000000 --- a/htdocs/src/images/favicon/safari-pinned-tab.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - - -Created by potrace 1.11, written by Peter Selinger 2001-2013 - - - - - - - diff --git a/htdocs/src/images/favicon/site.webmanifest b/htdocs/src/images/favicon/site.webmanifest deleted file mode 100644 index 31df508..0000000 --- a/htdocs/src/images/favicon/site.webmanifest +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "Armand Philippot", - "short_name": "AP", - "icons": [ - { - "src": "/wp-content/themes/apcom/assets/images/favicon/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/wp-content/themes/apcom/assets/images/favicon/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "theme_color": "#194476", - "background_color": "#194476", - "display": "standalone" -} diff --git a/htdocs/src/images/github.svg b/htdocs/src/images/github.svg deleted file mode 100644 index a39f6d2..0000000 --- a/htdocs/src/images/github.svg +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - image/svg+xml - - - - - - - diff --git a/htdocs/src/images/gitlab.svg b/htdocs/src/images/gitlab.svg deleted file mode 100644 index 5a0ce62..0000000 --- a/htdocs/src/images/gitlab.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - image/svg+xml - - - - - - - - - - - - - diff --git a/htdocs/src/js/app.js b/htdocs/src/js/app.js deleted file mode 100644 index 38aee0e..0000000 --- a/htdocs/src/js/app.js +++ /dev/null @@ -1,382 +0,0 @@ -import projects from './config/projects'; -import { - translate, - setLocale, - currentLocale, - supportedLanguages, -} from './i18n/i18n'; -import { - hideToBottom, - hideToLeft, - showFromBottom, - showFromLeft, -} from './utilities/animations'; -import { isSmallVw, isStyleJsExists } from './utilities/helpers'; - -/** - * Show/hide header and footer with slide animation (left). - */ -function toggleHeaderFooter() { - const header = document.querySelector('header'); - const footer = document.querySelector('footer'); - const elements = [header, footer]; - - elements.forEach((el) => { - if (el.classList.contains('hide')) { - showFromLeft(el); - } else { - hideToLeft(el); - } - }); -} - -/** - * Show/hide project details with slide animation (bottom). - */ -function toggleProjectDetails() { - const details = document.querySelector('.project-details'); - - if (details.classList.contains('hide')) { - showFromBottom(details); - } else { - hideToBottom(details); - } -} - -/** - * Add an event listener to show or hide header and footer. - */ -function listenMenuBtn() { - const menuBtn = document.querySelector('.btn--menu'); - menuBtn.addEventListener('click', toggleHeaderFooter); -} - -/** - * Show or hide the project details button depending on current location. - */ -function toggleProjectDetailsBtn() { - const button = document.querySelector('.btn--details'); - const currentPath = window.location.hash; - - if (currentPath) { - button.style.display = ''; - } else { - button.style.display = 'none'; - } -} - -/** - * Update the visibility of some DOM elements depending on viewport. - */ -function updateView() { - const header = document.querySelector('header'); - const footer = document.querySelector('footer'); - const toolbar = document.querySelector('.toolbar'); - const details = document.querySelector('.project-details'); - - if (isSmallVw()) { - header.classList.add('hide'); - footer.classList.add('hide'); - toolbar.classList.remove('hide'); - details?.classList.add('hide'); - details?.classList.remove('fade-in'); - } else { - showFromLeft(header); - showFromLeft(footer); - toolbar.classList.add('hide'); - details?.classList.remove('hide'); - details?.classList.add('fade-in'); - } - - toggleProjectDetailsBtn(); -} - -/** - * Update view when the window size changes. - */ -function listenWindowSize() { - window.addEventListener('resize', updateView); -} - -/** - * Retrieve a project by id. - * @param {Integer} id - The project id. - * @returns {Object} The current project. - */ -function getCurrentProject(id) { - return projects.find((project) => project.id === id); -} - -/** - * Get a list item for the given repo. - * @param {String} name - The repository name. - * @param {String} url - The repository URL. - * @returns {HTMLElement} A list item. - */ -function getRepoItem(name, url) { - const item = document.createElement('li'); - const link = document.createElement('a'); - const span = document.createElement('span'); - span.classList.add('screen-reader-text'); - span.textContent = name; - link.classList.add('list__link', `list__link--${name.toLocaleLowerCase()}`); - link.href = url; - link.appendChild(span); - item.classList.add('list__item'); - item.appendChild(link); - return item; -} - -/** - * Get the repos list wrapped inside ul element and the title. - * @param {Object[]} repos - An array of repo with name and URL. - * @returns {[title, list]} An array of HTMLElements for title and list. - */ -function getRepos(repos) { - if (repos.length === 0) return []; - - const wrapper = document.createElement('div'); - const title = document.createElement('h3'); - const list = document.createElement('ul'); - const items = repos.map((repo) => getRepoItem(repo.name, repo.url)); - title.classList.add('project-details__title'); - title.textContent = translate('main.project.details.repo', { - count: repos.length, - }); - list.classList.add('list', 'list--repos'); - list.append(...items); - wrapper.append(title, list); - return [title, list]; -} - -/** - * Get the technologies list wrapped inside ul element and the title. - * @param {String[]} technologies - An array of technology names. - * @returns {[title, list]} An array of HTMLElements for title and list. - */ -function getTechs(technologies) { - if (technologies.length === 0) return []; - - const title = document.createElement('h3'); - title.classList.add('project-details__title'); - title.textContent = translate('main.project.details.tech', { - count: technologies.length, - }); - const list = document.createElement('ul'); - const items = technologies.map((technology) => { - const item = document.createElement('li'); - item.textContent = technology; - return item; - }); - list.classList.add('list', 'list--tech'); - list.append(...items); - return [title, list]; -} - -/** - * Retrieve the project details. - * @param {Object} project - The project. - * @returns {HTMLElement} The project details wrapped in a div. - */ -function getProjectDetails(project) { - const details = document.createElement('div'); - const title = document.createElement('h2'); - const techList = project?.technologies ? getTechs(project.technologies) : []; - const reposList = getRepos(project.repo); - const locale = currentLocale(); - let description; - - if (project.description) { - description = document.createElement('div'); - description.classList.add('project-details__description'); - description.textContent = project.description[locale] || ''; - } else { - description = ''; - } - - title.classList.add('project-details__title'); - title.textContent = translate('main.project.details.about', { - name: project.name, - }); - details.classList.add('project-details'); - if (!isSmallVw()) details.classList.add('fade-in'); - details.replaceChildren(title, description, ...techList, ...reposList); - - return details; -} - -/** - * Get an iframe for the given path/url. - * @param {String} src - The path/url to use as source. - * @returns {HTMLElement} The iframe. - */ -function getIframe(src) { - const iframe = document.createElement('iframe'); - iframe.src = src; - return iframe; -} - -/** - * Retrieve the project preview. - * @param {String} projectPath - The project path. - * @returns {HTMLElement} The project preview wrapped in a div. - */ -function getProjectPreview(projectPath) { - const preview = document.createElement('div'); - const iframe = getIframe(projectPath); - preview.classList.add('project-preview', 'fade-in'); - preview.replaceChildren(iframe); - return preview; -} - -/** - * Display the targeted project. - * @param {String} id - The project id. - */ -function showProject(id) { - const currentProject = getCurrentProject(id); - const main = document.querySelector('.main'); - const details = getProjectDetails(currentProject); - const preview = getProjectPreview(currentProject.path); - const detailsBtn = document.querySelector('.btn--details'); - - if (isSmallVw()) details.classList.add('hide'); - - detailsBtn.textContent = translate('main.project.details.about', { - name: currentProject.name, - }); - detailsBtn.addEventListener('click', toggleProjectDetails); - window.history.pushState({}, currentProject.name, `/#${id}`); - document.title = `${currentProject.name} | Demo | Armand Philippot`; - main.replaceChildren(preview, details); -} - -/** - * Add a CSS class to the current project in projects nav. - * @param {String} id - The project id. - */ -function setSelectedProject(id) { - const links = document.querySelectorAll('.nav__link'); - links.forEach((link) => { - if (link.id === id) { - link.classList.add('nav__link--selected'); - } else { - link.classList.remove('nav__link--selected'); - } - }); -} - -/** - * Create a list item for a project. - * @param {String} id - The project id. - * @param {String} name - The project name. - * @returns {HTMLElement} The list item. - */ -function getProjectsNavItem(id, name) { - const item = document.createElement('li'); - const link = document.createElement('a'); - link.classList.add('nav__link'); - link.href = `/#${id}`; - link.id = id; - link.textContent = name; - link.addEventListener('click', (e) => { - e.preventDefault(); - showProject(id); - setSelectedProject(id); - toggleProjectDetailsBtn(); - if (isSmallVw()) toggleHeaderFooter(); - }); - item.classList.add('nav__item'); - item.appendChild(link); - return item; -} - -/** - * Print the list of available projects. - */ -function printProjectsNav() { - const ul = document.querySelector('.nav .nav__list'); - - projects.forEach((project) => { - const item = getProjectsNavItem(project.id, project.name); - ul.appendChild(item); - }); -} - -/** - * Add style.js script for development purposes. - */ -function loadWebpackStyles() { - if (isStyleJsExists()) { - const head = document.querySelector('head'); - const script = document.createElement('script'); - script.src = 'assets/js/style.js'; - head.appendChild(script); - } -} - -/** - * Load corresponding project if the requested page contains a hash. - */ -function printRequestedPage() { - const currentPath = window.location.hash; - - if (currentPath) { - const id = currentPath.replace('#', ''); - showProject(id); - setSelectedProject(id); - } -} - -/** - * Replace the legal notice link and text. - */ -function replaceLegalNoticeLink() { - const link = document.querySelector('.nav__link--legal'); - link.href = translate('footer.legalNotice.link'); - link.textContent = translate('footer.legalNotice.txt'); -} - -/** - * Translate all text available in HTML templates. - */ -function translateHTMLContent() { - const brandingDesc = document.querySelector('.branding__description'); - const navLabel = document.querySelector('.nav__label'); - const license = document.querySelector('.copyright__license'); - const instructions = document.querySelector('.instructions'); - brandingDesc.textContent = translate('branding.description'); - navLabel.textContent = translate('nav.title'); - license.title = translate('footer.license'); - if (instructions) instructions.textContent = translate('main.instructions'); -} - -/** - * Translate the website according to the user preferred language. - */ -function setAppLocale() { - const preferredLanguage = navigator.language; - const supportedLanguage = supportedLanguages.find( - (lang) => preferredLanguage.startsWith(lang.code) - // eslint-disable-next-line function-paren-newline -- Conflict with Prettier - ); - const locale = supportedLanguage?.code || 'en'; - setLocale(locale); -} - -/** - * Initialize the website with the projects list. - */ -function init() { - setAppLocale(); - translateHTMLContent(); - replaceLegalNoticeLink(); - loadWebpackStyles(); - printProjectsNav(); - updateView(); - listenWindowSize(); - listenMenuBtn(); - printRequestedPage(); -} - -init(); diff --git a/htdocs/src/js/config/projects.js b/htdocs/src/js/config/projects.js deleted file mode 100644 index 53f1af8..0000000 --- a/htdocs/src/js/config/projects.js +++ /dev/null @@ -1,224 +0,0 @@ -const projects = [ - { - id: 'bin2dec', - name: 'Bin2Dec', - description: { - en: 'Convert a binary string to a decimal number.', - fr: 'Convertit un nombre binaire en un nombre décimal.', - }, - path: './projects/js-small-apps/bin2dec/index.html', - repo: [ - { - name: 'Github', - url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/bin2dec', - }, - { - name: 'Gitlab', - url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/bin2dec', - }, - ], - technologies: ['Vanilla Javascript'], - }, - { - id: 'budget-app', - name: 'Budget App', - description: { - en: 'By selecting a language in the initialization form, only the currency is converted (the app is not translated). Also, no data is saved on page reload.', - fr: "En sélectionnant une langue dans le formulaire d'initialisation, seul le format des nombres change (l'application n'est pas traduite). Aucune donnée n'est conservée après rechargement de la page.", - }, - path: './projects/js-small-apps/budget-app/index.html', - repo: [ - { - name: 'Github', - url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/budget-app', - }, - { - name: 'Gitlab', - url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/budget-app', - }, - ], - technologies: ['Vanilla Javascript'], - }, - { - id: 'calculator', - name: 'Calculator', - description: { - en: 'A basic calculator. Decimal part is limited to 3 digits. The first part is limited to 8 digits. If the result does not respect these limits, you will see an error.', - fr: 'Une simple calculette. La partie décimale est limitée à 3 chiffres. La première partie est limitée à 8 chiffres. Si le résultat ne respecte pas ces limites, vous verrez une erreur.', - }, - path: './projects/js-small-apps/calculator/index.html', - repo: [ - { - name: 'Github', - url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/calculator', - }, - { - name: 'Gitlab', - url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/calculator', - }, - ], - technologies: ['Vanilla Javascript'], - }, - { - id: 'clock', - name: 'Clock', - description: { - en: 'What time is it? You can have the current hour in three formats: an analogic clock, a numeric display or a text.', - fr: "Quelle heure est-il ? Vous pouvez voir l'heure actuelle dans trois formats différents : une horloge analogique, un affichage numérique et sous forme de texte.", - }, - path: './projects/js-small-apps/clock/index.html', - repo: [ - { - name: 'Github', - url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/clock', - }, - { - name: 'Gitlab', - url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/clock', - }, - ], - technologies: ['Vanilla Javascript', 'SVG'], - }, - { - id: 'color-cycle', - name: 'Color cycle', - description: { - en: 'Play with hexadecimal colors. Set a color, then choose one or more increment values and start the preview.', - fr: "Jouez avec les couleurs hexadécimales. Définissez une couleur, puis choisissez une ou plusieurs valeurs d'incrémentation et démarrez l'aperçu.", - }, - path: './projects/js-small-apps/color-cycle/index.html', - repo: [ - { - name: 'Github', - url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/color-cycle', - }, - { - name: 'Gitlab', - url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/color-cycle', - }, - ], - technologies: ['Vanilla Javascript'], - }, - { - id: 'css-border-previewer', - name: 'CSS Border Previewer', - description: { - en: 'Play with CSS borders (style, width, radius). Then, you can copy the generated code if the preview suits you.', - fr: "Jouez avec les bordures CSS (style, largeur, radius). Ensuite, vous pouvez copier le code généré si l'aperçu vous satisfait.", - }, - path: './projects/js-small-apps/css-border-previewer/index.html', - repo: [ - { - name: 'Github', - url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/css-border-previewer', - }, - { - name: 'Gitlab', - url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/css-border-previewer', - }, - ], - technologies: ['Vanilla Javascript'], - }, - { - id: 'meme-generator', - name: 'Meme Generator', - description: { - en: 'Choose a random image, set one or more texts then position them. Your meme is ready!', - fr: 'Choisissez une image aléatoire, définissez un ou plusieurs textes et positionnez-les. Votre meme est prêt !', - }, - path: './projects/react-small-apps/meme-generator/build/index.html', - repo: [ - { - name: 'Github', - url: 'https://github.com/ArmandPhilippot/react-small-apps/tree/main/meme-generator', - }, - { - name: 'Gitlab', - url: 'https://gitlab.com/ArmandPhilippot/react-small-apps/-/tree/main/meme-generator', - }, - ], - technologies: ['React', 'Fetch'], - }, - { - id: 'notebook', - name: 'Notebook', - description: { - en: 'Create as many pages as you want and fill them. You can define a title and a body. Then you can easily navigate between your pages with the nav.', - fr: 'Créez autant de pages que vous le souhaitez et remplissez-les. Vous pouvez définir un titre et un corps de texte. Ensuite, vous pouvez facilement naviguer entre vos pages grâce à la navigation.', - }, - path: './projects/react-small-apps/notebook/build/', - repo: [ - { - name: 'Github', - url: 'https://github.com/ArmandPhilippot/react-small-apps/tree/main/notebook', - }, - { - name: 'Gitlab', - url: 'https://gitlab.com/ArmandPhilippot/react-small-apps/-/tree/main/notebook', - }, - ], - technologies: ['React', 'React router'], - }, - { - id: 'rps-game', - name: 'Rock Paper Scissors', - description: { - en: 'A basic implementation of the game. Try to beat your friend or the computer.', - fr: "Une implémentation du jeu. Essayez de battre votre ami ou l'ordinateur.", - }, - path: './projects/js-small-apps/rock-paper-scissors/index.html', - repo: [ - { - name: 'Github', - url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/rock-paper-scissors', - }, - { - name: 'Gitlab', - url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/rock-paper-scissors', - }, - ], - technologies: ['Vanilla Javascript'], - }, - { - id: 'todos', - name: 'Todos', - description: { - en: 'You can add, remove or mark as done your todos. For each todos, you can add some details in addition to the title.\n\nLogin: demo@email.com\nPassword: demo', - fr: 'Vous pouvez ajouter, supprimer ou marquer comme fait vos "todo". Pour chaque "todo", vous pouvez ajouter des détails en plus du titre.\n\nLogin : demo@email.com\nMot de passe : demo', - }, - path: './projects/react-small-apps/todos/build/', - repo: [ - { - name: 'Github', - url: 'https://github.com/ArmandPhilippot/react-small-apps/tree/main/todos', - }, - { - name: 'Gitlab', - url: 'https://gitlab.com/ArmandPhilippot/react-small-apps/-/tree/main/todos', - }, - ], - technologies: ['React', 'React router', 'Redux'], - }, - { - id: 'users-list', - name: 'Users list', - description: { - en: 'You can see a list of username. By clicking on it, the next column display information about the selected user.', - fr: "Vous pouvez voir une liste de noms d'utilisateur. En cliquant sur l'un d'eux, la colonne suivante affiche les informations à propos de cet utilisateur.", - }, - path: './projects/js-small-apps/users-list/index.html', - repo: [ - { - name: 'Github', - url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/users-list', - }, - { - name: 'Gitlab', - url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/users-list', - }, - ], - technologies: ['Vanilla Javascript', 'Fetch'], - }, -]; - -export default projects; diff --git a/htdocs/src/js/i18n/i18n.js b/htdocs/src/js/i18n/i18n.js deleted file mode 100644 index 6bdc7cd..0000000 --- a/htdocs/src/js/i18n/i18n.js +++ /dev/null @@ -1,42 +0,0 @@ -import I18n from 'i18n-js'; -import en from './locales/en'; -import fr from './locales/fr'; - -const supportedLanguages = [ - { - code: 'en', - label: 'English', - translations: en, - }, - { - code: 'fr', - label: 'Français', - translations: fr, - }, -]; - -supportedLanguages.forEach((locale) => { - I18n.translations[locale.code] = locale.translations; -}); - -function setLocale(locale) { - I18n.locale = locale; -} - -function currentLocale() { - return I18n.currentLocale(); -} - -function translate(name, params = {}) { - return I18n.t(name, params); -} - -const { defaultLocale } = I18n; - -export { - supportedLanguages, - setLocale, - translate, - defaultLocale, - currentLocale, -}; diff --git a/htdocs/src/js/i18n/locales/en.js b/htdocs/src/js/i18n/locales/en.js deleted file mode 100644 index 9717528..0000000 --- a/htdocs/src/js/i18n/locales/en.js +++ /dev/null @@ -1,36 +0,0 @@ -const en = { - branding: { - description: 'Front-end developer', - }, - nav: { - title: 'Apps list:', - }, - main: { - instructions: - 'Select an app inside menu to see a live preview and app details (description, technologies, repositories).', - project: { - details: { - about: 'About {{name}}', - repo: { - one: 'Repository:', - other: 'Repositories:', - zero: 'Repositories:', - }, - tech: { - one: 'Technology:', - other: 'Technologies:', - zero: 'Technologies:', - }, - }, - }, - }, - footer: { - legalNotice: { - txt: 'Legal notice', - link: 'legal-notice.html', - }, - license: 'License MIT', - }, -}; - -export default en; diff --git a/htdocs/src/js/i18n/locales/fr.js b/htdocs/src/js/i18n/locales/fr.js deleted file mode 100644 index 9c93012..0000000 --- a/htdocs/src/js/i18n/locales/fr.js +++ /dev/null @@ -1,36 +0,0 @@ -const fr = { - branding: { - description: 'Intégrateur web', - }, - nav: { - title: 'Liste des applications :', - }, - main: { - instructions: - "Sélectionnez une application dans le menu pour afficher un aperçu en direct et les informations sur l'application (description, technologies, dépôts).", - project: { - details: { - about: 'À propos de {{name}}', - repo: { - one: 'Dépôt :', - other: 'Dépôts :', - zero: 'Dépôt :', - }, - tech: { - one: 'Technologie :', - other: 'Technologies :', - zero: 'Technologie :', - }, - }, - }, - }, - footer: { - legalNotice: { - txt: 'Mentions légales', - link: 'mentions-legales.html', - }, - license: 'Licence MIT', - }, -}; - -export default fr; diff --git a/htdocs/src/js/utilities/animations.js b/htdocs/src/js/utilities/animations.js deleted file mode 100644 index 9a30685..0000000 --- a/htdocs/src/js/utilities/animations.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Change the element classes to hide it with a slide out left animation. - * @param {HTMLElement} el - The HTMLElement to hide. - */ -function hideToLeft(el) { - el?.classList.remove('slide-in--left'); - el?.classList.add('slide-out--left'); - setTimeout(() => { - el?.classList.add('hide'); - }, 800); -} - -/** - * Change the element classes to show it with a slide in left animation. - * @param {HTMLElement} el - The HTMLElement to show. - */ -function showFromLeft(el) { - el?.classList.remove('slide-out--left'); - el?.classList.remove('hide'); - el?.classList.add('slide-in--left'); -} - -/** - * Change the element classes to hide it with a slide out bottom animation. - * @param {HTMLElement} el - The HTMLElement to hide. - */ -function hideToBottom(el) { - el?.classList.remove('slide-in--up'); - el?.classList.add('slide-out--bottom'); - setTimeout(() => { - el?.classList.add('hide'); - }, 800); -} - -/** - * Change the element classes to show it with a slide in up animation. - * @param {HTMLElement} el - The HTMLElement to show. - */ -function showFromBottom(el) { - el?.classList.remove('slide-out--bottom'); - el?.classList.remove('hide'); - el?.classList.add('slide-in--up'); -} - -export { hideToLeft, showFromLeft, hideToBottom, showFromBottom }; diff --git a/htdocs/src/js/utilities/helpers.js b/htdocs/src/js/utilities/helpers.js deleted file mode 100644 index 470c49c..0000000 --- a/htdocs/src/js/utilities/helpers.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Check the size of the current viewport. - * @returns {Boolean} True if viewport lower than 1200px; false otherwise. - */ -function isSmallVw() { - return window.innerWidth < 1200; -} - -/** - * Check if /assets/styles.js exists (Webpack dev mode). - * @returns {Boolean} True if style.js exists ; false otherwise. - */ -async function isStyleJsExists() { - const filePath = 'assets/js/style.js'; - const response = await fetch(filePath); - return response.status === 200; -} - -export { isSmallVw, isStyleJsExists }; diff --git a/htdocs/src/scss/abstracts/_functions.scss b/htdocs/src/scss/abstracts/_functions.scss deleted file mode 100644 index 672e5e7..0000000 --- a/htdocs/src/scss/abstracts/_functions.scss +++ /dev/null @@ -1,4 +0,0 @@ -@forward "./functions/convert"; -@forward "./functions/css-vars"; -@forward "./functions/str-replace"; -@forward "./functions/encode"; diff --git a/htdocs/src/scss/abstracts/_mixins.scss b/htdocs/src/scss/abstracts/_mixins.scss deleted file mode 100644 index fd28631..0000000 --- a/htdocs/src/scss/abstracts/_mixins.scss +++ /dev/null @@ -1,2 +0,0 @@ -@forward "./mixins/css-vars"; -@forward "./mixins/media-queries"; diff --git a/htdocs/src/scss/abstracts/_placeholders.scss b/htdocs/src/scss/abstracts/_placeholders.scss deleted file mode 100644 index 079cae7..0000000 --- a/htdocs/src/scss/abstracts/_placeholders.scss +++ /dev/null @@ -1,32 +0,0 @@ -/// List Reset -%reset-list { - list-style-type: none; - margin: 0; - padding: 0; - - li { - margin-bottom: 0; - } -} - -/// Ordered List Reset -%reset-ordered-list { - @extend %reset-list; - - li { - counter-increment: none; - display: list-item; - - &::before { - display: none; - } - } -} - -/// Display an inline list with flexbox -%flex-list { - @extend %reset-list; - - display: flex; - flex-flow: row wrap; -} diff --git a/htdocs/src/scss/abstracts/_variables.scss b/htdocs/src/scss/abstracts/_variables.scss deleted file mode 100644 index a8a22a8..0000000 --- a/htdocs/src/scss/abstracts/_variables.scss +++ /dev/null @@ -1,103 +0,0 @@ -@use "sass:math"; -@use "../abstracts/functions" as fun; - -//=========================================================================== -// Ratios -//=========================================================================== - -/// Ratios map -/// @prop {String} keys - Keys are identifiers mapped to a given ratio -/// @prop {Map} value - Value is actual ratio -$ratios: ( - "minor-second": 1.067, - "major-second": 1.125, - "minor-third": 1.2, - "major-third": 1.25, - "perfect-fourth": 1.333, - "augmented-fourth": 1.414, - "perfect-fifth": 1.5, - "golden-number": 1.618, -); - -// Cannot declare the following function in partials due to module loop. -// Also, it will only be used in this file so it is not a problem. - -/// Get ratio -/// @param {String} $name - Ratio name. -/// @return {Integer} The ratio value. -@function get-ratio($name) { - @return map-get($ratios, $name); -} - -//=========================================================================== -// Layout -//=========================================================================== - -/// Breakpoints map -/// @prop {String} keys - Keys are identifiers mapped to a given length -/// @prop {Map} values - Values are actual breakpoints expressed in pixels -$breakpoints: ( - "xs": fun.convert-px(568, "em"), - "sm": fun.convert-px(768, "em"), - "md": fun.convert-px(1024, "em"), - "lg": fun.convert-px(1200, "em"), - "xl": fun.convert-px(1600, "em"), - "2xl": fun.convert-px(1920, "em"), -); - -//=========================================================================== -// Fonts -//=========================================================================== - -/* stylelint-disable -- Fonts name are not keywords, lowercase is not needed. */ -/// Regular font family -/// @type List -$font-family_primary: ("Inter", "Liberation Sans", Arial, sans-serif); - -/// Alternative regular font family -/// @type List -$font-family_secondary: ("Kanit", "Liberation Sans", Arial, sans-serif); - -$line-height: get-ratio("golden-number"); - -$font-size_base: 16px; -$font-size_base-rem: fun.convert-px(16); // font-size_base without unit -$font-size_ratio: get-ratio("minor-third"); -$font-size_sm: $font-size_base-rem * math.pow($font-size_ratio, -1); -$font-size_md: $font-size_base-rem * math.pow($font-size_ratio, 0); -$font-size_lg: $font-size_base-rem * math.pow($font-size_ratio, 1); -$font-size_xl: $font-size_base-rem * math.pow($font-size_ratio, 2); -$font-size_2xl: $font-size_base-rem * math.pow($font-size_ratio, 3); -$font-size_3xl: $font-size_base-rem * math.pow($font-size_ratio, 4); - -//============================================================================ -// Spacings -//============================================================================ - -$spacing_ratio: get-ratio("golden-number"); -$spacing_base: $spacing_ratio * 1rem; - -$spacing_3xs: math.div($spacing_base, 4); -$spacing_2xs: math.div($spacing_base, 3); -$spacing_xs: math.div($spacing_base, 2); -$spacing_sm: math.div($spacing_base, 1.5); -$spacing_md: $spacing_base; -$spacing_lg: $spacing_base * 1.5; - -//============================================================================ -// Colors -//============================================================================ - -$color_black-squeeze: hsl(212, 55%, 97%); -$color_catskill-white: hsl(212, 53%, 92%); -$color_link-water: hsl(212, 51%, 87%); -$color_geyser: hsl(212, 27%, 83%); -$color_gull-gray: hsl(212, 15%, 66%); -$color_pale-sky: hsl(212, 13%, 46%); -$color_nile-blue: hsl(212, 47%, 19%); -$color_firefly: hsl(212, 45%, 11%); -$color_chambray: hsl(212, 45%, 40%); -$color_chathams-blue: hsl(212, 65%, 28%); -$color_chathams-blue-light: hsl(212, 90%, 30%); -$color_chathams-blue-light-opacity-25: hsla(212, 90%, 30%, 0.25); -$color_chathams-blue-dark: hsl(212, 70%, 25%); diff --git a/htdocs/src/scss/abstracts/functions/_convert.scss b/htdocs/src/scss/abstracts/functions/_convert.scss deleted file mode 100644 index 9f51dc7..0000000 --- a/htdocs/src/scss/abstracts/functions/_convert.scss +++ /dev/null @@ -1,16 +0,0 @@ -@use "sass:math"; - -/// Convert px to rem or em. -/// @param {Number} $px Value in px -/// @param {String} $to Unit. Either "rem" or "em" -/// @param {Number} $standard 1rem (or 1em) = 16px -/// @return {Number} Value in rem or em -@function convert-px($px, $to: "rem", $standard: 16) { - @if $to == "rem" { - @return math.div($px, $standard) + 0rem; // stylelint-disable-line - } @else if $to == "em" { - @return math.div($px, $standard) + 0em; // stylelint-disable-line - } @else { - @error "`$to` must be either `rem` or `em`."; - } -} diff --git a/htdocs/src/scss/abstracts/functions/_css-vars.scss b/htdocs/src/scss/abstracts/functions/_css-vars.scss deleted file mode 100644 index 89e1a15..0000000 --- a/htdocs/src/scss/abstracts/functions/_css-vars.scss +++ /dev/null @@ -1,8 +0,0 @@ -/// Retrieve a CSS variable value with prefix -/// @see https://dev.to/felipperegazio/css-custom-properties-vars-with-sass-scss-a-practical-architecture-strategy-1m88 -/// @param {String} $name Variable name -/// @param {String} $prefix Variable prefix -/// @return {String} Variable in CSS format -@function get-var($name, $prefix: dap) { - @return var(--#{$prefix}-#{$name}); -} diff --git a/htdocs/src/scss/abstracts/functions/_encode.scss b/htdocs/src/scss/abstracts/functions/_encode.scss deleted file mode 100644 index 4350185..0000000 --- a/htdocs/src/scss/abstracts/functions/_encode.scss +++ /dev/null @@ -1,14 +0,0 @@ -@use "str-replace" as fun; - -/// Encode a SVG. -/// @param {String} $svg A complete svg (`...`). -/// @return The encoded svg, ready to use for background-image. -@function encode-svg($svg) { - $svg-encoding: (("<", "%3C"), (">", "%3E"), ("#", "%23")); - - @each $char, $encoded in $svg-encoding { - $svg: fun.str-replace($svg, $char, $encoded); - } - - @return "data:image/svg+xml;utf8," + $svg; -} diff --git a/htdocs/src/scss/abstracts/functions/_str-replace.scss b/htdocs/src/scss/abstracts/functions/_str-replace.scss deleted file mode 100644 index 624bf33..0000000 --- a/htdocs/src/scss/abstracts/functions/_str-replace.scss +++ /dev/null @@ -1,20 +0,0 @@ -/// Replace `$search` with `$replace` in `$string` -/// @author Hugo Giraudel -/// @param {String} $string - Initial string -/// @param {String} $search - Substring to replace -/// @param {String} $replace ('') - New value -/// @return {String} - Updated string -@function str-replace($string, $search, $replace: "") { - $index: str-index($string, $search); - - @if $index { - @return str-slice($string, 1, $index - 1) + $replace + - str-replace( - str-slice($string, $index + str-length($search)), - $search, - $replace - ); - } - - @return $string; -} diff --git a/htdocs/src/scss/abstracts/mixins/_css-vars.scss b/htdocs/src/scss/abstracts/mixins/_css-vars.scss deleted file mode 100644 index 8e31c96..0000000 --- a/htdocs/src/scss/abstracts/mixins/_css-vars.scss +++ /dev/null @@ -1,20 +0,0 @@ -/// Declare a set of CSS variables properly prefixed. -/// -/// @see https://dev.to/felipperegazio/css-custom-properties-vars-with-sass-scss-a-practical-architecture-strategy-1m88 -/// -/// @param {List} $variables - A list of variable name and value. -/// @param {Bool} $root - Set vars at root. -/// @param {String} $prefix - The variables prefix. -@mixin set-vars($variables, $root: true, $prefix: "dap") { - @if $root { - :root { - @each $name, $value in $variables { - --#{$prefix}-#{$name}: #{$value}; - } - } - } @else { - @each $name, $value in $variables { - --#{$prefix}-#{$name}: #{$value}; - } - } -} diff --git a/htdocs/src/scss/abstracts/mixins/_media-queries.scss b/htdocs/src/scss/abstracts/mixins/_media-queries.scss deleted file mode 100644 index fcaea4b..0000000 --- a/htdocs/src/scss/abstracts/mixins/_media-queries.scss +++ /dev/null @@ -1,81 +0,0 @@ -@use "../variables" as var; - -/// Media query: media type -/// @param {String} $type - Media type: all, screen, print, retina. -/// @example scss - `@media only screen` equivalent is: -/// @include media("screen"); -@mixin media($type) { - @if $type == "retina" { - $type: "(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi)"; - } @else if $type == "screen" or $type == "print" { - $type: "only #{$type}"; - } - - @media #{$type} { - @content; - } -} - -/// Media query: min-width / max-width -/// @param {String} $from - min-width breakpoint. -/// @param {String} $until - max-width breakpoint. -/// @example scss - `@media (min-width: "md")` equivalent is: -/// @include dimensions("md"); -@mixin dimensions($from: null, $until: null) { - $query: ""; - - @if $from { - @if type-of($from) == "string" { - $size: map-get(var.$breakpoints, $from); - $query: "(min-width: #{$size})"; - } @else { - @error "`$from` must be a string."; - } - } - - @if $from and $until { - $query: $query + " and "; - } - - @if $until { - @if type-of($until) == "string" { - $size: map-get(var.$breakpoints, $until); - $size: calc(#{$size} - 1px); - $query: $query + "(max-width: #{$size})"; - } @else { - @error "`$until` must be a string."; - } - } - - @media #{$query} { - @content; - } -} - -/// Media query: prefers-reduced-motion -/// @param {String} $value - Media query value: `no-preference` or `reduce`. -/// @example scss - @media (prefers-reduced-motion: "reduce") equivalent is: -/// @include motion("reduce"); -@mixin motion($value) { - @if $value == "no-preference" or $value == "reduce" { - @media (prefers-reduced-motion: #{$value}) { - @content; - } - } @else { - @error "Allowed values are `no-preference` and `reduce`."; - } -} - -/// Media query: any-pointer -/// @param {String} $value - Media query value: `fine`, `coarse` or `none`. -/// @example scss - @media (any-pointer: "fine") equivalent is: -/// @include pointer("fine"); -@mixin pointer($value) { - @if $value == "fine" or $value == "coarse" or $value == "none" { - @media (any-pointer: #{$value}) { - @content; - } - } @else { - @error "Allowed values are `fine`, `coarse` and `none`."; - } -} diff --git a/htdocs/src/scss/base/_animations.scss b/htdocs/src/scss/base/_animations.scss deleted file mode 100644 index a2df8ab..0000000 --- a/htdocs/src/scss/base/_animations.scss +++ /dev/null @@ -1,89 +0,0 @@ -@keyframes fadeIn { - 0% { - opacity: 0; - } - - 100% { - opacity: 1; - visibility: visible; - } -} - -@keyframes fadeOut { - 0% { - opacity: 1; - } - - 100% { - opacity: 0; - visibility: hidden; - } -} - -@keyframes slideInLeft { - 0% { - margin-left: -100%; - } - - 100% { - margin-left: 0; - visibility: visible; - } -} - -@keyframes slideOutLeft { - 0% { - margin-left: 0; - } - - 100% { - margin-left: -100%; - visibility: hidden; - } -} - -@keyframes slideInUp { - 0% { - margin-bottom: -100vh; - } - - 100% { - margin-bottom: 0; - visibility: visible; - } -} - -@keyframes slideOutBottom { - 0% { - margin-bottom: 0; - } - - 100% { - margin-bottom: -100vh; - visibility: hidden; - } -} - -.fade-in { - animation: fadeIn 1s; -} - -.fade-out { - animation: fadeOut 1s; -} - -.slide-in--left { - animation: slideInLeft 0.8s; -} - -.slide-out--left { - animation: slideOutLeft 0.8s; -} - -.slide-in--up { - animation: slideInUp 1s; -} - -.slide-out--bottom { - animation: slideOutBottom 1s; -} diff --git a/htdocs/src/scss/base/_base.scss b/htdocs/src/scss/base/_base.scss deleted file mode 100644 index a157a50..0000000 --- a/htdocs/src/scss/base/_base.scss +++ /dev/null @@ -1,3 +0,0 @@ -html { - overflow: hidden; -} diff --git a/htdocs/src/scss/base/_colors.scss b/htdocs/src/scss/base/_colors.scss deleted file mode 100644 index 6251b2e..0000000 --- a/htdocs/src/scss/base/_colors.scss +++ /dev/null @@ -1,21 +0,0 @@ -@use "../abstracts/mixins" as mix; -@use "../abstracts/variables" as var; -@include mix.set-vars( - ( - color-bg: #{var.$color_black-squeeze}, - color-bg-secondary: #{var.$color_catskill-white}, - color-bg-tertiary: #{var.$color_link-water}, - color-fg: #{var.$color_firefly}, - color-fg-inverted: #{var.$color_black-squeeze}, - color-border: #{var.$color_chathams-blue}, - color-border-light: #{var.$color_geyser}, - color-shadow-darker: #{var.$color_nile-blue}, - color-shadow-dark: #{var.$color_pale-sky}, - color-shadow: #{var.$color_chambray}, - color-shadow-light: #{var.$color_gull-gray}, - color-primary: #{var.$color_chathams-blue}, - color-primary-light: #{var.$color_chathams-blue-light}, - color-primary-light-opacity: #{var.$color_chathams-blue-light-opacity-25}, - color-primary-dark: #{var.$color_chathams-blue-dark}, - ) -); diff --git a/htdocs/src/scss/base/_fonts.scss b/htdocs/src/scss/base/_fonts.scss deleted file mode 100644 index 192f6b8..0000000 --- a/htdocs/src/scss/base/_fonts.scss +++ /dev/null @@ -1,115 +0,0 @@ -@use "../abstracts/functions" as fun; -@use "../abstracts/mixins" as mix; -@use "../abstracts/variables" as var; - -@font-face { - font-display: swap; - font-family: Kanit; - font-style: normal; - font-weight: 700; - src: url("../fonts/Kanit/Kanit-Bold.woff2") format("woff2"), - url("../fonts/Kanit/Kanit-Bold.woff") format("woff"); -} - -@font-face { - font-display: swap; - font-family: Kanit; - font-style: italic; - font-weight: 700; - src: url("../fonts/Kanit/Kanit-BoldItalic.woff2") format("woff2"), - url("../fonts/Kanit/Kanit-BoldItalic.woff") format("woff"); -} - -@font-face { - font-display: swap; - font-family: Kanit; - font-style: normal; - font-weight: 600; - src: url("../fonts/Kanit/Kanit-SemiBold.woff2") format("woff2"), - url("../fonts/Kanit/Kanit-SemiBold.woff") format("woff"); -} - -@font-face { - font-display: swap; - font-family: Kanit; - font-style: italic; - font-weight: 600; - src: url("../fonts/Kanit/Kanit-SemiBoldItalic.woff2") format("woff2"), - url("../fonts/Kanit/Kanit-SemiBoldItalic.woff") format("woff"); -} - -@font-face { - font-display: swap; - font-family: Kanit; - font-style: normal; - font-weight: 500; - src: url("../fonts/Kanit/Kanit-Medium.woff2") format("woff2"), - url("../fonts/Kanit/Kanit-Medium.woff") format("woff"); -} - -@font-face { - font-display: swap; - font-family: Kanit; - font-style: italic; - font-weight: 500; - src: url("../fonts/Kanit/Kanit-MediumItalic.woff2") format("woff2"), - url("../fonts/Kanit/Kanit-MediumItalic.woff") format("woff"); -} - -@font-face { - font-display: swap; - font-family: Kanit; - font-style: normal; - font-weight: 400; - src: url("../fonts/Kanit/Kanit-Regular.woff2") format("woff2"), - url("../fonts/Kanit/Kanit-Regular.woff") format("woff"); -} - -@font-face { - font-display: swap; - font-family: Kanit; - font-style: italic; - font-weight: 400; - src: url("../fonts/Kanit/Kanit-Italic.woff2") format("woff2"), - url("../fonts/Kanit/Kanit-Italic.woff") format("woff"); -} - -@font-face { - font-display: swap; - font-family: Kanit; - font-style: normal; - font-weight: 300; - src: url("../fonts/Kanit/Kanit-Light.woff2") format("woff2"), - url("../fonts/Kanit/Kanit-Light.woff") format("woff"); -} - -@font-face { - font-display: swap; - font-family: Kanit; - font-style: italic; - font-weight: 300; - src: url("../fonts/Kanit/Kanit-LightItalic.woff2") format("woff2"), - url("../fonts/Kanit/Kanit-LightItalic.woff") format("woff"); -} - -@font-face { - font-display: swap; - font-family: Inter; - font-style: oblique 0deg 10deg; - font-weight: 100 900; - src: url("../fonts/Inter/Inter.woff2?v=3.18") format("woff2"); -} - -@include mix.set-vars( - ( - font-family-primary: #{var.$font-family_primary}, - font-family-secondary: #{var.$font-family_secondary}, - font-size-sm: #{var.$font-size_sm}, - font-size-md: #{var.$font-size_md}, - font-size-lg: #{var.$font-size_lg}, - font-size-xl: #{var.$font-size_xl}, - font-size-2xl: #{var.$font-size_2xl}, - font-size-3xl: #{var.$font-size_3xl}, - line-height: #{var.$line-height}, - ) -); diff --git a/htdocs/src/scss/base/_helpers.scss b/htdocs/src/scss/base/_helpers.scss deleted file mode 100644 index d6a9233..0000000 --- a/htdocs/src/scss/base/_helpers.scss +++ /dev/null @@ -1,44 +0,0 @@ -@use "../abstracts/functions" as fun; -@use "../abstracts/mixins" as mix; - -.hide { - display: none !important; -} - -/* Text meant only for screen readers. */ -.screen-reader-text { - border: 0; - clip: rect(1px, 1px, 1px, 1px); - height: fun.convert-px(1); - overflow: hidden; - padding: 0; - position: absolute !important; - width: fun.convert-px(1); - word-break: normal; - word-wrap: normal !important; /* Many screen reader and browser combinations announce broken words as they would appear visually. */ - - &:focus { - background: fun.get-var(color-bg); - border: fun.convert-px(3) solid fun.get-var(color-border); - box-shadow: 0 0 fun.convert-px(2) fun.convert-px(2) - fun.get-var(color-shadow-light); - clip: auto !important; - color: fun.get-var(color-primary); - display: block; - font-size: fun.get-var(font-size-md); - font-weight: 600; - height: auto; - left: 0; - padding: fun.get-var(spacing-sm) fun.get-var(spacing-md); - top: 0; - width: auto; - z-index: 100000; - } -} - -@include mix.motion("reduce") { - * { - animation: none !important; - transition: none !important; - } -} diff --git a/htdocs/src/scss/base/_spacings.scss b/htdocs/src/scss/base/_spacings.scss deleted file mode 100644 index e908caf..0000000 --- a/htdocs/src/scss/base/_spacings.scss +++ /dev/null @@ -1,24 +0,0 @@ -@use "../abstracts/functions" as fun; -@use "../abstracts/mixins" as mix; -@use "../abstracts/variables" as var; -@include mix.set-vars( - ( - spacing-3xs: var.$spacing_3xs, - spacing-2xs: var.$spacing_2xs, - spacing-xs: var.$spacing_xs, - spacing-sm: var.$spacing_sm, - spacing-md: var.$spacing_md, - spacing-lg: var.$spacing_lg, - toolbar-height: fun.convert-px(60), - ) -); - -@include mix.media("screen") { - @include mix.dimensions("lg") { - @include mix.set-vars( - ( - toolbar-height: fun.convert-px(0), - ) - ); - } -} diff --git a/htdocs/src/scss/base/_typography.scss b/htdocs/src/scss/base/_typography.scss deleted file mode 100644 index b0504b7..0000000 --- a/htdocs/src/scss/base/_typography.scss +++ /dev/null @@ -1,48 +0,0 @@ -@use "../abstracts/functions" as fun; - -*::selection { - background: fun.get-var(color-primary-light-opacity); -} - -body { - background: fun.get-var(color-bg); - color: fun.get-var(color-fg); - font-size: fun.get-var(font-size-md); - line-height: fun.get-var(line-height); -} - -h1, -h2, -h3, -h4, -h5, -h6, -p, -ul { - margin: 0 0 fun.get-var(spacing-sm); -} - -a { - color: fun.get-var(color-primary); - text-decoration-thickness: fun.convert-px(2); - text-underline-offset: fun.convert-px(3); - transition: all 0.3s ease-in-out 0s; - - &:hover, - &:focus { - color: fun.get-var(color-primary-light); - text-decoration-color: fun.get-var(color-primary-light); - text-decoration-thickness: fun.convert-px(4); - } - - &:focus { - outline: fun.get-var(color-primary) dotted fun.convert-px(1); - } - - &:active { - color: fun.get-var(color-primary-dark); - outline: none; - text-decoration-color: fun.get-var(color-primary-dark); - text-decoration-thickness: fun.convert-px(2); - } -} diff --git a/htdocs/src/scss/components/_buttons.scss b/htdocs/src/scss/components/_buttons.scss deleted file mode 100644 index bc7959e..0000000 --- a/htdocs/src/scss/components/_buttons.scss +++ /dev/null @@ -1,21 +0,0 @@ -@use "../abstracts/functions" as fun; - -.btn { - background: fun.get-var(color-bg); - border: 0; - color: fun.get-var(color-primary); - cursor: pointer; - display: block; - font-weight: 600; - - &:hover, - &:focus { - background: fun.get-var(color-primary); - color: fun.get-var(color-fg-inverted); - } - - &:active { - background: fun.get-var(color-bg); - color: fun.get-var(color-primary); - } -} diff --git a/htdocs/src/scss/layout/_footer.scss b/htdocs/src/scss/layout/_footer.scss deleted file mode 100644 index 7dce0dc..0000000 --- a/htdocs/src/scss/layout/_footer.scss +++ /dev/null @@ -1,40 +0,0 @@ -@use "../abstracts/functions" as fun; -@use "../abstracts/mixins" as mix; - -.footer { - align-items: center; - background: fun.get-var(color-bg-secondary); - border-top: fun.convert-px(1) solid fun.get-var(color-border-light); - display: flex; - flex-flow: row wrap; - font-family: fun.get-var(font-family-secondary); - font-size: fun.get-var(font-size-md); - gap: fun.get-var(spacing-3xs); - justify-content: center; - padding: fun.get-var(spacing-sm) fun.get-var(spacing-md) - calc(#{fun.get-var(toolbar-height)} + #{fun.get-var(spacing-sm)}); - - @include mix.media("screen") { - @include mix.dimensions("lg") { - box-shadow: 0 -1px 2px 0 fun.get-var(color-shadow); - padding: fun.get-var(spacing-sm) fun.get-var(spacing-md); - } - } - - .nav { - display: inline-flex; - gap: fun.get-var(spacing-3xs); - - &::after { - content: "/"; - } - } -} - -.copyright { - align-items: center; - display: flex; - flex-flow: row wrap; - gap: fun.get-var(spacing-3xs); - justify-content: center; -} diff --git a/htdocs/src/scss/layout/_grid.scss b/htdocs/src/scss/layout/_grid.scss deleted file mode 100644 index 3749678..0000000 --- a/htdocs/src/scss/layout/_grid.scss +++ /dev/null @@ -1,43 +0,0 @@ -@use "../abstracts/functions" as fun; -@use "../abstracts/mixins" as mix; - -.body { - display: grid; - grid-template-columns: minmax(0, 1fr); - grid-template-rows: minmax(0, 1fr) max-content; - height: 100vh; - position: relative; - - @include mix.media("screen") { - @include mix.dimensions("lg") { - grid-template-columns: 1.5fr 4fr; - } - - @include mix.dimensions("xl") { - grid-template-columns: 1fr 4fr; - } - } -} - -.header { - grid-column: 1; - grid-row: 1; - width: 100%; -} - -.main { - grid-column: 1; - grid-row: 1 / -1; - - @include mix.media("screen") { - @include mix.dimensions("lg") { - grid-column: 2; - } - } -} - -.footer { - grid-column: 1; - grid-row: 2; - width: 100%; -} diff --git a/htdocs/src/scss/layout/_header.scss b/htdocs/src/scss/layout/_header.scss deleted file mode 100644 index cbc1693..0000000 --- a/htdocs/src/scss/layout/_header.scss +++ /dev/null @@ -1,143 +0,0 @@ -@use "../abstracts/functions" as fun; -@use "../abstracts/mixins" as mix; - -.header { - background: fun.get-var(color-bg-secondary); - overflow-y: auto; - padding: fun.get-var(spacing-md) - clamp(#{fun.get-var(spacing-md)}, 3vw, #{fun.get-var(spacing-lg)}); - scrollbar-color: fun.get-var(color-primary-light-opacity) - fun.get-var(color-bg-tertiary); - z-index: 5; - - @include mix.media("screen") { - @include mix.dimensions("lg") { - box-shadow: 0 -1px 2px 0 fun.get-var(color-shadow); - } - } -} - -.branding { - margin-bottom: clamp( - #{fun.get-var(spacing-sm)}, - 3vw, - #{fun.get-var(spacing-md)} - ); - text-align: center; - - &__title { - font-family: fun.get-var(font-family-secondary); - font-size: clamp( - #{fun.get-var(font-size-2xl)}, - 5vw, - #{fun.get-var(font-size-3xl)} - ); - font-weight: 500; - margin: fun.get-var(spacing-xs) 0 fun.get-var(spacing-3xs); - } - - &__link { - background: linear-gradient( - to top, - fun.get-var(color-primary-light) fun.convert-px(5), - transparent fun.convert-px(5) - ) - center / 0 100% no-repeat; - text-decoration: none; - transition: all 0.5s ease-in-out 0s; - - &:hover, - &:focus { - background-size: 100% 100%; - } - - &:active { - background-size: 0 100%; - } - } - - &__description { - font-family: fun.get-var(font-family-secondary); - font-size: clamp( - #{fun.get-var(font-size-md)}, - 3vw, - #{fun.get-var(font-size-lg)} - ); - font-weight: 400; - letter-spacing: fun.convert-px(1); - margin: 0; - text-transform: uppercase; - } -} - -.logo { - margin: auto; - position: relative; - width: max-content; - - &__image { - backface-visibility: hidden; - border: fun.convert-px(3) solid fun.get-var(color-border-light); - border-radius: 50%; - box-shadow: 0 0 fun.convert-px(6) fun.convert-px(1) - fun.get-var(color-shadow-darker); - left: 0; - position: absolute; - top: 0; - width: 100%; - - &--back { - transform: rotateY(180deg); - } - } - - &__link { - display: block; - height: clamp(#{fun.convert-px(75)}, 15vmin, #{fun.convert-px(90)}); - transform-style: preserve-3d; - transition: all 0.6s linear 0s; - width: clamp(#{fun.convert-px(75)}, 15vmin, #{fun.convert-px(90)}); - - &:hover, - &:focus { - outline: none; - transform: rotateY(180deg); - } - - &:hover &, - &:focus & { - &__image { - &--front { - transform: none; - } - - &--back { - transform: rotateY(180deg); - } - } - } - - &:focus & { - &__image { - box-shadow: 0 0 fun.convert-px(6) fun.convert-px(1) - fun.get-var(color-shadow-dark), - 0 0 0 fun.convert-px(5) fun.get-var(color-primary-light-opacity); - outline: none; - } - } - - &:active & { - &__image { - box-shadow: 0 0 fun.convert-px(6) fun.convert-px(1) - fun.get-var(color-shadow-dark), - 0 0 0 fun.convert-px(7) fun.get-var(color-primary-light-opacity); - } - } - } - - &:hover & { - &__link { - transform: rotateY(180deg); - } - } -} diff --git a/htdocs/src/scss/layout/_main.scss b/htdocs/src/scss/layout/_main.scss deleted file mode 100644 index 33c118e..0000000 --- a/htdocs/src/scss/layout/_main.scss +++ /dev/null @@ -1,153 +0,0 @@ -@use "../abstracts/functions" as fun; -@use "../abstracts/mixins" as mix; -@use "../abstracts/placeholders"; - -.main { - display: flex; - flex-flow: column nowrap; - height: calc(100% - #{fun.get-var(toolbar-height)}); - - @include mix.media("screen") { - @include mix.dimensions("lg") { - display: grid; - grid-template-columns: 5fr 2fr; - } - - @include mix.dimensions("xl") { - grid-template-columns: 4fr 1fr; - } - } -} - -// NoScript extension seems to replace noscript tag with a span. -.main > span, -.instructions, -noscript { - background: fun.get-var(color-bg); - padding: fun.get-var(spacing-md); - text-align: center; - - @include mix.media("screen") { - @include mix.dimensions("lg") { - grid-column: 1 / -1; - } - } -} - -.instructions { - align-items: center; - display: flex; - justify-content: center; -} - -.legal-notice { - height: 100%; - overflow-y: auto; - padding: clamp(#{fun.get-var(spacing-md)}, 3vw, #{fun.get-var(spacing-lg)}); - scrollbar-color: fun.get-var(color-primary-light-opacity) - fun.get-var(color-bg-tertiary); - width: 100%; - - @include mix.media("screen") { - @include mix.dimensions("lg") { - grid-column: 1 / -1; - } - } -} - -.legal-notice { - height: 100%; - overflow-y: auto; - padding: clamp(#{fun.get-var(spacing-md)}, 3vw, #{fun.get-var(spacing-lg)}); - scrollbar-color: fun.get-var(color-primary-light-opacity) - fun.get-var(color-bg-tertiary); - width: 100%; - - @include mix.media("screen") { - @include mix.dimensions("lg") { - grid-column: 1 / -1; - } - } -} - -.project-preview { - background: fun.get-var(color-bg); - flex: 0 1 100%; - min-height: 0; - width: 100%; - - @include mix.media("screen") { - @include mix.dimensions("lg") { - height: 100%; - } - } - - iframe { - border: 0; - height: 100%; - width: 100%; - } -} - -.project-details { - background: fun.get-var(color-bg-secondary); - box-shadow: 0 -1px 2px 0 fun.get-var(color-shadow); - flex: 1 0 100%; - overflow-y: auto; - padding: fun.get-var(spacing-md); - scrollbar-color: fun.get-var(color-primary-light-opacity) - fun.get-var(color-bg-tertiary); - - @include mix.media("screen") { - @include mix.dimensions("lg") { - font-size: fun.get-var(font-size-md); - } - } - - &__description { - margin-bottom: fun.get-var(spacing-md); - white-space: pre-wrap; - } - - .list { - &--tech { - padding-left: fun.get-var(spacing-sm); - } - - &--repos { - @extend %flex-list; - - gap: fun.get-var(spacing-xs); - } - - &__link { - background-repeat: no-repeat; - background-size: contain; - box-shadow: 0 0 0 0 fun.get-var(color-shadow); - display: block; - height: fun.convert-px(50); - transition: transform 0.3s ease-in-out 0s, - box-shadow 0.15s ease-in-out 0.15s; - width: fun.convert-px(50); - - &--github { - background: url(#{fun.encode-svg('')}); - } - - &--gitlab { - background: url(#{fun.encode-svg('')}); - } - - &:hover, - &:focus { - box-shadow: fun.convert-px(-1) fun.convert-px(1) fun.convert-px(4) - fun.convert-px(2) fun.get-var(color-shadow-light); - transform: scale(1.15); - } - - &:active { - opacity: 1; - } - } - } -} diff --git a/htdocs/src/scss/layout/_nav.scss b/htdocs/src/scss/layout/_nav.scss deleted file mode 100644 index 98e4cb5..0000000 --- a/htdocs/src/scss/layout/_nav.scss +++ /dev/null @@ -1,70 +0,0 @@ -@use "../abstracts/functions" as fun; -@use "../abstracts/placeholders"; - -.nav { - text-align: center; - - &__label { - font-weight: 600; - } - - &__list { - @extend %reset-list; - - .btn { - width: 100%; - } - } - - &:not(&--footer) &__item { - margin: fun.get-var(spacing-2xs) 0; - } - - &:not(&--footer) &__link { - background-image: linear-gradient( - to left, - #{fun.get-var(color-bg)} 0, - #{fun.get-var(color-bg)} 50%, - #{fun.get-var(color-primary)} 50% - ); - background-position: 100% 0; - background-size: 200% 100%; - border: fun.convert-px(3) solid fun.get-var(color-border); - border-radius: fun.convert-px(50); - display: block; - font-weight: 600; - margin: auto; - padding: fun.get-var(spacing-3xs); - position: relative; - text-decoration: none; - transition: all 0.4s ease-in-out 0s; - width: 75%; - - &:hover, - &:focus { - background-position: 0 0; - color: fun.get-var(color-fg-inverted); - } - - &:active { - background-position: 100% 0; - color: fun.get-var(color-primary-dark); - text-decoration: fun.convert-px(1) solid underline; - } - - &--selected { - background: fun.get-var(color-primary-dark); - box-shadow: inset 0 0 0 4px fun.get-var(color-bg); - color: fun.get-var(color-fg-inverted); - - &:hover, - &:focus { - background: fun.get-var(color-primary-light); - } - } - } - - .btn { - margin: auto; - } -} diff --git a/htdocs/src/scss/layout/_toolbar.scss b/htdocs/src/scss/layout/_toolbar.scss deleted file mode 100644 index 00e2bd7..0000000 --- a/htdocs/src/scss/layout/_toolbar.scss +++ /dev/null @@ -1,34 +0,0 @@ -@use "../abstracts/functions" as fun; - -.toolbar { - align-items: center; - background: fun.get-var(color-primary); - bottom: 0; - box-shadow: 0 -1px 2px 0 fun.get-var(color-shadow-dark); - color: fun.get-var(color-fg-inverted); - display: flex; - flex-flow: row nowrap; - gap: fun.get-var(spacing-xs); - height: fun.get-var(toolbar-height); - justify-content: center; - left: 0; - padding: 0 fun.get-var(spacing-sm); - position: absolute; - right: 0; - z-index: 2; - - & > &__options { - background: fun.get-var(color-primary); - color: fun.get-var(color-fg-inverted); - font-size: fun.get-var(font-size-sm); - height: 85%; - line-height: inherit; - padding: 0 fun.get-var(spacing-xs); - - &:hover, - &:focus { - background: fun.get-var(color-bg); - color: fun.get-var(color-primary); - } - } -} diff --git a/htdocs/src/scss/style.scss b/htdocs/src/scss/style.scss deleted file mode 100644 index 96c8fe5..0000000 --- a/htdocs/src/scss/style.scss +++ /dev/null @@ -1,40 +0,0 @@ -@charset 'utf-8'; - -/** - * 1.0 Vendors - * - * Import each files separately to define vendors styles order. - */ -@use "modern-normalize"; - -/** - * 2.0 Base - * - * Define some standard styles and CSS variables (colors, fonts...). - */ -@use "base/base"; -@use "base/animations"; -@use "base/colors"; -@use "base/fonts"; -@use "base/helpers"; -@use "base/spacings"; -@use "base/typography"; - -/** - * 3.0 Layout - * - * Define website layout. - */ -@use "layout/grid"; -@use "layout/header"; -@use "layout/main"; -@use "layout/footer"; -@use "layout/toolbar"; -@use "layout/nav"; - -/** -* 4.0 Components -* -* Define styles for all kind of specific modules like buttons, widgets... -*/ -@use "components/buttons"; diff --git a/index.html b/index.html new file mode 100644 index 0000000..47a8592 --- /dev/null +++ b/index.html @@ -0,0 +1,77 @@ + + + + + + + Demo | Armand Philippot + + + + +
+ + +
+
+
+ +

+ Armand Philippot +

+

Front-end developer

+
+ +
+
+
+ Select an app inside menu to see a live preview and app details + (description, technologies, repositories). +
+ +
+ + + + + diff --git a/legal-notice.html b/legal-notice.html new file mode 100644 index 0000000..7b6c2bc --- /dev/null +++ b/legal-notice.html @@ -0,0 +1,90 @@ + + + + + + + Legal Notice | Demo | Armand Philippot + + + + +
+ + +
+
+
+ +

+ Armand Philippot +

+

Front-end developer

+
+ +
+
+ +
+ + + + + diff --git a/license.html b/license.html new file mode 100644 index 0000000..15ea458 --- /dev/null +++ b/license.html @@ -0,0 +1,93 @@ + + + + + + + License | Demo | Armand Philippot + + + + +
+ + +
+
+
+ +

+ Armand Philippot +

+

Front-end developer

+
+ +
+
+ +
+ + + + + diff --git a/mentions-legales.html b/mentions-legales.html new file mode 100644 index 0000000..ebfdf52 --- /dev/null +++ b/mentions-legales.html @@ -0,0 +1,90 @@ + + + + + + + Mentions légales | Demo | Armand Philippot + + + + +
+ + +
+
+
+ +

+ Armand Philippot +

+

Front-end developer

+
+ +
+
+ +
+ + + + + diff --git a/package.json b/package.json index fa680a2..b9f77f5 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "lint:js": "eslint --ext .js,.jsx", "lint:scss": "stylelint \"**/*.scss\" --syntax scss --formatter verbose", "release": "standard-version -s", - "rtl": "rtlcss htdocs/assets/css/style.css htdocs/assets/css/style-rtl.css", + "rtl": "rtlcss assets/css/style.css assets/css/style-rtl.css", "serve": "webpack serve --node-env=development", "watch": "webpack --watch --progress --node-env=development" }, diff --git a/projects/js-small-apps b/projects/js-small-apps new file mode 160000 index 0000000..e06cb6d --- /dev/null +++ b/projects/js-small-apps @@ -0,0 +1 @@ +Subproject commit e06cb6d423997411f56897ded251913c5b9568ef diff --git a/projects/react-small-apps b/projects/react-small-apps new file mode 160000 index 0000000..d3dd2db --- /dev/null +++ b/projects/react-small-apps @@ -0,0 +1 @@ +Subproject commit d3dd2db1c46a2bf4d1f649536219f9d0ce9ced71 diff --git a/src/fonts/Inter/Inter.woff2 b/src/fonts/Inter/Inter.woff2 new file mode 100644 index 0000000..365eedc Binary files /dev/null and b/src/fonts/Inter/Inter.woff2 differ diff --git a/src/fonts/Inter/LICENSE.txt b/src/fonts/Inter/LICENSE.txt new file mode 100644 index 0000000..ff80f8c --- /dev/null +++ b/src/fonts/Inter/LICENSE.txt @@ -0,0 +1,94 @@ +Copyright (c) 2016-2020 The Inter Project Authors. +"Inter" is trademark of Rasmus Andersson. +https://github.com/rsms/inter + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION AND CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/src/fonts/Kanit/Kanit-Bold.woff b/src/fonts/Kanit/Kanit-Bold.woff new file mode 100644 index 0000000..7602cee Binary files /dev/null and b/src/fonts/Kanit/Kanit-Bold.woff differ diff --git a/src/fonts/Kanit/Kanit-Bold.woff2 b/src/fonts/Kanit/Kanit-Bold.woff2 new file mode 100644 index 0000000..8494234 Binary files /dev/null and b/src/fonts/Kanit/Kanit-Bold.woff2 differ diff --git a/src/fonts/Kanit/Kanit-BoldItalic.woff b/src/fonts/Kanit/Kanit-BoldItalic.woff new file mode 100644 index 0000000..af36319 Binary files /dev/null and b/src/fonts/Kanit/Kanit-BoldItalic.woff differ diff --git a/src/fonts/Kanit/Kanit-BoldItalic.woff2 b/src/fonts/Kanit/Kanit-BoldItalic.woff2 new file mode 100644 index 0000000..61f04ef Binary files /dev/null and b/src/fonts/Kanit/Kanit-BoldItalic.woff2 differ diff --git a/src/fonts/Kanit/Kanit-Italic.woff b/src/fonts/Kanit/Kanit-Italic.woff new file mode 100644 index 0000000..462f295 Binary files /dev/null and b/src/fonts/Kanit/Kanit-Italic.woff differ diff --git a/src/fonts/Kanit/Kanit-Italic.woff2 b/src/fonts/Kanit/Kanit-Italic.woff2 new file mode 100644 index 0000000..1723709 Binary files /dev/null and b/src/fonts/Kanit/Kanit-Italic.woff2 differ diff --git a/src/fonts/Kanit/Kanit-Light.woff b/src/fonts/Kanit/Kanit-Light.woff new file mode 100644 index 0000000..872ce1b Binary files /dev/null and b/src/fonts/Kanit/Kanit-Light.woff differ diff --git a/src/fonts/Kanit/Kanit-Light.woff2 b/src/fonts/Kanit/Kanit-Light.woff2 new file mode 100644 index 0000000..6b35eda Binary files /dev/null and b/src/fonts/Kanit/Kanit-Light.woff2 differ diff --git a/src/fonts/Kanit/Kanit-LightItalic.woff b/src/fonts/Kanit/Kanit-LightItalic.woff new file mode 100644 index 0000000..b81b7b3 Binary files /dev/null and b/src/fonts/Kanit/Kanit-LightItalic.woff differ diff --git a/src/fonts/Kanit/Kanit-LightItalic.woff2 b/src/fonts/Kanit/Kanit-LightItalic.woff2 new file mode 100644 index 0000000..f933a2c Binary files /dev/null and b/src/fonts/Kanit/Kanit-LightItalic.woff2 differ diff --git a/src/fonts/Kanit/Kanit-Medium.woff b/src/fonts/Kanit/Kanit-Medium.woff new file mode 100644 index 0000000..d5b3510 Binary files /dev/null and b/src/fonts/Kanit/Kanit-Medium.woff differ diff --git a/src/fonts/Kanit/Kanit-Medium.woff2 b/src/fonts/Kanit/Kanit-Medium.woff2 new file mode 100644 index 0000000..a012e98 Binary files /dev/null and b/src/fonts/Kanit/Kanit-Medium.woff2 differ diff --git a/src/fonts/Kanit/Kanit-MediumItalic.woff b/src/fonts/Kanit/Kanit-MediumItalic.woff new file mode 100644 index 0000000..6859f58 Binary files /dev/null and b/src/fonts/Kanit/Kanit-MediumItalic.woff differ diff --git a/src/fonts/Kanit/Kanit-MediumItalic.woff2 b/src/fonts/Kanit/Kanit-MediumItalic.woff2 new file mode 100644 index 0000000..8af9298 Binary files /dev/null and b/src/fonts/Kanit/Kanit-MediumItalic.woff2 differ diff --git a/src/fonts/Kanit/Kanit-Regular.woff b/src/fonts/Kanit/Kanit-Regular.woff new file mode 100644 index 0000000..eec0b42 Binary files /dev/null and b/src/fonts/Kanit/Kanit-Regular.woff differ diff --git a/src/fonts/Kanit/Kanit-Regular.woff2 b/src/fonts/Kanit/Kanit-Regular.woff2 new file mode 100644 index 0000000..9b925bf Binary files /dev/null and b/src/fonts/Kanit/Kanit-Regular.woff2 differ diff --git a/src/fonts/Kanit/Kanit-SemiBold.woff b/src/fonts/Kanit/Kanit-SemiBold.woff new file mode 100644 index 0000000..dacdb5b Binary files /dev/null and b/src/fonts/Kanit/Kanit-SemiBold.woff differ diff --git a/src/fonts/Kanit/Kanit-SemiBold.woff2 b/src/fonts/Kanit/Kanit-SemiBold.woff2 new file mode 100644 index 0000000..52d4527 Binary files /dev/null and b/src/fonts/Kanit/Kanit-SemiBold.woff2 differ diff --git a/src/fonts/Kanit/Kanit-SemiBoldItalic.woff b/src/fonts/Kanit/Kanit-SemiBoldItalic.woff new file mode 100644 index 0000000..ebf0137 Binary files /dev/null and b/src/fonts/Kanit/Kanit-SemiBoldItalic.woff differ diff --git a/src/fonts/Kanit/Kanit-SemiBoldItalic.woff2 b/src/fonts/Kanit/Kanit-SemiBoldItalic.woff2 new file mode 100644 index 0000000..300d77a Binary files /dev/null and b/src/fonts/Kanit/Kanit-SemiBoldItalic.woff2 differ diff --git a/src/fonts/Kanit/OFL.txt b/src/fonts/Kanit/OFL.txt new file mode 100644 index 0000000..0f48ea4 --- /dev/null +++ b/src/fonts/Kanit/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Kanit Project Authors (https://github.com/cadsondemak/kanit) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/src/images/armand-philippot-logo.svg b/src/images/armand-philippot-logo.svg new file mode 100644 index 0000000..17a245e --- /dev/null +++ b/src/images/armand-philippot-logo.svg @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/images/armand-philippot.jpg b/src/images/armand-philippot.jpg new file mode 100644 index 0000000..2c8ef50 Binary files /dev/null and b/src/images/armand-philippot.jpg differ diff --git a/src/images/cc-by-sa.svg b/src/images/cc-by-sa.svg new file mode 100644 index 0000000..b1fc892 --- /dev/null +++ b/src/images/cc-by-sa.svg @@ -0,0 +1,79 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/src/images/favicon/android-chrome-192x192.png b/src/images/favicon/android-chrome-192x192.png new file mode 100644 index 0000000..2def6f5 Binary files /dev/null and b/src/images/favicon/android-chrome-192x192.png differ diff --git a/src/images/favicon/android-chrome-512x512.png b/src/images/favicon/android-chrome-512x512.png new file mode 100644 index 0000000..514e060 Binary files /dev/null and b/src/images/favicon/android-chrome-512x512.png differ diff --git a/src/images/favicon/apple-touch-icon.png b/src/images/favicon/apple-touch-icon.png new file mode 100644 index 0000000..e4b7ae0 Binary files /dev/null and b/src/images/favicon/apple-touch-icon.png differ diff --git a/src/images/favicon/browserconfig.xml b/src/images/favicon/browserconfig.xml new file mode 100644 index 0000000..f9c2e67 --- /dev/null +++ b/src/images/favicon/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #2b5797 + + + diff --git a/src/images/favicon/favicon-16x16.png b/src/images/favicon/favicon-16x16.png new file mode 100644 index 0000000..525a1b3 Binary files /dev/null and b/src/images/favicon/favicon-16x16.png differ diff --git a/src/images/favicon/favicon-32x32.png b/src/images/favicon/favicon-32x32.png new file mode 100644 index 0000000..c4b80ad Binary files /dev/null and b/src/images/favicon/favicon-32x32.png differ diff --git a/src/images/favicon/favicon.ico b/src/images/favicon/favicon.ico new file mode 100644 index 0000000..d1d41a8 Binary files /dev/null and b/src/images/favicon/favicon.ico differ diff --git a/src/images/favicon/mstile-144x144.png b/src/images/favicon/mstile-144x144.png new file mode 100644 index 0000000..f68a989 Binary files /dev/null and b/src/images/favicon/mstile-144x144.png differ diff --git a/src/images/favicon/mstile-150x150.png b/src/images/favicon/mstile-150x150.png new file mode 100644 index 0000000..b36424a Binary files /dev/null and b/src/images/favicon/mstile-150x150.png differ diff --git a/src/images/favicon/mstile-310x150.png b/src/images/favicon/mstile-310x150.png new file mode 100644 index 0000000..f395692 Binary files /dev/null and b/src/images/favicon/mstile-310x150.png differ diff --git a/src/images/favicon/mstile-310x310.png b/src/images/favicon/mstile-310x310.png new file mode 100644 index 0000000..3c7fd66 Binary files /dev/null and b/src/images/favicon/mstile-310x310.png differ diff --git a/src/images/favicon/mstile-70x70.png b/src/images/favicon/mstile-70x70.png new file mode 100644 index 0000000..12d226b Binary files /dev/null and b/src/images/favicon/mstile-70x70.png differ diff --git a/src/images/favicon/safari-pinned-tab.svg b/src/images/favicon/safari-pinned-tab.svg new file mode 100644 index 0000000..2354f78 --- /dev/null +++ b/src/images/favicon/safari-pinned-tab.svg @@ -0,0 +1,25 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + + + diff --git a/src/images/favicon/site.webmanifest b/src/images/favicon/site.webmanifest new file mode 100644 index 0000000..31df508 --- /dev/null +++ b/src/images/favicon/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "Armand Philippot", + "short_name": "AP", + "icons": [ + { + "src": "/wp-content/themes/apcom/assets/images/favicon/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/wp-content/themes/apcom/assets/images/favicon/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#194476", + "background_color": "#194476", + "display": "standalone" +} diff --git a/src/images/github.svg b/src/images/github.svg new file mode 100644 index 0000000..a39f6d2 --- /dev/null +++ b/src/images/github.svg @@ -0,0 +1,69 @@ + + + + + + + + image/svg+xml + + + + + + + diff --git a/src/images/gitlab.svg b/src/images/gitlab.svg new file mode 100644 index 0000000..5a0ce62 --- /dev/null +++ b/src/images/gitlab.svg @@ -0,0 +1,93 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/src/js/app.js b/src/js/app.js new file mode 100644 index 0000000..38aee0e --- /dev/null +++ b/src/js/app.js @@ -0,0 +1,382 @@ +import projects from './config/projects'; +import { + translate, + setLocale, + currentLocale, + supportedLanguages, +} from './i18n/i18n'; +import { + hideToBottom, + hideToLeft, + showFromBottom, + showFromLeft, +} from './utilities/animations'; +import { isSmallVw, isStyleJsExists } from './utilities/helpers'; + +/** + * Show/hide header and footer with slide animation (left). + */ +function toggleHeaderFooter() { + const header = document.querySelector('header'); + const footer = document.querySelector('footer'); + const elements = [header, footer]; + + elements.forEach((el) => { + if (el.classList.contains('hide')) { + showFromLeft(el); + } else { + hideToLeft(el); + } + }); +} + +/** + * Show/hide project details with slide animation (bottom). + */ +function toggleProjectDetails() { + const details = document.querySelector('.project-details'); + + if (details.classList.contains('hide')) { + showFromBottom(details); + } else { + hideToBottom(details); + } +} + +/** + * Add an event listener to show or hide header and footer. + */ +function listenMenuBtn() { + const menuBtn = document.querySelector('.btn--menu'); + menuBtn.addEventListener('click', toggleHeaderFooter); +} + +/** + * Show or hide the project details button depending on current location. + */ +function toggleProjectDetailsBtn() { + const button = document.querySelector('.btn--details'); + const currentPath = window.location.hash; + + if (currentPath) { + button.style.display = ''; + } else { + button.style.display = 'none'; + } +} + +/** + * Update the visibility of some DOM elements depending on viewport. + */ +function updateView() { + const header = document.querySelector('header'); + const footer = document.querySelector('footer'); + const toolbar = document.querySelector('.toolbar'); + const details = document.querySelector('.project-details'); + + if (isSmallVw()) { + header.classList.add('hide'); + footer.classList.add('hide'); + toolbar.classList.remove('hide'); + details?.classList.add('hide'); + details?.classList.remove('fade-in'); + } else { + showFromLeft(header); + showFromLeft(footer); + toolbar.classList.add('hide'); + details?.classList.remove('hide'); + details?.classList.add('fade-in'); + } + + toggleProjectDetailsBtn(); +} + +/** + * Update view when the window size changes. + */ +function listenWindowSize() { + window.addEventListener('resize', updateView); +} + +/** + * Retrieve a project by id. + * @param {Integer} id - The project id. + * @returns {Object} The current project. + */ +function getCurrentProject(id) { + return projects.find((project) => project.id === id); +} + +/** + * Get a list item for the given repo. + * @param {String} name - The repository name. + * @param {String} url - The repository URL. + * @returns {HTMLElement} A list item. + */ +function getRepoItem(name, url) { + const item = document.createElement('li'); + const link = document.createElement('a'); + const span = document.createElement('span'); + span.classList.add('screen-reader-text'); + span.textContent = name; + link.classList.add('list__link', `list__link--${name.toLocaleLowerCase()}`); + link.href = url; + link.appendChild(span); + item.classList.add('list__item'); + item.appendChild(link); + return item; +} + +/** + * Get the repos list wrapped inside ul element and the title. + * @param {Object[]} repos - An array of repo with name and URL. + * @returns {[title, list]} An array of HTMLElements for title and list. + */ +function getRepos(repos) { + if (repos.length === 0) return []; + + const wrapper = document.createElement('div'); + const title = document.createElement('h3'); + const list = document.createElement('ul'); + const items = repos.map((repo) => getRepoItem(repo.name, repo.url)); + title.classList.add('project-details__title'); + title.textContent = translate('main.project.details.repo', { + count: repos.length, + }); + list.classList.add('list', 'list--repos'); + list.append(...items); + wrapper.append(title, list); + return [title, list]; +} + +/** + * Get the technologies list wrapped inside ul element and the title. + * @param {String[]} technologies - An array of technology names. + * @returns {[title, list]} An array of HTMLElements for title and list. + */ +function getTechs(technologies) { + if (technologies.length === 0) return []; + + const title = document.createElement('h3'); + title.classList.add('project-details__title'); + title.textContent = translate('main.project.details.tech', { + count: technologies.length, + }); + const list = document.createElement('ul'); + const items = technologies.map((technology) => { + const item = document.createElement('li'); + item.textContent = technology; + return item; + }); + list.classList.add('list', 'list--tech'); + list.append(...items); + return [title, list]; +} + +/** + * Retrieve the project details. + * @param {Object} project - The project. + * @returns {HTMLElement} The project details wrapped in a div. + */ +function getProjectDetails(project) { + const details = document.createElement('div'); + const title = document.createElement('h2'); + const techList = project?.technologies ? getTechs(project.technologies) : []; + const reposList = getRepos(project.repo); + const locale = currentLocale(); + let description; + + if (project.description) { + description = document.createElement('div'); + description.classList.add('project-details__description'); + description.textContent = project.description[locale] || ''; + } else { + description = ''; + } + + title.classList.add('project-details__title'); + title.textContent = translate('main.project.details.about', { + name: project.name, + }); + details.classList.add('project-details'); + if (!isSmallVw()) details.classList.add('fade-in'); + details.replaceChildren(title, description, ...techList, ...reposList); + + return details; +} + +/** + * Get an iframe for the given path/url. + * @param {String} src - The path/url to use as source. + * @returns {HTMLElement} The iframe. + */ +function getIframe(src) { + const iframe = document.createElement('iframe'); + iframe.src = src; + return iframe; +} + +/** + * Retrieve the project preview. + * @param {String} projectPath - The project path. + * @returns {HTMLElement} The project preview wrapped in a div. + */ +function getProjectPreview(projectPath) { + const preview = document.createElement('div'); + const iframe = getIframe(projectPath); + preview.classList.add('project-preview', 'fade-in'); + preview.replaceChildren(iframe); + return preview; +} + +/** + * Display the targeted project. + * @param {String} id - The project id. + */ +function showProject(id) { + const currentProject = getCurrentProject(id); + const main = document.querySelector('.main'); + const details = getProjectDetails(currentProject); + const preview = getProjectPreview(currentProject.path); + const detailsBtn = document.querySelector('.btn--details'); + + if (isSmallVw()) details.classList.add('hide'); + + detailsBtn.textContent = translate('main.project.details.about', { + name: currentProject.name, + }); + detailsBtn.addEventListener('click', toggleProjectDetails); + window.history.pushState({}, currentProject.name, `/#${id}`); + document.title = `${currentProject.name} | Demo | Armand Philippot`; + main.replaceChildren(preview, details); +} + +/** + * Add a CSS class to the current project in projects nav. + * @param {String} id - The project id. + */ +function setSelectedProject(id) { + const links = document.querySelectorAll('.nav__link'); + links.forEach((link) => { + if (link.id === id) { + link.classList.add('nav__link--selected'); + } else { + link.classList.remove('nav__link--selected'); + } + }); +} + +/** + * Create a list item for a project. + * @param {String} id - The project id. + * @param {String} name - The project name. + * @returns {HTMLElement} The list item. + */ +function getProjectsNavItem(id, name) { + const item = document.createElement('li'); + const link = document.createElement('a'); + link.classList.add('nav__link'); + link.href = `/#${id}`; + link.id = id; + link.textContent = name; + link.addEventListener('click', (e) => { + e.preventDefault(); + showProject(id); + setSelectedProject(id); + toggleProjectDetailsBtn(); + if (isSmallVw()) toggleHeaderFooter(); + }); + item.classList.add('nav__item'); + item.appendChild(link); + return item; +} + +/** + * Print the list of available projects. + */ +function printProjectsNav() { + const ul = document.querySelector('.nav .nav__list'); + + projects.forEach((project) => { + const item = getProjectsNavItem(project.id, project.name); + ul.appendChild(item); + }); +} + +/** + * Add style.js script for development purposes. + */ +function loadWebpackStyles() { + if (isStyleJsExists()) { + const head = document.querySelector('head'); + const script = document.createElement('script'); + script.src = 'assets/js/style.js'; + head.appendChild(script); + } +} + +/** + * Load corresponding project if the requested page contains a hash. + */ +function printRequestedPage() { + const currentPath = window.location.hash; + + if (currentPath) { + const id = currentPath.replace('#', ''); + showProject(id); + setSelectedProject(id); + } +} + +/** + * Replace the legal notice link and text. + */ +function replaceLegalNoticeLink() { + const link = document.querySelector('.nav__link--legal'); + link.href = translate('footer.legalNotice.link'); + link.textContent = translate('footer.legalNotice.txt'); +} + +/** + * Translate all text available in HTML templates. + */ +function translateHTMLContent() { + const brandingDesc = document.querySelector('.branding__description'); + const navLabel = document.querySelector('.nav__label'); + const license = document.querySelector('.copyright__license'); + const instructions = document.querySelector('.instructions'); + brandingDesc.textContent = translate('branding.description'); + navLabel.textContent = translate('nav.title'); + license.title = translate('footer.license'); + if (instructions) instructions.textContent = translate('main.instructions'); +} + +/** + * Translate the website according to the user preferred language. + */ +function setAppLocale() { + const preferredLanguage = navigator.language; + const supportedLanguage = supportedLanguages.find( + (lang) => preferredLanguage.startsWith(lang.code) + // eslint-disable-next-line function-paren-newline -- Conflict with Prettier + ); + const locale = supportedLanguage?.code || 'en'; + setLocale(locale); +} + +/** + * Initialize the website with the projects list. + */ +function init() { + setAppLocale(); + translateHTMLContent(); + replaceLegalNoticeLink(); + loadWebpackStyles(); + printProjectsNav(); + updateView(); + listenWindowSize(); + listenMenuBtn(); + printRequestedPage(); +} + +init(); diff --git a/src/js/config/projects.js b/src/js/config/projects.js new file mode 100644 index 0000000..53f1af8 --- /dev/null +++ b/src/js/config/projects.js @@ -0,0 +1,224 @@ +const projects = [ + { + id: 'bin2dec', + name: 'Bin2Dec', + description: { + en: 'Convert a binary string to a decimal number.', + fr: 'Convertit un nombre binaire en un nombre décimal.', + }, + path: './projects/js-small-apps/bin2dec/index.html', + repo: [ + { + name: 'Github', + url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/bin2dec', + }, + { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/bin2dec', + }, + ], + technologies: ['Vanilla Javascript'], + }, + { + id: 'budget-app', + name: 'Budget App', + description: { + en: 'By selecting a language in the initialization form, only the currency is converted (the app is not translated). Also, no data is saved on page reload.', + fr: "En sélectionnant une langue dans le formulaire d'initialisation, seul le format des nombres change (l'application n'est pas traduite). Aucune donnée n'est conservée après rechargement de la page.", + }, + path: './projects/js-small-apps/budget-app/index.html', + repo: [ + { + name: 'Github', + url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/budget-app', + }, + { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/budget-app', + }, + ], + technologies: ['Vanilla Javascript'], + }, + { + id: 'calculator', + name: 'Calculator', + description: { + en: 'A basic calculator. Decimal part is limited to 3 digits. The first part is limited to 8 digits. If the result does not respect these limits, you will see an error.', + fr: 'Une simple calculette. La partie décimale est limitée à 3 chiffres. La première partie est limitée à 8 chiffres. Si le résultat ne respecte pas ces limites, vous verrez une erreur.', + }, + path: './projects/js-small-apps/calculator/index.html', + repo: [ + { + name: 'Github', + url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/calculator', + }, + { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/calculator', + }, + ], + technologies: ['Vanilla Javascript'], + }, + { + id: 'clock', + name: 'Clock', + description: { + en: 'What time is it? You can have the current hour in three formats: an analogic clock, a numeric display or a text.', + fr: "Quelle heure est-il ? Vous pouvez voir l'heure actuelle dans trois formats différents : une horloge analogique, un affichage numérique et sous forme de texte.", + }, + path: './projects/js-small-apps/clock/index.html', + repo: [ + { + name: 'Github', + url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/clock', + }, + { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/clock', + }, + ], + technologies: ['Vanilla Javascript', 'SVG'], + }, + { + id: 'color-cycle', + name: 'Color cycle', + description: { + en: 'Play with hexadecimal colors. Set a color, then choose one or more increment values and start the preview.', + fr: "Jouez avec les couleurs hexadécimales. Définissez une couleur, puis choisissez une ou plusieurs valeurs d'incrémentation et démarrez l'aperçu.", + }, + path: './projects/js-small-apps/color-cycle/index.html', + repo: [ + { + name: 'Github', + url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/color-cycle', + }, + { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/color-cycle', + }, + ], + technologies: ['Vanilla Javascript'], + }, + { + id: 'css-border-previewer', + name: 'CSS Border Previewer', + description: { + en: 'Play with CSS borders (style, width, radius). Then, you can copy the generated code if the preview suits you.', + fr: "Jouez avec les bordures CSS (style, largeur, radius). Ensuite, vous pouvez copier le code généré si l'aperçu vous satisfait.", + }, + path: './projects/js-small-apps/css-border-previewer/index.html', + repo: [ + { + name: 'Github', + url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/css-border-previewer', + }, + { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/css-border-previewer', + }, + ], + technologies: ['Vanilla Javascript'], + }, + { + id: 'meme-generator', + name: 'Meme Generator', + description: { + en: 'Choose a random image, set one or more texts then position them. Your meme is ready!', + fr: 'Choisissez une image aléatoire, définissez un ou plusieurs textes et positionnez-les. Votre meme est prêt !', + }, + path: './projects/react-small-apps/meme-generator/build/index.html', + repo: [ + { + name: 'Github', + url: 'https://github.com/ArmandPhilippot/react-small-apps/tree/main/meme-generator', + }, + { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/react-small-apps/-/tree/main/meme-generator', + }, + ], + technologies: ['React', 'Fetch'], + }, + { + id: 'notebook', + name: 'Notebook', + description: { + en: 'Create as many pages as you want and fill them. You can define a title and a body. Then you can easily navigate between your pages with the nav.', + fr: 'Créez autant de pages que vous le souhaitez et remplissez-les. Vous pouvez définir un titre et un corps de texte. Ensuite, vous pouvez facilement naviguer entre vos pages grâce à la navigation.', + }, + path: './projects/react-small-apps/notebook/build/', + repo: [ + { + name: 'Github', + url: 'https://github.com/ArmandPhilippot/react-small-apps/tree/main/notebook', + }, + { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/react-small-apps/-/tree/main/notebook', + }, + ], + technologies: ['React', 'React router'], + }, + { + id: 'rps-game', + name: 'Rock Paper Scissors', + description: { + en: 'A basic implementation of the game. Try to beat your friend or the computer.', + fr: "Une implémentation du jeu. Essayez de battre votre ami ou l'ordinateur.", + }, + path: './projects/js-small-apps/rock-paper-scissors/index.html', + repo: [ + { + name: 'Github', + url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/rock-paper-scissors', + }, + { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/rock-paper-scissors', + }, + ], + technologies: ['Vanilla Javascript'], + }, + { + id: 'todos', + name: 'Todos', + description: { + en: 'You can add, remove or mark as done your todos. For each todos, you can add some details in addition to the title.\n\nLogin: demo@email.com\nPassword: demo', + fr: 'Vous pouvez ajouter, supprimer ou marquer comme fait vos "todo". Pour chaque "todo", vous pouvez ajouter des détails en plus du titre.\n\nLogin : demo@email.com\nMot de passe : demo', + }, + path: './projects/react-small-apps/todos/build/', + repo: [ + { + name: 'Github', + url: 'https://github.com/ArmandPhilippot/react-small-apps/tree/main/todos', + }, + { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/react-small-apps/-/tree/main/todos', + }, + ], + technologies: ['React', 'React router', 'Redux'], + }, + { + id: 'users-list', + name: 'Users list', + description: { + en: 'You can see a list of username. By clicking on it, the next column display information about the selected user.', + fr: "Vous pouvez voir une liste de noms d'utilisateur. En cliquant sur l'un d'eux, la colonne suivante affiche les informations à propos de cet utilisateur.", + }, + path: './projects/js-small-apps/users-list/index.html', + repo: [ + { + name: 'Github', + url: 'https://github.com/ArmandPhilippot/js-small-apps/tree/main/users-list', + }, + { + name: 'Gitlab', + url: 'https://gitlab.com/ArmandPhilippot/js-small-apps/-/tree/main/users-list', + }, + ], + technologies: ['Vanilla Javascript', 'Fetch'], + }, +]; + +export default projects; diff --git a/src/js/i18n/i18n.js b/src/js/i18n/i18n.js new file mode 100644 index 0000000..6bdc7cd --- /dev/null +++ b/src/js/i18n/i18n.js @@ -0,0 +1,42 @@ +import I18n from 'i18n-js'; +import en from './locales/en'; +import fr from './locales/fr'; + +const supportedLanguages = [ + { + code: 'en', + label: 'English', + translations: en, + }, + { + code: 'fr', + label: 'Français', + translations: fr, + }, +]; + +supportedLanguages.forEach((locale) => { + I18n.translations[locale.code] = locale.translations; +}); + +function setLocale(locale) { + I18n.locale = locale; +} + +function currentLocale() { + return I18n.currentLocale(); +} + +function translate(name, params = {}) { + return I18n.t(name, params); +} + +const { defaultLocale } = I18n; + +export { + supportedLanguages, + setLocale, + translate, + defaultLocale, + currentLocale, +}; diff --git a/src/js/i18n/locales/en.js b/src/js/i18n/locales/en.js new file mode 100644 index 0000000..9717528 --- /dev/null +++ b/src/js/i18n/locales/en.js @@ -0,0 +1,36 @@ +const en = { + branding: { + description: 'Front-end developer', + }, + nav: { + title: 'Apps list:', + }, + main: { + instructions: + 'Select an app inside menu to see a live preview and app details (description, technologies, repositories).', + project: { + details: { + about: 'About {{name}}', + repo: { + one: 'Repository:', + other: 'Repositories:', + zero: 'Repositories:', + }, + tech: { + one: 'Technology:', + other: 'Technologies:', + zero: 'Technologies:', + }, + }, + }, + }, + footer: { + legalNotice: { + txt: 'Legal notice', + link: 'legal-notice.html', + }, + license: 'License MIT', + }, +}; + +export default en; diff --git a/src/js/i18n/locales/fr.js b/src/js/i18n/locales/fr.js new file mode 100644 index 0000000..9c93012 --- /dev/null +++ b/src/js/i18n/locales/fr.js @@ -0,0 +1,36 @@ +const fr = { + branding: { + description: 'Intégrateur web', + }, + nav: { + title: 'Liste des applications :', + }, + main: { + instructions: + "Sélectionnez une application dans le menu pour afficher un aperçu en direct et les informations sur l'application (description, technologies, dépôts).", + project: { + details: { + about: 'À propos de {{name}}', + repo: { + one: 'Dépôt :', + other: 'Dépôts :', + zero: 'Dépôt :', + }, + tech: { + one: 'Technologie :', + other: 'Technologies :', + zero: 'Technologie :', + }, + }, + }, + }, + footer: { + legalNotice: { + txt: 'Mentions légales', + link: 'mentions-legales.html', + }, + license: 'Licence MIT', + }, +}; + +export default fr; diff --git a/src/js/utilities/animations.js b/src/js/utilities/animations.js new file mode 100644 index 0000000..9a30685 --- /dev/null +++ b/src/js/utilities/animations.js @@ -0,0 +1,45 @@ +/** + * Change the element classes to hide it with a slide out left animation. + * @param {HTMLElement} el - The HTMLElement to hide. + */ +function hideToLeft(el) { + el?.classList.remove('slide-in--left'); + el?.classList.add('slide-out--left'); + setTimeout(() => { + el?.classList.add('hide'); + }, 800); +} + +/** + * Change the element classes to show it with a slide in left animation. + * @param {HTMLElement} el - The HTMLElement to show. + */ +function showFromLeft(el) { + el?.classList.remove('slide-out--left'); + el?.classList.remove('hide'); + el?.classList.add('slide-in--left'); +} + +/** + * Change the element classes to hide it with a slide out bottom animation. + * @param {HTMLElement} el - The HTMLElement to hide. + */ +function hideToBottom(el) { + el?.classList.remove('slide-in--up'); + el?.classList.add('slide-out--bottom'); + setTimeout(() => { + el?.classList.add('hide'); + }, 800); +} + +/** + * Change the element classes to show it with a slide in up animation. + * @param {HTMLElement} el - The HTMLElement to show. + */ +function showFromBottom(el) { + el?.classList.remove('slide-out--bottom'); + el?.classList.remove('hide'); + el?.classList.add('slide-in--up'); +} + +export { hideToLeft, showFromLeft, hideToBottom, showFromBottom }; diff --git a/src/js/utilities/helpers.js b/src/js/utilities/helpers.js new file mode 100644 index 0000000..470c49c --- /dev/null +++ b/src/js/utilities/helpers.js @@ -0,0 +1,19 @@ +/** + * Check the size of the current viewport. + * @returns {Boolean} True if viewport lower than 1200px; false otherwise. + */ +function isSmallVw() { + return window.innerWidth < 1200; +} + +/** + * Check if /assets/styles.js exists (Webpack dev mode). + * @returns {Boolean} True if style.js exists ; false otherwise. + */ +async function isStyleJsExists() { + const filePath = 'assets/js/style.js'; + const response = await fetch(filePath); + return response.status === 200; +} + +export { isSmallVw, isStyleJsExists }; diff --git a/src/scss/abstracts/_functions.scss b/src/scss/abstracts/_functions.scss new file mode 100644 index 0000000..672e5e7 --- /dev/null +++ b/src/scss/abstracts/_functions.scss @@ -0,0 +1,4 @@ +@forward "./functions/convert"; +@forward "./functions/css-vars"; +@forward "./functions/str-replace"; +@forward "./functions/encode"; diff --git a/src/scss/abstracts/_mixins.scss b/src/scss/abstracts/_mixins.scss new file mode 100644 index 0000000..fd28631 --- /dev/null +++ b/src/scss/abstracts/_mixins.scss @@ -0,0 +1,2 @@ +@forward "./mixins/css-vars"; +@forward "./mixins/media-queries"; diff --git a/src/scss/abstracts/_placeholders.scss b/src/scss/abstracts/_placeholders.scss new file mode 100644 index 0000000..079cae7 --- /dev/null +++ b/src/scss/abstracts/_placeholders.scss @@ -0,0 +1,32 @@ +/// List Reset +%reset-list { + list-style-type: none; + margin: 0; + padding: 0; + + li { + margin-bottom: 0; + } +} + +/// Ordered List Reset +%reset-ordered-list { + @extend %reset-list; + + li { + counter-increment: none; + display: list-item; + + &::before { + display: none; + } + } +} + +/// Display an inline list with flexbox +%flex-list { + @extend %reset-list; + + display: flex; + flex-flow: row wrap; +} diff --git a/src/scss/abstracts/_variables.scss b/src/scss/abstracts/_variables.scss new file mode 100644 index 0000000..a8a22a8 --- /dev/null +++ b/src/scss/abstracts/_variables.scss @@ -0,0 +1,103 @@ +@use "sass:math"; +@use "../abstracts/functions" as fun; + +//=========================================================================== +// Ratios +//=========================================================================== + +/// Ratios map +/// @prop {String} keys - Keys are identifiers mapped to a given ratio +/// @prop {Map} value - Value is actual ratio +$ratios: ( + "minor-second": 1.067, + "major-second": 1.125, + "minor-third": 1.2, + "major-third": 1.25, + "perfect-fourth": 1.333, + "augmented-fourth": 1.414, + "perfect-fifth": 1.5, + "golden-number": 1.618, +); + +// Cannot declare the following function in partials due to module loop. +// Also, it will only be used in this file so it is not a problem. + +/// Get ratio +/// @param {String} $name - Ratio name. +/// @return {Integer} The ratio value. +@function get-ratio($name) { + @return map-get($ratios, $name); +} + +//=========================================================================== +// Layout +//=========================================================================== + +/// Breakpoints map +/// @prop {String} keys - Keys are identifiers mapped to a given length +/// @prop {Map} values - Values are actual breakpoints expressed in pixels +$breakpoints: ( + "xs": fun.convert-px(568, "em"), + "sm": fun.convert-px(768, "em"), + "md": fun.convert-px(1024, "em"), + "lg": fun.convert-px(1200, "em"), + "xl": fun.convert-px(1600, "em"), + "2xl": fun.convert-px(1920, "em"), +); + +//=========================================================================== +// Fonts +//=========================================================================== + +/* stylelint-disable -- Fonts name are not keywords, lowercase is not needed. */ +/// Regular font family +/// @type List +$font-family_primary: ("Inter", "Liberation Sans", Arial, sans-serif); + +/// Alternative regular font family +/// @type List +$font-family_secondary: ("Kanit", "Liberation Sans", Arial, sans-serif); + +$line-height: get-ratio("golden-number"); + +$font-size_base: 16px; +$font-size_base-rem: fun.convert-px(16); // font-size_base without unit +$font-size_ratio: get-ratio("minor-third"); +$font-size_sm: $font-size_base-rem * math.pow($font-size_ratio, -1); +$font-size_md: $font-size_base-rem * math.pow($font-size_ratio, 0); +$font-size_lg: $font-size_base-rem * math.pow($font-size_ratio, 1); +$font-size_xl: $font-size_base-rem * math.pow($font-size_ratio, 2); +$font-size_2xl: $font-size_base-rem * math.pow($font-size_ratio, 3); +$font-size_3xl: $font-size_base-rem * math.pow($font-size_ratio, 4); + +//============================================================================ +// Spacings +//============================================================================ + +$spacing_ratio: get-ratio("golden-number"); +$spacing_base: $spacing_ratio * 1rem; + +$spacing_3xs: math.div($spacing_base, 4); +$spacing_2xs: math.div($spacing_base, 3); +$spacing_xs: math.div($spacing_base, 2); +$spacing_sm: math.div($spacing_base, 1.5); +$spacing_md: $spacing_base; +$spacing_lg: $spacing_base * 1.5; + +//============================================================================ +// Colors +//============================================================================ + +$color_black-squeeze: hsl(212, 55%, 97%); +$color_catskill-white: hsl(212, 53%, 92%); +$color_link-water: hsl(212, 51%, 87%); +$color_geyser: hsl(212, 27%, 83%); +$color_gull-gray: hsl(212, 15%, 66%); +$color_pale-sky: hsl(212, 13%, 46%); +$color_nile-blue: hsl(212, 47%, 19%); +$color_firefly: hsl(212, 45%, 11%); +$color_chambray: hsl(212, 45%, 40%); +$color_chathams-blue: hsl(212, 65%, 28%); +$color_chathams-blue-light: hsl(212, 90%, 30%); +$color_chathams-blue-light-opacity-25: hsla(212, 90%, 30%, 0.25); +$color_chathams-blue-dark: hsl(212, 70%, 25%); diff --git a/src/scss/abstracts/functions/_convert.scss b/src/scss/abstracts/functions/_convert.scss new file mode 100644 index 0000000..9f51dc7 --- /dev/null +++ b/src/scss/abstracts/functions/_convert.scss @@ -0,0 +1,16 @@ +@use "sass:math"; + +/// Convert px to rem or em. +/// @param {Number} $px Value in px +/// @param {String} $to Unit. Either "rem" or "em" +/// @param {Number} $standard 1rem (or 1em) = 16px +/// @return {Number} Value in rem or em +@function convert-px($px, $to: "rem", $standard: 16) { + @if $to == "rem" { + @return math.div($px, $standard) + 0rem; // stylelint-disable-line + } @else if $to == "em" { + @return math.div($px, $standard) + 0em; // stylelint-disable-line + } @else { + @error "`$to` must be either `rem` or `em`."; + } +} diff --git a/src/scss/abstracts/functions/_css-vars.scss b/src/scss/abstracts/functions/_css-vars.scss new file mode 100644 index 0000000..89e1a15 --- /dev/null +++ b/src/scss/abstracts/functions/_css-vars.scss @@ -0,0 +1,8 @@ +/// Retrieve a CSS variable value with prefix +/// @see https://dev.to/felipperegazio/css-custom-properties-vars-with-sass-scss-a-practical-architecture-strategy-1m88 +/// @param {String} $name Variable name +/// @param {String} $prefix Variable prefix +/// @return {String} Variable in CSS format +@function get-var($name, $prefix: dap) { + @return var(--#{$prefix}-#{$name}); +} diff --git a/src/scss/abstracts/functions/_encode.scss b/src/scss/abstracts/functions/_encode.scss new file mode 100644 index 0000000..4350185 --- /dev/null +++ b/src/scss/abstracts/functions/_encode.scss @@ -0,0 +1,14 @@ +@use "str-replace" as fun; + +/// Encode a SVG. +/// @param {String} $svg A complete svg (`...`). +/// @return The encoded svg, ready to use for background-image. +@function encode-svg($svg) { + $svg-encoding: (("<", "%3C"), (">", "%3E"), ("#", "%23")); + + @each $char, $encoded in $svg-encoding { + $svg: fun.str-replace($svg, $char, $encoded); + } + + @return "data:image/svg+xml;utf8," + $svg; +} diff --git a/src/scss/abstracts/functions/_str-replace.scss b/src/scss/abstracts/functions/_str-replace.scss new file mode 100644 index 0000000..624bf33 --- /dev/null +++ b/src/scss/abstracts/functions/_str-replace.scss @@ -0,0 +1,20 @@ +/// Replace `$search` with `$replace` in `$string` +/// @author Hugo Giraudel +/// @param {String} $string - Initial string +/// @param {String} $search - Substring to replace +/// @param {String} $replace ('') - New value +/// @return {String} - Updated string +@function str-replace($string, $search, $replace: "") { + $index: str-index($string, $search); + + @if $index { + @return str-slice($string, 1, $index - 1) + $replace + + str-replace( + str-slice($string, $index + str-length($search)), + $search, + $replace + ); + } + + @return $string; +} diff --git a/src/scss/abstracts/mixins/_css-vars.scss b/src/scss/abstracts/mixins/_css-vars.scss new file mode 100644 index 0000000..8e31c96 --- /dev/null +++ b/src/scss/abstracts/mixins/_css-vars.scss @@ -0,0 +1,20 @@ +/// Declare a set of CSS variables properly prefixed. +/// +/// @see https://dev.to/felipperegazio/css-custom-properties-vars-with-sass-scss-a-practical-architecture-strategy-1m88 +/// +/// @param {List} $variables - A list of variable name and value. +/// @param {Bool} $root - Set vars at root. +/// @param {String} $prefix - The variables prefix. +@mixin set-vars($variables, $root: true, $prefix: "dap") { + @if $root { + :root { + @each $name, $value in $variables { + --#{$prefix}-#{$name}: #{$value}; + } + } + } @else { + @each $name, $value in $variables { + --#{$prefix}-#{$name}: #{$value}; + } + } +} diff --git a/src/scss/abstracts/mixins/_media-queries.scss b/src/scss/abstracts/mixins/_media-queries.scss new file mode 100644 index 0000000..fcaea4b --- /dev/null +++ b/src/scss/abstracts/mixins/_media-queries.scss @@ -0,0 +1,81 @@ +@use "../variables" as var; + +/// Media query: media type +/// @param {String} $type - Media type: all, screen, print, retina. +/// @example scss - `@media only screen` equivalent is: +/// @include media("screen"); +@mixin media($type) { + @if $type == "retina" { + $type: "(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi)"; + } @else if $type == "screen" or $type == "print" { + $type: "only #{$type}"; + } + + @media #{$type} { + @content; + } +} + +/// Media query: min-width / max-width +/// @param {String} $from - min-width breakpoint. +/// @param {String} $until - max-width breakpoint. +/// @example scss - `@media (min-width: "md")` equivalent is: +/// @include dimensions("md"); +@mixin dimensions($from: null, $until: null) { + $query: ""; + + @if $from { + @if type-of($from) == "string" { + $size: map-get(var.$breakpoints, $from); + $query: "(min-width: #{$size})"; + } @else { + @error "`$from` must be a string."; + } + } + + @if $from and $until { + $query: $query + " and "; + } + + @if $until { + @if type-of($until) == "string" { + $size: map-get(var.$breakpoints, $until); + $size: calc(#{$size} - 1px); + $query: $query + "(max-width: #{$size})"; + } @else { + @error "`$until` must be a string."; + } + } + + @media #{$query} { + @content; + } +} + +/// Media query: prefers-reduced-motion +/// @param {String} $value - Media query value: `no-preference` or `reduce`. +/// @example scss - @media (prefers-reduced-motion: "reduce") equivalent is: +/// @include motion("reduce"); +@mixin motion($value) { + @if $value == "no-preference" or $value == "reduce" { + @media (prefers-reduced-motion: #{$value}) { + @content; + } + } @else { + @error "Allowed values are `no-preference` and `reduce`."; + } +} + +/// Media query: any-pointer +/// @param {String} $value - Media query value: `fine`, `coarse` or `none`. +/// @example scss - @media (any-pointer: "fine") equivalent is: +/// @include pointer("fine"); +@mixin pointer($value) { + @if $value == "fine" or $value == "coarse" or $value == "none" { + @media (any-pointer: #{$value}) { + @content; + } + } @else { + @error "Allowed values are `fine`, `coarse` and `none`."; + } +} diff --git a/src/scss/base/_animations.scss b/src/scss/base/_animations.scss new file mode 100644 index 0000000..a2df8ab --- /dev/null +++ b/src/scss/base/_animations.scss @@ -0,0 +1,89 @@ +@keyframes fadeIn { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + visibility: visible; + } +} + +@keyframes fadeOut { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + visibility: hidden; + } +} + +@keyframes slideInLeft { + 0% { + margin-left: -100%; + } + + 100% { + margin-left: 0; + visibility: visible; + } +} + +@keyframes slideOutLeft { + 0% { + margin-left: 0; + } + + 100% { + margin-left: -100%; + visibility: hidden; + } +} + +@keyframes slideInUp { + 0% { + margin-bottom: -100vh; + } + + 100% { + margin-bottom: 0; + visibility: visible; + } +} + +@keyframes slideOutBottom { + 0% { + margin-bottom: 0; + } + + 100% { + margin-bottom: -100vh; + visibility: hidden; + } +} + +.fade-in { + animation: fadeIn 1s; +} + +.fade-out { + animation: fadeOut 1s; +} + +.slide-in--left { + animation: slideInLeft 0.8s; +} + +.slide-out--left { + animation: slideOutLeft 0.8s; +} + +.slide-in--up { + animation: slideInUp 1s; +} + +.slide-out--bottom { + animation: slideOutBottom 1s; +} diff --git a/src/scss/base/_base.scss b/src/scss/base/_base.scss new file mode 100644 index 0000000..a157a50 --- /dev/null +++ b/src/scss/base/_base.scss @@ -0,0 +1,3 @@ +html { + overflow: hidden; +} diff --git a/src/scss/base/_colors.scss b/src/scss/base/_colors.scss new file mode 100644 index 0000000..6251b2e --- /dev/null +++ b/src/scss/base/_colors.scss @@ -0,0 +1,21 @@ +@use "../abstracts/mixins" as mix; +@use "../abstracts/variables" as var; +@include mix.set-vars( + ( + color-bg: #{var.$color_black-squeeze}, + color-bg-secondary: #{var.$color_catskill-white}, + color-bg-tertiary: #{var.$color_link-water}, + color-fg: #{var.$color_firefly}, + color-fg-inverted: #{var.$color_black-squeeze}, + color-border: #{var.$color_chathams-blue}, + color-border-light: #{var.$color_geyser}, + color-shadow-darker: #{var.$color_nile-blue}, + color-shadow-dark: #{var.$color_pale-sky}, + color-shadow: #{var.$color_chambray}, + color-shadow-light: #{var.$color_gull-gray}, + color-primary: #{var.$color_chathams-blue}, + color-primary-light: #{var.$color_chathams-blue-light}, + color-primary-light-opacity: #{var.$color_chathams-blue-light-opacity-25}, + color-primary-dark: #{var.$color_chathams-blue-dark}, + ) +); diff --git a/src/scss/base/_fonts.scss b/src/scss/base/_fonts.scss new file mode 100644 index 0000000..192f6b8 --- /dev/null +++ b/src/scss/base/_fonts.scss @@ -0,0 +1,115 @@ +@use "../abstracts/functions" as fun; +@use "../abstracts/mixins" as mix; +@use "../abstracts/variables" as var; + +@font-face { + font-display: swap; + font-family: Kanit; + font-style: normal; + font-weight: 700; + src: url("../fonts/Kanit/Kanit-Bold.woff2") format("woff2"), + url("../fonts/Kanit/Kanit-Bold.woff") format("woff"); +} + +@font-face { + font-display: swap; + font-family: Kanit; + font-style: italic; + font-weight: 700; + src: url("../fonts/Kanit/Kanit-BoldItalic.woff2") format("woff2"), + url("../fonts/Kanit/Kanit-BoldItalic.woff") format("woff"); +} + +@font-face { + font-display: swap; + font-family: Kanit; + font-style: normal; + font-weight: 600; + src: url("../fonts/Kanit/Kanit-SemiBold.woff2") format("woff2"), + url("../fonts/Kanit/Kanit-SemiBold.woff") format("woff"); +} + +@font-face { + font-display: swap; + font-family: Kanit; + font-style: italic; + font-weight: 600; + src: url("../fonts/Kanit/Kanit-SemiBoldItalic.woff2") format("woff2"), + url("../fonts/Kanit/Kanit-SemiBoldItalic.woff") format("woff"); +} + +@font-face { + font-display: swap; + font-family: Kanit; + font-style: normal; + font-weight: 500; + src: url("../fonts/Kanit/Kanit-Medium.woff2") format("woff2"), + url("../fonts/Kanit/Kanit-Medium.woff") format("woff"); +} + +@font-face { + font-display: swap; + font-family: Kanit; + font-style: italic; + font-weight: 500; + src: url("../fonts/Kanit/Kanit-MediumItalic.woff2") format("woff2"), + url("../fonts/Kanit/Kanit-MediumItalic.woff") format("woff"); +} + +@font-face { + font-display: swap; + font-family: Kanit; + font-style: normal; + font-weight: 400; + src: url("../fonts/Kanit/Kanit-Regular.woff2") format("woff2"), + url("../fonts/Kanit/Kanit-Regular.woff") format("woff"); +} + +@font-face { + font-display: swap; + font-family: Kanit; + font-style: italic; + font-weight: 400; + src: url("../fonts/Kanit/Kanit-Italic.woff2") format("woff2"), + url("../fonts/Kanit/Kanit-Italic.woff") format("woff"); +} + +@font-face { + font-display: swap; + font-family: Kanit; + font-style: normal; + font-weight: 300; + src: url("../fonts/Kanit/Kanit-Light.woff2") format("woff2"), + url("../fonts/Kanit/Kanit-Light.woff") format("woff"); +} + +@font-face { + font-display: swap; + font-family: Kanit; + font-style: italic; + font-weight: 300; + src: url("../fonts/Kanit/Kanit-LightItalic.woff2") format("woff2"), + url("../fonts/Kanit/Kanit-LightItalic.woff") format("woff"); +} + +@font-face { + font-display: swap; + font-family: Inter; + font-style: oblique 0deg 10deg; + font-weight: 100 900; + src: url("../fonts/Inter/Inter.woff2?v=3.18") format("woff2"); +} + +@include mix.set-vars( + ( + font-family-primary: #{var.$font-family_primary}, + font-family-secondary: #{var.$font-family_secondary}, + font-size-sm: #{var.$font-size_sm}, + font-size-md: #{var.$font-size_md}, + font-size-lg: #{var.$font-size_lg}, + font-size-xl: #{var.$font-size_xl}, + font-size-2xl: #{var.$font-size_2xl}, + font-size-3xl: #{var.$font-size_3xl}, + line-height: #{var.$line-height}, + ) +); diff --git a/src/scss/base/_helpers.scss b/src/scss/base/_helpers.scss new file mode 100644 index 0000000..d6a9233 --- /dev/null +++ b/src/scss/base/_helpers.scss @@ -0,0 +1,44 @@ +@use "../abstracts/functions" as fun; +@use "../abstracts/mixins" as mix; + +.hide { + display: none !important; +} + +/* Text meant only for screen readers. */ +.screen-reader-text { + border: 0; + clip: rect(1px, 1px, 1px, 1px); + height: fun.convert-px(1); + overflow: hidden; + padding: 0; + position: absolute !important; + width: fun.convert-px(1); + word-break: normal; + word-wrap: normal !important; /* Many screen reader and browser combinations announce broken words as they would appear visually. */ + + &:focus { + background: fun.get-var(color-bg); + border: fun.convert-px(3) solid fun.get-var(color-border); + box-shadow: 0 0 fun.convert-px(2) fun.convert-px(2) + fun.get-var(color-shadow-light); + clip: auto !important; + color: fun.get-var(color-primary); + display: block; + font-size: fun.get-var(font-size-md); + font-weight: 600; + height: auto; + left: 0; + padding: fun.get-var(spacing-sm) fun.get-var(spacing-md); + top: 0; + width: auto; + z-index: 100000; + } +} + +@include mix.motion("reduce") { + * { + animation: none !important; + transition: none !important; + } +} diff --git a/src/scss/base/_spacings.scss b/src/scss/base/_spacings.scss new file mode 100644 index 0000000..e908caf --- /dev/null +++ b/src/scss/base/_spacings.scss @@ -0,0 +1,24 @@ +@use "../abstracts/functions" as fun; +@use "../abstracts/mixins" as mix; +@use "../abstracts/variables" as var; +@include mix.set-vars( + ( + spacing-3xs: var.$spacing_3xs, + spacing-2xs: var.$spacing_2xs, + spacing-xs: var.$spacing_xs, + spacing-sm: var.$spacing_sm, + spacing-md: var.$spacing_md, + spacing-lg: var.$spacing_lg, + toolbar-height: fun.convert-px(60), + ) +); + +@include mix.media("screen") { + @include mix.dimensions("lg") { + @include mix.set-vars( + ( + toolbar-height: fun.convert-px(0), + ) + ); + } +} diff --git a/src/scss/base/_typography.scss b/src/scss/base/_typography.scss new file mode 100644 index 0000000..b0504b7 --- /dev/null +++ b/src/scss/base/_typography.scss @@ -0,0 +1,48 @@ +@use "../abstracts/functions" as fun; + +*::selection { + background: fun.get-var(color-primary-light-opacity); +} + +body { + background: fun.get-var(color-bg); + color: fun.get-var(color-fg); + font-size: fun.get-var(font-size-md); + line-height: fun.get-var(line-height); +} + +h1, +h2, +h3, +h4, +h5, +h6, +p, +ul { + margin: 0 0 fun.get-var(spacing-sm); +} + +a { + color: fun.get-var(color-primary); + text-decoration-thickness: fun.convert-px(2); + text-underline-offset: fun.convert-px(3); + transition: all 0.3s ease-in-out 0s; + + &:hover, + &:focus { + color: fun.get-var(color-primary-light); + text-decoration-color: fun.get-var(color-primary-light); + text-decoration-thickness: fun.convert-px(4); + } + + &:focus { + outline: fun.get-var(color-primary) dotted fun.convert-px(1); + } + + &:active { + color: fun.get-var(color-primary-dark); + outline: none; + text-decoration-color: fun.get-var(color-primary-dark); + text-decoration-thickness: fun.convert-px(2); + } +} diff --git a/src/scss/components/_buttons.scss b/src/scss/components/_buttons.scss new file mode 100644 index 0000000..bc7959e --- /dev/null +++ b/src/scss/components/_buttons.scss @@ -0,0 +1,21 @@ +@use "../abstracts/functions" as fun; + +.btn { + background: fun.get-var(color-bg); + border: 0; + color: fun.get-var(color-primary); + cursor: pointer; + display: block; + font-weight: 600; + + &:hover, + &:focus { + background: fun.get-var(color-primary); + color: fun.get-var(color-fg-inverted); + } + + &:active { + background: fun.get-var(color-bg); + color: fun.get-var(color-primary); + } +} diff --git a/src/scss/layout/_footer.scss b/src/scss/layout/_footer.scss new file mode 100644 index 0000000..7dce0dc --- /dev/null +++ b/src/scss/layout/_footer.scss @@ -0,0 +1,40 @@ +@use "../abstracts/functions" as fun; +@use "../abstracts/mixins" as mix; + +.footer { + align-items: center; + background: fun.get-var(color-bg-secondary); + border-top: fun.convert-px(1) solid fun.get-var(color-border-light); + display: flex; + flex-flow: row wrap; + font-family: fun.get-var(font-family-secondary); + font-size: fun.get-var(font-size-md); + gap: fun.get-var(spacing-3xs); + justify-content: center; + padding: fun.get-var(spacing-sm) fun.get-var(spacing-md) + calc(#{fun.get-var(toolbar-height)} + #{fun.get-var(spacing-sm)}); + + @include mix.media("screen") { + @include mix.dimensions("lg") { + box-shadow: 0 -1px 2px 0 fun.get-var(color-shadow); + padding: fun.get-var(spacing-sm) fun.get-var(spacing-md); + } + } + + .nav { + display: inline-flex; + gap: fun.get-var(spacing-3xs); + + &::after { + content: "/"; + } + } +} + +.copyright { + align-items: center; + display: flex; + flex-flow: row wrap; + gap: fun.get-var(spacing-3xs); + justify-content: center; +} diff --git a/src/scss/layout/_grid.scss b/src/scss/layout/_grid.scss new file mode 100644 index 0000000..3749678 --- /dev/null +++ b/src/scss/layout/_grid.scss @@ -0,0 +1,43 @@ +@use "../abstracts/functions" as fun; +@use "../abstracts/mixins" as mix; + +.body { + display: grid; + grid-template-columns: minmax(0, 1fr); + grid-template-rows: minmax(0, 1fr) max-content; + height: 100vh; + position: relative; + + @include mix.media("screen") { + @include mix.dimensions("lg") { + grid-template-columns: 1.5fr 4fr; + } + + @include mix.dimensions("xl") { + grid-template-columns: 1fr 4fr; + } + } +} + +.header { + grid-column: 1; + grid-row: 1; + width: 100%; +} + +.main { + grid-column: 1; + grid-row: 1 / -1; + + @include mix.media("screen") { + @include mix.dimensions("lg") { + grid-column: 2; + } + } +} + +.footer { + grid-column: 1; + grid-row: 2; + width: 100%; +} diff --git a/src/scss/layout/_header.scss b/src/scss/layout/_header.scss new file mode 100644 index 0000000..cbc1693 --- /dev/null +++ b/src/scss/layout/_header.scss @@ -0,0 +1,143 @@ +@use "../abstracts/functions" as fun; +@use "../abstracts/mixins" as mix; + +.header { + background: fun.get-var(color-bg-secondary); + overflow-y: auto; + padding: fun.get-var(spacing-md) + clamp(#{fun.get-var(spacing-md)}, 3vw, #{fun.get-var(spacing-lg)}); + scrollbar-color: fun.get-var(color-primary-light-opacity) + fun.get-var(color-bg-tertiary); + z-index: 5; + + @include mix.media("screen") { + @include mix.dimensions("lg") { + box-shadow: 0 -1px 2px 0 fun.get-var(color-shadow); + } + } +} + +.branding { + margin-bottom: clamp( + #{fun.get-var(spacing-sm)}, + 3vw, + #{fun.get-var(spacing-md)} + ); + text-align: center; + + &__title { + font-family: fun.get-var(font-family-secondary); + font-size: clamp( + #{fun.get-var(font-size-2xl)}, + 5vw, + #{fun.get-var(font-size-3xl)} + ); + font-weight: 500; + margin: fun.get-var(spacing-xs) 0 fun.get-var(spacing-3xs); + } + + &__link { + background: linear-gradient( + to top, + fun.get-var(color-primary-light) fun.convert-px(5), + transparent fun.convert-px(5) + ) + center / 0 100% no-repeat; + text-decoration: none; + transition: all 0.5s ease-in-out 0s; + + &:hover, + &:focus { + background-size: 100% 100%; + } + + &:active { + background-size: 0 100%; + } + } + + &__description { + font-family: fun.get-var(font-family-secondary); + font-size: clamp( + #{fun.get-var(font-size-md)}, + 3vw, + #{fun.get-var(font-size-lg)} + ); + font-weight: 400; + letter-spacing: fun.convert-px(1); + margin: 0; + text-transform: uppercase; + } +} + +.logo { + margin: auto; + position: relative; + width: max-content; + + &__image { + backface-visibility: hidden; + border: fun.convert-px(3) solid fun.get-var(color-border-light); + border-radius: 50%; + box-shadow: 0 0 fun.convert-px(6) fun.convert-px(1) + fun.get-var(color-shadow-darker); + left: 0; + position: absolute; + top: 0; + width: 100%; + + &--back { + transform: rotateY(180deg); + } + } + + &__link { + display: block; + height: clamp(#{fun.convert-px(75)}, 15vmin, #{fun.convert-px(90)}); + transform-style: preserve-3d; + transition: all 0.6s linear 0s; + width: clamp(#{fun.convert-px(75)}, 15vmin, #{fun.convert-px(90)}); + + &:hover, + &:focus { + outline: none; + transform: rotateY(180deg); + } + + &:hover &, + &:focus & { + &__image { + &--front { + transform: none; + } + + &--back { + transform: rotateY(180deg); + } + } + } + + &:focus & { + &__image { + box-shadow: 0 0 fun.convert-px(6) fun.convert-px(1) + fun.get-var(color-shadow-dark), + 0 0 0 fun.convert-px(5) fun.get-var(color-primary-light-opacity); + outline: none; + } + } + + &:active & { + &__image { + box-shadow: 0 0 fun.convert-px(6) fun.convert-px(1) + fun.get-var(color-shadow-dark), + 0 0 0 fun.convert-px(7) fun.get-var(color-primary-light-opacity); + } + } + } + + &:hover & { + &__link { + transform: rotateY(180deg); + } + } +} diff --git a/src/scss/layout/_main.scss b/src/scss/layout/_main.scss new file mode 100644 index 0000000..8ab842b --- /dev/null +++ b/src/scss/layout/_main.scss @@ -0,0 +1,138 @@ +@use "../abstracts/functions" as fun; +@use "../abstracts/mixins" as mix; +@use "../abstracts/placeholders"; + +.main { + display: flex; + flex-flow: column nowrap; + height: calc(100% - #{fun.get-var(toolbar-height)}); + + @include mix.media("screen") { + @include mix.dimensions("lg") { + display: grid; + grid-template-columns: 5fr 2fr; + } + + @include mix.dimensions("xl") { + grid-template-columns: 4fr 1fr; + } + } +} + +// NoScript extension seems to replace noscript tag with a span. +.main > span, +.instructions, +noscript { + background: fun.get-var(color-bg); + padding: fun.get-var(spacing-md); + text-align: center; + + @include mix.media("screen") { + @include mix.dimensions("lg") { + grid-column: 1 / -1; + } + } +} + +.instructions { + align-items: center; + display: flex; + justify-content: center; +} + +.legal-notice { + height: 100%; + overflow-y: auto; + padding: clamp(#{fun.get-var(spacing-md)}, 3vw, #{fun.get-var(spacing-lg)}); + scrollbar-color: fun.get-var(color-primary-light-opacity) + fun.get-var(color-bg-tertiary); + width: 100%; + + @include mix.media("screen") { + @include mix.dimensions("lg") { + grid-column: 1 / -1; + } + } +} + +.project-preview { + background: fun.get-var(color-bg); + flex: 0 1 100%; + min-height: 0; + width: 100%; + + @include mix.media("screen") { + @include mix.dimensions("lg") { + height: 100%; + } + } + + iframe { + border: 0; + height: 100%; + width: 100%; + } +} + +.project-details { + background: fun.get-var(color-bg-secondary); + box-shadow: 0 -1px 2px 0 fun.get-var(color-shadow); + flex: 1 0 100%; + overflow-y: auto; + padding: fun.get-var(spacing-md); + scrollbar-color: fun.get-var(color-primary-light-opacity) + fun.get-var(color-bg-tertiary); + + @include mix.media("screen") { + @include mix.dimensions("lg") { + font-size: fun.get-var(font-size-md); + } + } + + &__description { + margin-bottom: fun.get-var(spacing-md); + white-space: pre-wrap; + } + + .list { + &--tech { + padding-left: fun.get-var(spacing-sm); + } + + &--repos { + @extend %flex-list; + + gap: fun.get-var(spacing-xs); + } + + &__link { + background-repeat: no-repeat; + background-size: contain; + box-shadow: 0 0 0 0 fun.get-var(color-shadow); + display: block; + height: fun.convert-px(50); + transition: transform 0.3s ease-in-out 0s, + box-shadow 0.15s ease-in-out 0.15s; + width: fun.convert-px(50); + + &--github { + background: url(#{fun.encode-svg('')}); + } + + &--gitlab { + background: url(#{fun.encode-svg('')}); + } + + &:hover, + &:focus { + box-shadow: fun.convert-px(-1) fun.convert-px(1) fun.convert-px(4) + fun.convert-px(2) fun.get-var(color-shadow-light); + transform: scale(1.15); + } + + &:active { + opacity: 1; + } + } + } +} diff --git a/src/scss/layout/_nav.scss b/src/scss/layout/_nav.scss new file mode 100644 index 0000000..98e4cb5 --- /dev/null +++ b/src/scss/layout/_nav.scss @@ -0,0 +1,70 @@ +@use "../abstracts/functions" as fun; +@use "../abstracts/placeholders"; + +.nav { + text-align: center; + + &__label { + font-weight: 600; + } + + &__list { + @extend %reset-list; + + .btn { + width: 100%; + } + } + + &:not(&--footer) &__item { + margin: fun.get-var(spacing-2xs) 0; + } + + &:not(&--footer) &__link { + background-image: linear-gradient( + to left, + #{fun.get-var(color-bg)} 0, + #{fun.get-var(color-bg)} 50%, + #{fun.get-var(color-primary)} 50% + ); + background-position: 100% 0; + background-size: 200% 100%; + border: fun.convert-px(3) solid fun.get-var(color-border); + border-radius: fun.convert-px(50); + display: block; + font-weight: 600; + margin: auto; + padding: fun.get-var(spacing-3xs); + position: relative; + text-decoration: none; + transition: all 0.4s ease-in-out 0s; + width: 75%; + + &:hover, + &:focus { + background-position: 0 0; + color: fun.get-var(color-fg-inverted); + } + + &:active { + background-position: 100% 0; + color: fun.get-var(color-primary-dark); + text-decoration: fun.convert-px(1) solid underline; + } + + &--selected { + background: fun.get-var(color-primary-dark); + box-shadow: inset 0 0 0 4px fun.get-var(color-bg); + color: fun.get-var(color-fg-inverted); + + &:hover, + &:focus { + background: fun.get-var(color-primary-light); + } + } + } + + .btn { + margin: auto; + } +} diff --git a/src/scss/layout/_toolbar.scss b/src/scss/layout/_toolbar.scss new file mode 100644 index 0000000..00e2bd7 --- /dev/null +++ b/src/scss/layout/_toolbar.scss @@ -0,0 +1,34 @@ +@use "../abstracts/functions" as fun; + +.toolbar { + align-items: center; + background: fun.get-var(color-primary); + bottom: 0; + box-shadow: 0 -1px 2px 0 fun.get-var(color-shadow-dark); + color: fun.get-var(color-fg-inverted); + display: flex; + flex-flow: row nowrap; + gap: fun.get-var(spacing-xs); + height: fun.get-var(toolbar-height); + justify-content: center; + left: 0; + padding: 0 fun.get-var(spacing-sm); + position: absolute; + right: 0; + z-index: 2; + + & > &__options { + background: fun.get-var(color-primary); + color: fun.get-var(color-fg-inverted); + font-size: fun.get-var(font-size-sm); + height: 85%; + line-height: inherit; + padding: 0 fun.get-var(spacing-xs); + + &:hover, + &:focus { + background: fun.get-var(color-bg); + color: fun.get-var(color-primary); + } + } +} diff --git a/src/scss/style.scss b/src/scss/style.scss new file mode 100644 index 0000000..96c8fe5 --- /dev/null +++ b/src/scss/style.scss @@ -0,0 +1,40 @@ +@charset 'utf-8'; + +/** + * 1.0 Vendors + * + * Import each files separately to define vendors styles order. + */ +@use "modern-normalize"; + +/** + * 2.0 Base + * + * Define some standard styles and CSS variables (colors, fonts...). + */ +@use "base/base"; +@use "base/animations"; +@use "base/colors"; +@use "base/fonts"; +@use "base/helpers"; +@use "base/spacings"; +@use "base/typography"; + +/** + * 3.0 Layout + * + * Define website layout. + */ +@use "layout/grid"; +@use "layout/header"; +@use "layout/main"; +@use "layout/footer"; +@use "layout/toolbar"; +@use "layout/nav"; + +/** +* 4.0 Components +* +* Define styles for all kind of specific modules like buttons, widgets... +*/ +@use "components/buttons"; -- cgit v1.2.3