diff --git a/.phpactor.yml b/.phpactor.yml new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md index a2a8259..30f358f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,15 @@ +## Structure + +- Tech wall +- Pro projects +- Side projects +- Find me on +- Volunteering +- Contact form +- Reading list + + ## Technology walls Mosaic of squared technologies logos diff --git a/assets/locales/en.yaml b/assets/locales/en.yaml new file mode 100644 index 0000000..e63dfe1 --- /dev/null +++ b/assets/locales/en.yaml @@ -0,0 +1,43 @@ +external-website: External website + +last-update: "Last update :" +about-website: "About this website" + +page: + title: Matthieu Bessat - Web developer + +profile: + intro: + main: Hi! I'm Matthieu + secondary: I like to program stuff + +technologies: + title: Some of the technologies that I'm currently working with + +projects: + pro: + title: Highlighted professional projects + side: + title: Highlighted side projects + +links: + title: Find me on... + +contact: + no-script: If you want to use the contact form, you will need to enable javascript to run free-ish software + title: Contact me + use-email: "You can contact me via e-mail:" + use-form: "Or you can use the form bellow if you prefer not opening your email client." + form: + name: Your name + email: Your email + subject: The subject + message: Your message + submit: Submit + sucess: Thanks, I will try to read pursly your message + fail: Failure + +locales: + choose: "Choose your language:" + + diff --git a/assets/locales/fr.yaml b/assets/locales/fr.yaml new file mode 100644 index 0000000..93d0835 --- /dev/null +++ b/assets/locales/fr.yaml @@ -0,0 +1,47 @@ +go-back-to-main: Revenir à la page principale +external-website: Site web externe + +last-update: Dernière mise à jour le +about-website: A propos de ce site + +page: + title: Matthieu Bessat - Développeur web + +profile: + intro: + main: Salut, je m'appelle Matthieu + secondary: J'aime bien programmer des trucs + +technologies: + title: Quelques des technologies avec lesquelles je travaille en ce moment + +projects: + title: Projets + project: Projet + pro: + title: Projets professionels + side: + title: Autres projets + background: Contexte + solution: Solutions + technologies: Technologies utilisées + + +links: + title: Retrouvez moi sur... + +contact: + no-script: Si vous voulez utiliser le formulaire de contact, vous devez activer le javascript (le site propose un code libre) + title: Me contacter + use-email: "Vous pouvez me contacter par e-mail :" + use-form: "Ou vous pouvez utiliser le formulaire ci-dessous" + form: + name: Votre nom + email: Votre email + subject: Sujet du message + message: Votre message + submit: Soumettre + +locales: + choose: "Choisissez votre langue : " + diff --git a/assets/scripts/contact.js b/assets/scripts/contact.js new file mode 100644 index 0000000..5501f26 --- /dev/null +++ b/assets/scripts/contact.js @@ -0,0 +1,52 @@ +import axios from 'axios' + +function contactFormSetup() { + /** + * Contact form javascript implementation + */ + const namespace = "lefuturiste" + const postURL = "https://contact-form.thingmill.fr" + + const form = document.getElementById('contact-form') + const formName = document.getElementById('contact-form-name') + const formEmail = document.getElementById('contact-form-email') + const formSubject = document.getElementById('contact-form-subject') + const formMessage = document.getElementById('contact-form-message') + + const formSubmit = document.getElementById('contact-form-submit') + const formButtonLoading = document.getElementById('contact-form-loading') + const successAlert = document.getElementById('contact-alert-success') + const errorAlert = document.getElementById('contact-alert-error') + + form.onsubmit = (e) => { + e.preventDefault() + console.log('> Attempting to send a message', formName.value, formEmail.value, formMessage.value) + let savedButtonText = formSubmit.textContent; + formButtonLoading.style.display = 'block' + formSubmit.style.display = 'none' + axios.post(postURL + '/' + namespace, { + name: formName.value, + email: formEmail.value, + subject: formSubject.value, + message: formMessage.value + }).then(() => { + console.log('> Message sent') + successAlert.style.display = "block"; + formButtonLoading.style.display = 'none' + formSubmit.style.display = 'block' + formSubmit.textContent = savedButtonText; + + // reset the form? + // formName.value = '' + // formEmail.value = '' + // formSubject.value = '' + // formMessage.value = '' + }).catch((err) => { + errorAlert.style.display = "block"; + console.error(err) + }) + } +} + +export { contactFormSetup } + diff --git a/assets/scripts/main.js b/assets/scripts/main.js index e69de29..40f4dab 100755 --- a/assets/scripts/main.js +++ b/assets/scripts/main.js @@ -0,0 +1,6 @@ +import axios from 'axios' + +import { contactFormSetup } from './contact' + +contactFormSetup() + diff --git a/assets/styles/components/button.scss b/assets/styles/components/button.scss new file mode 100644 index 0000000..834686c --- /dev/null +++ b/assets/styles/components/button.scss @@ -0,0 +1,8 @@ +.btn { + border-radius: 2px; + box-shadow: 1px 2px 4px 0 rgba(0,0,0,.08); + padding: 1em 1.5em; + margin-top: 1em; + border: 1px solid #ccc; + font-size: 16px; +} diff --git a/assets/styles/components/chips.scss b/assets/styles/components/chips.scss new file mode 100644 index 0000000..042d048 --- /dev/null +++ b/assets/styles/components/chips.scss @@ -0,0 +1,13 @@ +.chips { + display: flex; + flex-wrap: wrap; + + .chip { + margin-right: .5em; + padding: 0.5em 0.9em; + background: $primary; + color: white; + font-size: .9em; + border-radius: 10rem; + } +} diff --git a/assets/styles/components/contact.scss b/assets/styles/components/contact.scss new file mode 100644 index 0000000..c21afac --- /dev/null +++ b/assets/styles/components/contact.scss @@ -0,0 +1,125 @@ +.means-of-contact { + + p { + margin-bottom: 1em; + } +} + +.contact-form-container { + + form { + border-radius: 3px; + border: 1px dashed #80808054; + padding: 1em; + } + + .contact-form-first-group { + display: flex; + + .contact-form-input:first-of-type { + margin-right: .4em; + } + .contact-form-input:nth-of-type(2) { + margin-left: .4em; + } + } + + .contact-form-input { + width: 100%; + margin-top: 1em; + } + + input, textarea { + width: 100%; + margin-top: .6em; + border-radius: 3px; + padding: .6em; + border: 1px solid #dbdbdb; + box-shadow: 1px 2px 4px 0 rgba(0,0,0,.1); + } + + .contact-form-message textarea { + min-height: 8em; + } + + label { + display: block; + } + + + @keyframes rotating { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } + } + + .loading-button { + display: none; + svg { + animation: rotating 2s linear infinite; + } + } + + .contact-form-submit { + + } + + .contact-form-container { + display: flex; + justify-content: end; + } +} + + +.alert { + padding: 1em; + margin-bottom: 1em; + border-radius: 3px; + + .alert-content { + display: flex; + align-items: center; + } +} + +.alert.alert-success { + background: green; + color: white; +} + +.alert.alert-error { + background: red; + color: white; +} + +.alert .alert-icon { + padding-right: 0.6em; +} + +@media (min-width: $sm-breakpoint) { + .contact-form-first-group { + .contact-form-input { + margin-top: 0; + } + } +} + +@media (max-width: $sm-breakpoint) { + .contact-form { + .contact-form-first-group { + display: block; + .contact-form-input { + margin-left: 0 !important; + margin-right: 0 !important; + } + } + } + + + .contact-form-input:first-of-type { + margin-top: 0; + } +} diff --git a/assets/styles/components/footer.scss b/assets/styles/components/footer.scss new file mode 100644 index 0000000..82ad427 --- /dev/null +++ b/assets/styles/components/footer.scss @@ -0,0 +1,46 @@ +.footer-buffer { + height: 2em; +} + +footer { + margin-top: auto; + border-top: 1px solid gray; + border-color: rgb(84, 91, 94) !important; +} + +.footer-container { + padding: 2em 0; + display: flex; + justify-content: space-between; +} + +.footer-left div { + margin-bottom: .5em; +} + +.locale-switch-large { + display: flex; + align-items: center; + + .l { + margin-right: 1em; + } + .r { + a { + margin-right: .2em; + } + } +} + +.locale-switch-short { + display: none; +} + +@media (max-width: $sm-breakpoint) { + .locale-switch-large { + display: none; + } + .locale-switch-short { + display: block; + } +} diff --git a/assets/styles/components/links.scss b/assets/styles/components/links.scss new file mode 100644 index 0000000..d892e2d --- /dev/null +++ b/assets/styles/components/links.scss @@ -0,0 +1,57 @@ +.links-container { + display: flex; + justify-content: center; +} + +.links { + width: 80%; + display: flex; + justify-content: space-around; + flex-wrap: wrap; + .link-card { + margin: 1em; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + min-width: 10em; + } + + .link-card-title { + margin-top: .9em; + } + + .link-card-logo { + padding: .5em; + width: 4em; + height: 4em; + overflow: hidden; + display: flex; + justify-content: center; + align-content: center; + + img { + max-width: 4em; + max-height: 4em; + } + } +} + +@media (max-width: $sm-breakpoint) { + .links { + width: 100%; + .link-card { + justify-content: center; + } + + } +} + +@media (max-width: $xs-breakpoint) { + .links { + width: 100%; + .link-card { + min-width: 6em; + } + } +} \ No newline at end of file diff --git a/assets/styles/components/profile.scss b/assets/styles/components/profile.scss index e35e802..72a77b7 100644 --- a/assets/styles/components/profile.scss +++ b/assets/styles/components/profile.scss @@ -95,16 +95,66 @@ img.main-profile { .profile-content .title-1 { text-align: center; - font-size: 3.5em; + font-size: 3em; margin-bottom: 0.4em; + line-height: 1.5em; } .profile-content .title-2 { text-align: center; - font-size: 2em; + font-size: 1.5em; margin-bottom: 1em; } .profile-content .subtitle { font-size: 1.5em; } + + +@media (max-width: $sm-breakpoint) { + .profile-container { + flex-direction: column; + } + .profile-imgs-container { + margin-right: 0; + } + .profile-content { + margin-left: 0; + margin-top: -1em; + + .title-1 { + font-size: 2.5em; + } + .title-2 { + font-size: 1.2em; + } + } + + .profile-imgs-container { + transform: scale(0.7); + margin-top: -4em; + } +} + +@media (max-width: $xs-breakpoint) { +.landing-section { + padding-top: 0; +} + + .profile-content { + margin-top: -4em; + padding: 0 1em; + + .title-1 { + font-size: 2.5em; + } + .title-2 { + font-size: 1.2em; + } + } + .profile-imgs-container { + transform: scale(0.6); + margin-top: -4em; + } + +} diff --git a/assets/styles/components/projects.scss b/assets/styles/components/projects.scss index 4965a91..8ce0398 100644 --- a/assets/styles/components/projects.scss +++ b/assets/styles/components/projects.scss @@ -1,14 +1,25 @@ .projects { - display: flex; - flex-wrap: wrap; - + display: grid; + grid-template-columns: 1fr 1fr; + grid-gap: 1em; + .project-card { - width: 20em; - margin-right: .5em; - margin-bottom: .5em; border: 1px solid gray; padding: 1.5em; border-radius: 5px; + + display: flex; + flex-direction: column; + justify-content: space-between; + } + + .project-top { + display: flex; + + } + + .project-left { + width: 100%; } .project-title { @@ -19,4 +30,40 @@ font-size: .9em; color: gray; } -} \ No newline at end of file + .project-img { + width: 5em; + } + + .project-right { + padding-left: .5em; + + } + + .project-bottom { + padding-top: 1em; + } +} + + +@media(min-width: $lg-breakpoint) { + .projects { + grid-template-columns: 1fr 1fr 1fr; + } +} + +@media(max-width: $md-breakpoint) { + .projects { + grid-template-columns: 1fr 1fr; + } +} + +@media (max-width: $sm-breakpoint) { + .projects { + grid-template-columns: 1fr; + } + + .project-right { + padding-left: 1em !important; + } +} + diff --git a/assets/styles/components/technologies.scss b/assets/styles/components/technologies.scss index 87273cd..8a6c281 100644 --- a/assets/styles/components/technologies.scss +++ b/assets/styles/components/technologies.scss @@ -37,6 +37,7 @@ img { width: 100%; } + padding: 2em; } .item-bg img.img-stretch { width: auto; @@ -70,7 +71,13 @@ } -@media (max-width: 1300px) { +@media (min-width: 400px) { + .item-bg { + padding: 1em !important; + } +} + +@media (max-width: 1000px) { .tech-mosaic { .item { flex-basis: percentage(math.div(1, 5)); @@ -78,14 +85,14 @@ } } -@media (max-width: 900px) { +@media (max-width: 800px) { .tech-mosaic { .item { - flex-basis: percentage(math.div(1, 3)); + flex-basis: percentage(math.div(1, 4)); } } } -@media (max-width: 500px) { +@media (max-width: 400px) { .tech-mosaic { .item { flex-basis: percentage(math.div(1, 2)); diff --git a/assets/styles/components/typography.scss b/assets/styles/components/typography.scss index 6ab79a8..594b8c0 100644 --- a/assets/styles/components/typography.scss +++ b/assets/styles/components/typography.scss @@ -1,3 +1,23 @@ +p { + line-height: 1.45em; + margin-bottom: 1em; +} + +h1 { + font-size: xx-large; + margin-bottom: 1em; +} + +h2 { + font-size: x-large; + margin-bottom: .5em; +} + +h3 { + font-size: large; +} + + .section-title { display: grid; font-size: 1.2em; diff --git a/assets/styles/main.scss b/assets/styles/main.scss index a684e91..f524d18 100755 --- a/assets/styles/main.scss +++ b/assets/styles/main.scss @@ -1,5 +1,10 @@ @import url('https://fonts.googleapis.com/css2?family=Libre+Baskerville&display=swap'); +$lg-breakpoint: 1500px; +$md-breakpoint: 1000px; +$sm-breakpoint: 900px; +$xs-breakpoint: 400px; + $primary: #4ba05f; $secondary: #a04b8c; @@ -7,10 +12,21 @@ $secondary: #a04b8c; @import './components/profile.scss'; @import './components/projects.scss'; @import './components/technologies.scss'; +@import './components/links.scss'; @import './components/typography.scss'; +@import './components/footer.scss'; +@import './components/contact.scss'; +@import './components/button.scss'; +@import './components/chips.scss'; -p { - line-height: 1.2em; +body { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +.hidden { + display: none; } .container { @@ -25,7 +41,45 @@ p { font-family: 'Libre Baskerville', serif; } + section { border-bottom: 1px solid gray; padding-bottom: 1em; } + +section:last-of-type { + border-bottom: 0; +} + +.about-header { + margin: 2em 0; +} + + +@media (min-width: $lg-breakpoint) { + .container { + width: 58%; + } +} + +@media (min-width: $md-breakpoint) { + .container { + width: 65%; + } +} + +@media (max-width: $md-breakpoint) { + .container { + width: 75%; + } +} + +@media (max-width: $sm-breakpoint) { + .container { + width: 90%; + } +} + +@media (max-width: $xs-breakpoint) { + +} diff --git a/composer.json b/composer.json index b13e84b..7988cea 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,9 @@ "slim/twig-view": "^3.3", "symfony/yaml": "^6.0", "php-di/slim-bridge": "^3.2", - "symfony/var-dumper": "^6.0" + "symfony/var-dumper": "^6.0", + "adbario/php-dot-notation": "^3.1", + "boronczyk/localization-middleware": "^2.0" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index fc40e71..7eecabc 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,114 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5b64e03d031244562c5d143e11e9aa3e", + "content-hash": "cec518bea90ee444e31994b065bc4f79", "packages": [ + { + "name": "adbario/php-dot-notation", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/adbario/php-dot-notation.git", + "reference": "aca06bf775809808232b8c21ce1ca3a4fca70ace" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/adbario/php-dot-notation/zipball/aca06bf775809808232b8c21ce1ca3a4fca70ace", + "reference": "aca06bf775809808232b8c21ce1ca3a4fca70ace", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.5", + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "^3.6" + }, + "type": "library", + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Adbar\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Riku Särkinen", + "email": "riku@adbar.io" + } + ], + "description": "PHP dot notation access to arrays", + "homepage": "https://github.com/adbario/php-dot-notation", + "keywords": [ + "ArrayAccess", + "dotnotation" + ], + "support": { + "issues": "https://github.com/adbario/php-dot-notation/issues", + "source": "https://github.com/adbario/php-dot-notation/tree/3.1.1" + }, + "time": "2022-03-28T01:09:30+00:00" + }, + { + "name": "boronczyk/localization-middleware", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/tboronczyk/localization-middleware.git", + "reference": "c2370e2631351ee35e21095db258a939fdb61368" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tboronczyk/localization-middleware/zipball/c2370e2631351ee35e21095db258a939fdb61368", + "reference": "c2370e2631351ee35e21095db258a939fdb61368", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "psr/http-message": "^1.0", + "psr/http-server-middleware": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.4", + "slim/psr7": "^1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Boronczyk\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Timothy Boronczyk", + "email": "tboronczyk@gmail.com" + } + ], + "description": "PSR-15 middleware to assist primarily with language-based content negotiation and various other localization tasks.", + "homepage": "https://github.com/tboronczyk/localization-middleware", + "keywords": [ + "localization", + "psr-15", + "slim" + ], + "support": { + "issues": "https://github.com/tboronczyk/localization-middleware/issues", + "source": "https://github.com/tboronczyk/localization-middleware/tree/2.0.1" + }, + "time": "2021-04-15T02:46:25+00:00" + }, { "name": "fig/http-message-util", "version": "1.1.5", @@ -62,6 +168,65 @@ }, "time": "2020-11-24T22:02:12+00:00" }, + { + "name": "laravel/serializable-closure", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "09f0e9fb61829f628205b7c94906c28740ff9540" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/09f0e9fb61829f628205b7c94906c28740ff9540", + "reference": "09f0e9fb61829f628205b7c94906c28740ff9540", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "require-dev": { + "pestphp/pest": "^1.18", + "phpstan/phpstan": "^0.12.98", + "symfony/var-dumper": "^5.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "time": "2022-05-16T17:09:47+00:00" + }, { "name": "nikic/fast-route", "version": "v1.3.0", @@ -112,71 +277,6 @@ }, "time": "2018-02-13T20:26:39+00:00" }, - { - "name": "opis/closure", - "version": "3.6.3", - "source": { - "type": "git", - "url": "https://github.com/opis/closure.git", - "reference": "3d81e4309d2a927abbe66df935f4bb60082805ad" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/opis/closure/zipball/3d81e4309d2a927abbe66df935f4bb60082805ad", - "reference": "3d81e4309d2a927abbe66df935f4bb60082805ad", - "shasum": "" - }, - "require": { - "php": "^5.4 || ^7.0 || ^8.0" - }, - "require-dev": { - "jeremeamia/superclosure": "^2.0", - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.6.x-dev" - } - }, - "autoload": { - "files": [ - "functions.php" - ], - "psr-4": { - "Opis\\Closure\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marius Sarca", - "email": "marius.sarca@gmail.com" - }, - { - "name": "Sorin Sarca", - "email": "sarca_sorin@hotmail.com" - } - ], - "description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.", - "homepage": "https://opis.io/closure", - "keywords": [ - "anonymous functions", - "closure", - "function", - "serializable", - "serialization", - "serialize" - ], - "support": { - "issues": "https://github.com/opis/closure/issues", - "source": "https://github.com/opis/closure/tree/3.6.3" - }, - "time": "2022-01-27T09:35:39+00:00" - }, { "name": "php-di/invoker", "version": "2.3.3", @@ -234,21 +334,21 @@ }, { "name": "php-di/php-di", - "version": "6.3.5", + "version": "6.4.0", "source": { "type": "git", "url": "https://github.com/PHP-DI/PHP-DI.git", - "reference": "b8126d066ce144765300ee0ab040c1ed6c9ef588" + "reference": "ae0f1b3b03d8b29dff81747063cbfd6276246cc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/b8126d066ce144765300ee0ab040c1ed6c9ef588", - "reference": "b8126d066ce144765300ee0ab040c1ed6c9ef588", + "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/ae0f1b3b03d8b29dff81747063cbfd6276246cc4", + "reference": "ae0f1b3b03d8b29dff81747063cbfd6276246cc4", "shasum": "" }, "require": { - "opis/closure": "^3.5.5", - "php": ">=7.2.0", + "laravel/serializable-closure": "^1.0", + "php": ">=7.4.0", "php-di/invoker": "^2.0", "php-di/phpdoc-reader": "^2.0.1", "psr/container": "^1.0" @@ -257,12 +357,12 @@ "psr/container-implementation": "^1.0" }, "require-dev": { - "doctrine/annotations": "~1.2", + "doctrine/annotations": "~1.10", "friendsofphp/php-cs-fixer": "^2.4", "mnapoli/phpunit-easymock": "^1.2", - "ocramius/proxy-manager": "^2.0.2", + "ocramius/proxy-manager": "^2.11.2", "phpstan/phpstan": "^0.12", - "phpunit/phpunit": "^8.5|^9.0" + "phpunit/phpunit": "^9.5" }, "suggest": { "doctrine/annotations": "Install it if you want to use annotations (version ~1.2)", @@ -294,7 +394,7 @@ ], "support": { "issues": "https://github.com/PHP-DI/PHP-DI/issues", - "source": "https://github.com/PHP-DI/PHP-DI/tree/6.3.5" + "source": "https://github.com/PHP-DI/PHP-DI/tree/6.4.0" }, "funding": [ { @@ -306,7 +406,7 @@ "type": "tidelift" } ], - "time": "2021-09-02T09:49:58+00:00" + "time": "2022-04-09T16:46:38+00:00" }, { "name": "php-di/phpdoc-reader", @@ -839,22 +939,22 @@ }, { "name": "slim/slim", - "version": "4.9.0", + "version": "4.10.0", "source": { "type": "git", "url": "https://github.com/slimphp/Slim.git", - "reference": "44d3c9c0bfcc47e52e42b097b6062689d21b904b" + "reference": "0dfc7d2fdf2553b361d864d51af3fe8a6ad168b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim/zipball/44d3c9c0bfcc47e52e42b097b6062689d21b904b", - "reference": "44d3c9c0bfcc47e52e42b097b6062689d21b904b", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/0dfc7d2fdf2553b361d864d51af3fe8a6ad168b0", + "reference": "0dfc7d2fdf2553b361d864d51af3fe8a6ad168b0", "shasum": "" }, "require": { "ext-json": "*", "nikic/fast-route": "^1.3", - "php": "^7.3 || ^8.0", + "php": "^7.4 || ^8.0", "psr/container": "^1.0 || ^2.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.0", @@ -865,13 +965,15 @@ "require-dev": { "adriansuter/php-autoload-override": "^1.2", "ext-simplexml": "*", - "guzzlehttp/psr7": "^2.0", + "guzzlehttp/psr7": "^2.1", + "httpsoft/http-message": "^1.0", + "httpsoft/http-server-request": "^1.0", "laminas/laminas-diactoros": "^2.8", - "nyholm/psr7": "^1.4", + "nyholm/psr7": "^1.5", "nyholm/psr7-server": "^1.0", - "phpspec/prophecy": "^1.14", + "phpspec/prophecy": "^1.15", "phpspec/prophecy-phpunit": "^2.0", - "phpstan/phpstan": "^0.12.99", + "phpstan/phpstan": "^1.4", "phpunit/phpunit": "^9.5", "slim/http": "^1.2", "slim/psr7": "^1.5", @@ -948,7 +1050,7 @@ "type": "tidelift" } ], - "time": "2021-10-05T03:00:00+00:00" + "time": "2022-03-14T14:18:23+00:00" }, { "name": "slim/twig-view", @@ -1017,16 +1119,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.25.0", + "version": "v1.26.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "30885182c981ab175d4d034db0f6f469898070ab" + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab", - "reference": "30885182c981ab175d4d034db0f6f469898070ab", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", "shasum": "" }, "require": { @@ -1041,7 +1143,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.26-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1079,7 +1181,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" }, "funding": [ { @@ -1095,20 +1197,20 @@ "type": "tidelift" } ], - "time": "2021-10-20T20:35:02+00:00" + "time": "2022-05-24T11:49:31+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.25.0", + "version": "v1.26.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825" + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825", - "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", "shasum": "" }, "require": { @@ -1123,7 +1225,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.26-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1162,7 +1264,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" }, "funding": [ { @@ -1178,20 +1280,20 @@ "type": "tidelift" } ], - "time": "2021-11-30T18:21:41+00:00" + "time": "2022-05-24T11:49:31+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.25.0", + "version": "v1.26.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c" + "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c", - "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", "shasum": "" }, "require": { @@ -1200,7 +1302,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.26-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1245,7 +1347,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" }, "funding": [ { @@ -1261,20 +1363,20 @@ "type": "tidelift" } ], - "time": "2022-03-04T08:16:47+00:00" + "time": "2022-05-10T07:21:04+00:00" }, { "name": "symfony/polyfill-php81", - "version": "v1.25.0", + "version": "v1.26.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f" + "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/5de4ba2d41b15f9bd0e19b2ab9674135813ec98f", - "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/13f6d1271c663dc5ae9fb843a8f16521db7687a1", + "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1", "shasum": "" }, "require": { @@ -1283,7 +1385,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.26-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1324,7 +1426,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.26.0" }, "funding": [ { @@ -1340,24 +1442,24 @@ "type": "tidelift" } ], - "time": "2021-09-13T13:58:11+00:00" + "time": "2022-05-24T11:49:31+00:00" }, { "name": "symfony/var-dumper", - "version": "v6.0.6", + "version": "v6.1.0", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "38358405ae948963c50a3aae3dfea598223ba15e" + "reference": "98587d939cb783aa04e828e8fa857edaca24c212" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/38358405ae948963c50a3aae3dfea598223ba15e", - "reference": "38358405ae948963c50a3aae3dfea598223ba15e", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/98587d939cb783aa04e828e8fa857edaca24c212", + "reference": "98587d939cb783aa04e828e8fa857edaca24c212", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { @@ -1412,7 +1514,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.0.6" + "source": "https://github.com/symfony/var-dumper/tree/v6.1.0" }, "funding": [ { @@ -1428,24 +1530,24 @@ "type": "tidelift" } ], - "time": "2022-03-02T12:58:14+00:00" + "time": "2022-05-21T13:34:40+00:00" }, { "name": "symfony/yaml", - "version": "v6.0.3", + "version": "v6.1.2", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "e77f3ea0b21141d771d4a5655faa54f692b34af5" + "reference": "b01c4e7dc6a51cbf114567af04a19789fd1011fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/e77f3ea0b21141d771d4a5655faa54f692b34af5", - "reference": "e77f3ea0b21141d771d4a5655faa54f692b34af5", + "url": "https://api.github.com/repos/symfony/yaml/zipball/b01c4e7dc6a51cbf114567af04a19789fd1011fe", + "reference": "b01c4e7dc6a51cbf114567af04a19789fd1011fe", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -1486,7 +1588,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v6.0.3" + "source": "https://github.com/symfony/yaml/tree/v6.1.2" }, "funding": [ { @@ -1502,20 +1604,20 @@ "type": "tidelift" } ], - "time": "2022-01-26T17:23:29+00:00" + "time": "2022-06-20T12:01:07+00:00" }, { "name": "twig/twig", - "version": "v3.3.8", + "version": "v3.4.1", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "972d8604a92b7054828b539f2febb0211dd5945c" + "reference": "e939eae92386b69b49cfa4599dd9bead6bf4a342" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/972d8604a92b7054828b539f2febb0211dd5945c", - "reference": "972d8604a92b7054828b539f2febb0211dd5945c", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/e939eae92386b69b49cfa4599dd9bead6bf4a342", + "reference": "e939eae92386b69b49cfa4599dd9bead6bf4a342", "shasum": "" }, "require": { @@ -1530,7 +1632,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -1566,7 +1668,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.3.8" + "source": "https://github.com/twigphp/Twig/tree/v3.4.1" }, "funding": [ { @@ -1578,7 +1680,7 @@ "type": "tidelift" } ], - "time": "2022-02-04T06:59:48+00:00" + "time": "2022-05-17T05:48:52+00:00" } ], "packages-dev": [], @@ -1589,5 +1691,5 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.3.0" } diff --git a/config/home.yaml b/config/home.yaml deleted file mode 100644 index 6a95f32..0000000 --- a/config/home.yaml +++ /dev/null @@ -1,16 +0,0 @@ -links: - youtube: https://www.youtube.com/channel/UC0x-gNbsyyrC69HjT5Z44MQ - twitter: https://twitter.com/_le_futuriste - github: https://github.com/lefuturiste - gitlab: https://gitlab.com/lefuturiste - malt: https://www.malt.fr/profile/matthieubessat - discord_guild: https://discord.com/invite/3W94m7ts3H - soundcloud: https://soundcloud.com/lefuturiste - stackoverflow: https://stackoverflow.com/users/7100565/lefuturiste - osm: https://openstreetmap.org/user/lefuturiste - osm_wiki: https://wiki.openstreetmap.org/wiki/User:Lefuturiste - wikipedia_fr: https://fr.wikipedia.org/wiki/Utilisateur:Matthieu2743 - wikidata: https://www.wikidata.org/wiki/User:Matthieu2743 - mastodon: https://mstdn.io/users/lefuturiste - - diff --git a/config/links.yaml b/config/links.yaml new file mode 100644 index 0000000..9f60762 --- /dev/null +++ b/config/links.yaml @@ -0,0 +1,58 @@ +links: + - id: mastodon + name: Mastodon + url: https://mstdn.io/users/lefuturiste + # - id: youtube + # name: YouTube + # url: https://www.youtube.com/channel/UC0x-gNbsyyrC69HjT5Z44MQ + + # - id: twitter + # name: Twitter + # url: https://twitter.com/_le_futuriste + - id: github + name: GitHub + url: https://github.com/lefuturiste + - id: gitlab + name: GitLab + url: https://gitlab.com/lefuturiste + thumbnail: + src: gitlab.svg + alt: Gitlab logo + style: "transform: scale(1.5)" + - id: malt + name: Malt + url: https://www.malt.fr/profile/matthieubessat + thumbnail: + src: malt.svg + - id: discord + name: + fr: Mon serveur discord + en: My discord server + url: https://discord.com/invite/3W94m7ts3H + thumbnail: + src: discord.svg + style: "transform: scale(0.75)" + # - id: soundcloud + # name: Sound Cloud + # url: https://soundcloud.com/lefuturiste + - id: stackoverflow + name: StackOverflow + url: https://stackoverflow.com/users/7100565/lefuturiste + - id: osm + name: OpenStreetMap + url: https://openstreetmap.org/user/lefuturiste + # - id: osm_wiki + # name: + # en: OpenStreetMap wiki + # fr: Le wiki d'OpenStreetMap + # url: https://wiki.openstreetmap.org/wiki/User:Lefuturiste + - id: wikipedia_fr + name: Wikipedia + url: https://fr.wikipedia.org/wiki/Utilisateur:Matthieu2743 + thumbnail: + src: wikipedia.svg + alt: Wikipedia logo + - id: wikidata + name: Wikidata + url: https://www.wikidata.org/wiki/User:Matthieu2743 + diff --git a/config/misc.yaml b/config/misc.yaml new file mode 100644 index 0000000..ea307f2 --- /dev/null +++ b/config/misc.yaml @@ -0,0 +1,4 @@ +email: bonjour@matthieubessat.fr +updated_at: '2022-07-04' + + diff --git a/config/projects.yaml b/config/projects.yaml index 796d227..cf6f858 100644 --- a/config/projects.yaml +++ b/config/projects.yaml @@ -1,99 +1,214 @@ pro_projects: - - id: associations.espacecondorcet + - id: forum_asso + detailled_page: true date: 2020-07 link: https://associations.espacecondorcet.org + images: + - id: public_list + name: + fr: Liste des associations + en: f + description: + fr: Page regroupant toute les associations de la localité triées par catégories + en: Public page + - id: public_page + name: + fr: Page d'une association + en: e + description: + fr: Page publique d'une association contenenant toute les informations de sa fiche + en: e + - id: manage_organizations + name: + fr: Gestion des associations + en: e + description: + fr: L'administrateur peut ajouter, modifier, valider toute les associations depuis une unique interface + en: e + - id: changes_approval + name: + fr: Vérification par l'administrateur + en: e + description: + fr: L'administrateur peut valider ou refuser les changements apportés par l'association + en: e + - id: edit_main + name: + fr: Panel associatif + en: e + description: + fr: Les associations disposent d'un espace réservé pour remplir leur fiche + en: e + - id: edit_images + name: + fr: Images et vidéos + en: e + description: + fr: Les associations peuvent ajouter des images ou des vidéos + en: e technologies: - - Node.js - - TypeScript - - Express.js - - Vue.js - - Vuetify - - MongoDB - locales: - fr: - name: Annuaire assocatif administré - description: Création d'une application web de gestion de contenu - background: | - En Juin 2020, l'Espace condorcet voulais un moyen de partager les associations locales - en: - name: Administered association directory - description: Created a web app of content management - background: | - In June of 2020, the Espace Condorcet wanted a way to have the public discover associations around them in around the town. + - node + - typescript + - express + - vue + - vuetify + - mongodb + name: + fr: Annuaire assocatif administré + en: Administered association directory + description: + fr: | + Application web de gestion de fiche associative avec interface administrative. + en: | + Web application of association management with administration interface. + background: + fr: | + En Juin 2020, l'Espace condorcet cherchait un moyen numérique afin de substituer le forum associatif physique qui était compromis pour cause sanitaire, ils voulait un moyen simple pour que les associations puissent se décrire et en même temps que le personnel de l'espace condorcet puissent valider le contenu facilement sans d'étapes manuelle pour la publication. + en: | + In June of 2020, the Espace Condorcet wanted a way to have the public discover associations around them in around the town. + solution: + fr: | + Afin de répondre aux besoins du client, j'ai développé pendant l'été le logiciel qui se compose en plusieurs parties : une partie accessible par tous qui permet de naviguer dans les associations basé sur un serveur Node.js et une interface d'administration et d'édition par les associations qui communique avec une API web en Node.js qui va ensuite mettre à jour la base de données MongoDB. + en: | + Foo bar + action: + fr: Site web externe + en: External website + thumbnail: + src: condorcet.jpg + alt: + fr: Un dessin enfantin représentant le soleil éclairant un chemin + en: A childish drawing representing the sun lighting up a footway. - id: tracklift + detailled_page: false date: 2022-01 link: https://tracklift.fr + name: Tracklift + # En 2021 Socobat Environnement avait le besoin d'une application web afin de modéliser la gestion des déchets sur des chantiers d'ascenseurs (collecte, retraitement, revalorisation) et de partager le résultat à des clients. J'ai démarré le développement à partir d'une base et j'ai mit en production le site web. + description: + fr: | + Gestion de la récupération, le traitement et la revalorisation des déchets issues des chantiers d'ascenseurs. + # De façon plus général, logiciel de gestion de l'activité d'une entreprise. + en: | + Management of the recovering, the processing and the revaluation of wastes from elevator's worksite + thumbnail: + src: logo_square.svg + alt: A 'T' letter encapsulated into brackets technologies: - - Vue - - Vuetify - - Symfony 5 - - Api Platform - - PHP - - MySQL - locales: - fr: - name: Tracklift - description: Site pour gérer l'activité de démontage d'ascenseur de l'entreprise Socobat - background: | - Sometimes none run start somebody learn card. - Once remain foreign player task decision social. Student police lawyer pattern local. - en: - name: Tracklift - description: Web site to manage business of the tracklift society - background: | - Sometimes none run start somebody learn card. - Once remain foreign player task decision social. Student police lawyer pattern local. + - vue + - vuetify + - symfony + - api-platform + - php + - mysql side_projects: - id: werobot + detailled_page: false date: 2018-11 link: https://werobot.fr technologies: - - Nuxt.js - - Vue.js - - PHP - - Slim Framework - locales: - fr: - name: WeRobot.fr - description: Création d'un site vitrine accompagné d'un blog pour l'association robotique locale We Robot - en: - name: WeRobot.fr - description: Creation of a presentation website along with a blog for the robotic club We Robot + - nuxt + - vue + - php + - slim + - mariadb + thumbnail: + src: logo-300.png + alt: + fr: Une flèche rouge poussant vers le haut la lettre W bleu surmonté par la lettre R en blanc + en: A red arrow pushing up a blue W letter which is supporting a white R letter + name: WeRobot.fr + description: + en: A presentation website along with a blog for the robotic club We Robot. + fr: Un site vitrine accompagné d'un blog pour l'association de robotique locale We Robot. - id: retrobox + detailled_page: false name: RetroBox + description: + en: E-commerce website from scratch to sell the retrobox console and allow the customer to manage it remotely. + fr: E-commerce de A à Z pour vendre des consoles RetroBox et permettre la gestion à distance de celle ci par le client. date: 2018-08 link: https://retrobox.tech + thumbnail: + src: logo_alone_square.png + alt: + fr: Une console démontrant qu'il peut afficher plusieurs couleurs de façon verticale + en: A console demonstrating that it can show many coulor in an vertical mannel technologies: - - Nuxt.js + - nuxt + - vue + - php + - slim + - mariadb + - socket-io + - stripe + - paypal + - electron - id: jobatator name: Jobatator date: 2020-06 - link: https://github.com/jobatator + link: https://github.com/jobatator + logo: logo.png + description: + fr: | + Un serveur TCP développé comme alternative simplifié à RabbitMQ afin de dispatcher des tâches à des processus. + en: | + A simplified alternative to RabbitMQ to dispatch jobs to workers (in order to for example send an email). technologies: - - Go + - go + action: + fr: GitHub du projet + en: GitHub page - id: keyvaluer + detailled_page: false name: KeyValuer date: 2020-04 link: https://github.com/lefuturiste/keyvaluer + description: + fr: | + Une implementation d'un serveur supportant les commandes basiques de Redis formant une base clé-valeur. + en: | + An implementation of a redis compatible serveur to have a key-value database. technologies: - - Go + - go + action: + fr: GitHub du projet + en: GitHub page - id: discord-monolog-handler + detailled_page: false name: Discord Monolog handler date: 2017-08 link: https://github.com/lefuturiste/monolog-discord-handler technologies: - - PHP + - php + description: + fr: | + Un projet open-source pour permettre à la librairie monolog d'envoyer des logs via un webhook discord. + en: | + An open-source project to extend the monolog library in order to send logs via discord webhooks. + action: + fr: GitHub du projet + en: GitHub page - id: langatator + detailled_page: false name: Langatator date: 2022-05 link: https://gitlab.com/lefuturiste/langatator + description: + fr: | + Développement d'un langage de programmation impératif interprété afin de découvrir le fonctionnement d'un interpréteur. + en: | + Creation of an impérative interpreted langage, trying to enforce a grammar with a lexer, a parser and an evaluator. technologies: - - C - - id: quasator - name: Quasator - date: 2022-03 - link: https://gitlab.com/lefuturiste/quasator - technologies: - - Python - - Latex + - c + action: + fr: GitLab du projet + en: GitLab page + # - id: quasator + # name: Quasator + # date: 2022-03 + # link: https://gitlab.com/lefuturiste/quasator + # technologies: + # - python + # - latex diff --git a/config/technologies.yaml b/config/technologies.yaml index 25740ce..9771826 100644 --- a/config/technologies.yaml +++ b/config/technologies.yaml @@ -1,90 +1,212 @@ +# TODO: add git; vim; mariadb; latex; socket-io; docker; c (programming language); express.js; stripe and paypal integrations + technologies: # Front End - - image: css3.svg + - id: css + image: css3.svg name: CSS 3 height: stretch - - image: html5.svg + website: https://www.w3.org/TR/css/ + wikidata: Q11707176 + + - id: html + image: html5.svg name: HTML 5 + website: https://html.spec.whatwg.org/multipage/ + wikidata: Q2053 # JavaScript - - image: jquery.svg + - id: jquery + image: jquery.svg name: JQuery - - image: node.svg - name: Node.js - - image: yarn.svg - name: Yarn - - image: webpack.svg - name: Webpack - - image: sass.svg - name: SASS - - image: typescript.svg - name: Type Script + website: https://jquery.com/ + wikidata: Q230036 - - image: electron.png + - id: node + image: node.svg + name: Node.js + website: https://nodejs.org/ + wikidata: Q756100 + + - id: yarn + image: yarn.svg + name: Yarn + website: https://yarnpkg.com + wikidata: Q28975591 + + - id: webpack + image: webpack.svg + name: Webpack + website: https://webpack.js.org/ + wikidata: + + - id: sass + image: sass.svg + name: SASS + website: https://sass-lang.com/ + wikidata: Q1572865 + + - id: typescript + image: typescript.svg + name: Type Script + website: https://www.typescriptlang.org/ + wikidata: Q978185 + + - id: electron + image: electron.svg name: Electron + website: https://www.electronjs.org/ + wikidata: Q21614124 # Python - - image: python.svg + - id: python + image: python.svg name: Python - - image: gunicorn.svg - name: Gunicorn + website: https://python.org + wikidata: Q28865 + - id: gunicorn + image: gunicorn.svg + name: Gunicorn + website: https://gunicorn.org/ + wikidata: Q5618801 # Dev Ops - - image: docker.svg + - id: docker + image: docker.svg name: Docker - - image: caddy.png + website: https://docker.com + wikidata: Q15206305 + + - id: caddy + image: caddy.png name: Caddy - - image: nginx.svg + website: https://caddyserver.com/ + wikidata: Q24008327 + + - id: nginx + image: nginx.svg name: Nginx height: stretch - - image: varnish.svg - name: Varnish - - image: cloudflare.svg - name: Cloudflare + website: https://nginx.org/en/ + wikidata: Q306144 + - id: varnish + image: varnish.svg + name: Varnish + website: https://varnish-cache.org + wikidata: Q1602447 + + - id: cloudflare + image: cloudflare.svg + name: Cloudflare + website: https://cloudflare.com + wikidata: Q4778915 # Databases - - image: mysql.svg - name: MySQL - description: Database management system - - image: mongodb.svg + - id: mariadb + alias: mysql + image: mariadb.svg + name: MariaDB + description: Database management system similar to MySQL + website: https://mariadb.org/ + wikidata: Q787177 + + - id: mongodb + image: mongodb.svg height: stretch name: MongoDB - - image: elasticsearch.png + description: NoSQL database + website: https://mongodb.com + wikidata: Q1165204 + + - id: elasticsearch + image: elasticsearch.png name: Elastic Search - - image: redis.svg + website: https://www.elastic.co/elasticsearch/ + wikidata: Q3050461 + + - id: redis + image: redis.svg name: Redis + website: https://redis.io + wikidata: Q2136322 # PHP ecosystem - - image: php.svg + - id: php + image: php.svg name: PHP description: Language to make cool website website: https://php.net color: blue - - image: api-platform.svg + wikidata: Q59 + + - id: api-platform + image: api-platform.svg name: API Platform - - image: composer.svg + website: https://api-platform.com/ + wikidata: Q108740058 + + - id: composer + image: composer.svg name: Composer height: stretch - - image: slim.svg + website: https://getcomposer.org/ + wikidata: Q15252222 + + - id: slim + image: slim.svg name: Slim Framework - - image: laravel.svg + website: https://www.slimframework.com/ + wikidata: null + + - id: laravel + image: laravel.svg name: Laravel - - image: symfony.svg + website: https://laravel.com/ + wikidata: Q13634357 + + - id: symfony + image: symfony.svg name: Symfony - - image: phpunit.svg + website: https://symfony.com + wikidata: Q1322933 + + - id: phpunit + image: phpunit.svg name: PHP Unit + website: https://phpunit.de/ + wikidata: Q1663673 - - image: vue.svg + - id: vue + image: vue.svg name: Vue.js - - image: nuxt.svg + website: https://vuejs.org/ + wikidata: Q24589705 + + - id: nuxt + image: nuxt.svg name: Nuxt.js - - image: vuetify.svg + website: https://nuxtjs.org/ + wikidata: Q55641291 + + - id: vuetify + image: vuetify.svg name: Vuetify + website: https://vuetifyjs.com/ + wikidata: Q112943923 - - image: golang.svg + - id: go + image: go.svg name: Go + website: https://go.dev + wikidata: Q37227 + + - id: express + image: express.svg + name: Express.js + wikidata: Q16878131 + diff --git a/package.json b/package.json index e81167b..ecb19ab 100755 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "build": "cross-env NODE_ENV=production webpack" }, "dependencies": { + "axios": "^0.27.2", "file-loader": "^6.2.0", "node-sass-glob-importer": "^5.3.2", "sass": "^1.42.1", @@ -14,5 +15,6 @@ }, "devDependencies": { "cross-env": "^7.0.3" - } + }, + "_comment": "YES" } diff --git a/public/dist/app.min.css b/public/dist/app.min.css index 54f5b1a..181a338 100755 --- a/public/dist/app.min.css +++ b/public/dist/app.min.css @@ -1 +1 @@ -@import"https://fonts.googleapis.com/css2?family=Libre+Baskerville&display=swap";html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:"";content:none}table{border-collapse:collapse;border-spacing:0}*{box-sizing:border-box}.landing-section{box-shadow:inset 0 7px 15px -7px rgba(0,0,0,.4),inset 0 -7px 15px -7px rgba(0,0,0,.4);padding:2em 0;background:linear-gradient(90deg, #3ca441 0%, #5fc463 50%);color:#fff;border-bottom:0}.profile-container{display:flex;justify-content:center;align-items:center}.profile-imgs-container{width:28em;height:28em;display:flex;justify-content:center;align-items:center;flex-direction:column;position:relative;margin-right:3em}@keyframes animate{0%{transform:perspective(1000px) rotateY(0deg) rotateZ(-45deg)}100%{transform:perspective(1000px) rotateY(360deg) rotateZ(-45deg)}}.profile-imgs-wrapper:hover{animation:animate 20s linear infinite}.profile-imgs-wrapper{transform:rotateZ(-45deg);border-radius:5px;overflow:hidden;z-index:3}.profile-imgs{position:relative}.profile-shadow{transform:perspective(1000px) rotateX(99deg);z-index:1;position:absolute;bottom:-104px;width:80%;height:50%;border-radius:50%;background:radial-gradient(circle, white 0%, rgba(255, 255, 255, 0) 100%);filter:blur(45px)}.additional-profile,.additional-profile-filter{position:absolute}.additional-profile{z-index:1}.additional-profile-filter{z-index:2}.profile-content{margin-left:1em;display:flex;justify-content:center;align-items:center;flex-direction:column}.profile-content .title-1{text-align:center;font-size:3.5em;margin-bottom:.4em}.profile-content .title-2{text-align:center;font-size:2em;margin-bottom:1em}.profile-content .subtitle{font-size:1.5em}.projects{display:flex;flex-wrap:wrap}.projects .project-card{width:20em;margin-right:.5em;margin-bottom:.5em;border:1px solid gray;padding:1.5em;border-radius:5px}.projects .project-title{font-size:1em;padding-bottom:1em}.projects .project-description{font-size:.9em;color:gray}.tech-mosaic{display:flex;flex-wrap:wrap}.tech-mosaic .item{position:relative;box-sizing:border-box;background-color:rgba(231,231,231,.425);flex-basis:12.5%}.tech-mosaic .item::before{content:"";display:block;padding-top:100%}.tech-mosaic .item .item-inside{position:absolute;top:0;left:0;height:100%;width:100%}.tech-mosaic .item-bg{width:100%;height:100%;position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;overflow:hidden}.tech-mosaic .item-bg img{width:100%}.tech-mosaic .item-bg img.img-stretch{width:auto;height:100%}.tech-mosaic .item-content{position:absolute;top:0;left:0;width:100%;height:100%;color:#fff;display:flex;justify-content:center;align-items:center;opacity:1;background-color:rgba(95,196,99,.46);transition:all .2s}.tech-mosaic .item-content>div{opacity:0;font-size:1.1em;text-align:center;padding:.5em;line-height:1.5em;max-width:100%}.tech-mosaic .item:hover .item-content{background-color:rgba(0,0,0,0)}@media(max-width: 1300px){.tech-mosaic .item{flex-basis:20%}}@media(max-width: 900px){.tech-mosaic .item{flex-basis:33.3333333333%}}@media(max-width: 500px){.tech-mosaic .item{flex-basis:50%}}.section-title{display:grid;font-size:1.2em;grid-template-columns:auto 1fr;margin-bottom:1em;margin-top:1.3em}.section-title .title-text{color:#000;display:flex;align-items:center}.section-title::before{margin:8px 15px 8px 8px;content:"";width:10px;height:10px;display:block;background-color:#4ba05f}p{line-height:1.2em}.container{width:70%;margin:0 auto}.typo,body{font-family:"Libre Baskerville",serif}section{border-bottom:1px solid gray;padding-bottom:1em} \ No newline at end of file +@import"https://fonts.googleapis.com/css2?family=Libre+Baskerville&display=swap";html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:"";content:none}table{border-collapse:collapse;border-spacing:0}*{box-sizing:border-box}.landing-section{box-shadow:inset 0 7px 15px -7px rgba(0,0,0,.4),inset 0 -7px 15px -7px rgba(0,0,0,.4);padding:2em 0;background:linear-gradient(90deg, #3ca441 0%, #5fc463 50%);color:#fff;border-bottom:0}.profile-container{display:flex;justify-content:center;align-items:center}.profile-imgs-container{width:28em;height:28em;display:flex;justify-content:center;align-items:center;flex-direction:column;position:relative;margin-right:3em}@keyframes animate{0%{transform:perspective(1000px) rotateY(0deg) rotateZ(-45deg)}100%{transform:perspective(1000px) rotateY(360deg) rotateZ(-45deg)}}.profile-imgs-wrapper:hover{animation:animate 20s linear infinite}.profile-imgs-wrapper{transform:rotateZ(-45deg);border-radius:5px;overflow:hidden;z-index:3}.profile-imgs{position:relative}.profile-shadow{transform:perspective(1000px) rotateX(99deg);z-index:1;position:absolute;bottom:-104px;width:80%;height:50%;border-radius:50%;background:radial-gradient(circle, white 0%, rgba(255, 255, 255, 0) 100%);filter:blur(45px)}.additional-profile,.additional-profile-filter{position:absolute}.additional-profile{z-index:1}.additional-profile-filter{z-index:2}.profile-content{margin-left:1em;display:flex;justify-content:center;align-items:center;flex-direction:column}.profile-content .title-1{text-align:center;font-size:3em;margin-bottom:.4em;line-height:1.5em}.profile-content .title-2{text-align:center;font-size:1.5em;margin-bottom:1em}.profile-content .subtitle{font-size:1.5em}@media(max-width: 900px){.profile-container{flex-direction:column}.profile-imgs-container{margin-right:0}.profile-content{margin-left:0;margin-top:-1em}.profile-content .title-1{font-size:2.5em}.profile-content .title-2{font-size:1.2em}.profile-imgs-container{transform:scale(0.7);margin-top:-4em}}@media(max-width: 400px){.landing-section{padding-top:0}.profile-content{margin-top:-4em;padding:0 1em}.profile-content .title-1{font-size:2.5em}.profile-content .title-2{font-size:1.2em}.profile-imgs-container{transform:scale(0.6);margin-top:-4em}}.projects{display:grid;grid-template-columns:1fr 1fr;grid-gap:1em}.projects .project-card{border:1px solid gray;padding:1.5em;border-radius:5px;display:flex;flex-direction:column;justify-content:space-between}.projects .project-top{display:flex}.projects .project-left{width:100%}.projects .project-title{font-size:1em;padding-bottom:1em}.projects .project-description{font-size:.9em;color:gray}.projects .project-img{width:5em}.projects .project-right{padding-left:.5em}.projects .project-bottom{padding-top:1em}@media(min-width: 1500px){.projects{grid-template-columns:1fr 1fr 1fr}}@media(max-width: 1000px){.projects{grid-template-columns:1fr 1fr}}@media(max-width: 900px){.projects{grid-template-columns:1fr}.project-right{padding-left:1em !important}}.tech-mosaic{display:flex;flex-wrap:wrap}.tech-mosaic .item{position:relative;box-sizing:border-box;background-color:rgba(231,231,231,.425);flex-basis:12.5%}.tech-mosaic .item::before{content:"";display:block;padding-top:100%}.tech-mosaic .item .item-inside{position:absolute;top:0;left:0;height:100%;width:100%}.tech-mosaic .item-bg{width:100%;height:100%;position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;overflow:hidden;padding:2em}.tech-mosaic .item-bg img{width:100%}.tech-mosaic .item-bg img.img-stretch{width:auto;height:100%}.tech-mosaic .item-content{position:absolute;top:0;left:0;width:100%;height:100%;color:#fff;display:flex;justify-content:center;align-items:center;opacity:1;background-color:rgba(95,196,99,.46);transition:all .2s}.tech-mosaic .item-content>div{opacity:0;font-size:1.1em;text-align:center;padding:.5em;line-height:1.5em;max-width:100%}.tech-mosaic .item:hover .item-content{background-color:rgba(0,0,0,0)}@media(min-width: 400px){.item-bg{padding:1em !important}}@media(max-width: 1000px){.tech-mosaic .item{flex-basis:20%}}@media(max-width: 800px){.tech-mosaic .item{flex-basis:25%}}@media(max-width: 400px){.tech-mosaic .item{flex-basis:50%}}.links-container{display:flex;justify-content:center}.links{width:80%;display:flex;justify-content:space-around;flex-wrap:wrap}.links .link-card{margin:1em;display:flex;flex-direction:column;justify-content:center;align-items:center;min-width:10em}.links .link-card-title{margin-top:.9em}.links .link-card-logo{padding:.5em;width:4em;height:4em;overflow:hidden;display:flex;justify-content:center;align-content:center}.links .link-card-logo img{max-width:4em;max-height:4em}@media(max-width: 900px){.links{width:100%}.links .link-card{justify-content:center}}@media(max-width: 400px){.links{width:100%}.links .link-card{min-width:6em}}p{line-height:1.45em;margin-bottom:1em}h1{font-size:xx-large;margin-bottom:1em}h2{font-size:x-large;margin-bottom:.5em}h3{font-size:large}.section-title{display:grid;font-size:1.2em;grid-template-columns:auto 1fr;margin-bottom:1em;margin-top:1.3em}.section-title .title-text{color:#000;display:flex;align-items:center}.section-title::before{margin:8px 15px 8px 8px;content:"";width:10px;height:10px;display:block;background-color:#4ba05f}.footer-buffer{height:2em}footer{margin-top:auto;border-top:1px solid gray;border-color:#545b5e !important}.footer-container{padding:2em 0;display:flex;justify-content:space-between}.footer-left div{margin-bottom:.5em}.locale-switch-large{display:flex;align-items:center}.locale-switch-large .l{margin-right:1em}.locale-switch-large .r a{margin-right:.2em}.locale-switch-short{display:none}@media(max-width: 900px){.locale-switch-large{display:none}.locale-switch-short{display:block}}.means-of-contact p{margin-bottom:1em}.contact-form-container form{border-radius:3px;border:1px dashed #80808054;padding:1em}.contact-form-container .contact-form-first-group{display:flex}.contact-form-container .contact-form-first-group .contact-form-input:first-of-type{margin-right:.4em}.contact-form-container .contact-form-first-group .contact-form-input:nth-of-type(2){margin-left:.4em}.contact-form-container .contact-form-input{width:100%;margin-top:1em}.contact-form-container input,.contact-form-container textarea{width:100%;margin-top:.6em;border-radius:3px;padding:.6em;border:1px solid #dbdbdb;box-shadow:1px 2px 4px 0 rgba(0,0,0,.1)}.contact-form-container .contact-form-message textarea{min-height:8em}.contact-form-container label{display:block}@keyframes rotating{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}.contact-form-container .loading-button{display:none}.contact-form-container .loading-button svg{animation:rotating 2s linear infinite}.contact-form-container .contact-form-container{display:flex;justify-content:end}.alert{padding:1em;margin-bottom:1em;border-radius:3px}.alert .alert-content{display:flex;align-items:center}.alert.alert-success{background:green;color:#fff}.alert.alert-error{background:red;color:#fff}.alert .alert-icon{padding-right:.6em}@media(min-width: 900px){.contact-form-first-group .contact-form-input{margin-top:0}}@media(max-width: 900px){.contact-form .contact-form-first-group{display:block}.contact-form .contact-form-first-group .contact-form-input{margin-left:0 !important;margin-right:0 !important}.contact-form-input:first-of-type{margin-top:0}}.btn{border-radius:2px;box-shadow:1px 2px 4px 0 rgba(0,0,0,.08);padding:1em 1.5em;margin-top:1em;border:1px solid #ccc;font-size:16px}.chips{display:flex;flex-wrap:wrap}.chips .chip{margin-right:.5em;padding:.5em .9em;background:#4ba05f;color:#fff;font-size:.9em;border-radius:10rem}body{min-height:100vh;display:flex;flex-direction:column}.hidden{display:none}.container{width:70%;margin:0 auto}.typo,body{font-family:"Libre Baskerville",serif}section{border-bottom:1px solid gray;padding-bottom:1em}section:last-of-type{border-bottom:0}.about-header{margin:2em 0}@media(min-width: 1500px){.container{width:58%}}@media(min-width: 1000px){.container{width:65%}}@media(max-width: 1000px){.container{width:75%}}@media(max-width: 900px){.container{width:90%}} \ No newline at end of file diff --git a/public/dist/app.min.js b/public/dist/app.min.js index c4a1448..c5bc4f3 100755 --- a/public/dist/app.min.js +++ b/public/dist/app.min.js @@ -1,2 +1,2 @@ /*! For license information please see app.min.js.LICENSE.txt */ -(()=>{var e={d:(t,r)=>{for(var o in r)e.o(r,o)&&!e.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:r[o]})}};e.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),e.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),e.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var t;e.g.importScripts&&(t=e.g.location+"");var r=e.g.document;if(!t&&r&&(r.currentScript&&(t=r.currentScript.src),!t)){var o=r.getElementsByTagName("script");o.length&&(t=o[o.length-1].src)}if(!t)throw new Error("Automatic publicPath is not supported in this browser");t=t.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),e.p=t})();var t={};(()=>{"use strict";e.r(t),e.d(t,{default:()=>r});const r=e.p+"app.min.css"})()})(); \ No newline at end of file +(()=>{var e={"./assets/scripts/contact.js":(e,t,o)=>{"use strict";o.r(t),o.d(t,{contactFormSetup:()=>s});var r=o("./node_modules/axios/index.js"),n=o.n(r);function s(){const e=document.getElementById("contact-form"),t=document.getElementById("contact-form-name"),o=document.getElementById("contact-form-email"),r=document.getElementById("contact-form-subject"),s=document.getElementById("contact-form-message"),i=document.getElementById("contact-form-submit"),a=document.getElementById("contact-form-loading"),l=document.getElementById("contact-alert-success"),u=document.getElementById("contact-alert-error");e.onsubmit=e=>{e.preventDefault(),console.log("> Attempting to send a message",t.value,o.value,s.value);let c=i.textContent;a.style.display="block",i.style.display="none",n().post("https://contact-form.thingmill.fr/lefuturiste",{name:t.value,email:o.value,subject:r.value,message:s.value}).then((()=>{console.log("> Message sent"),l.style.display="block",a.style.display="none",i.style.display="block",i.textContent=c})).catch((e=>{u.style.display="block",console.error(e)}))}}},"./node_modules/axios/index.js":(e,t,o)=>{e.exports=o("./node_modules/axios/lib/axios.js")},"./node_modules/axios/lib/adapters/xhr.js":(e,t,o)=>{"use strict";var r=o("./node_modules/axios/lib/utils.js"),n=o("./node_modules/axios/lib/core/settle.js"),s=o("./node_modules/axios/lib/helpers/cookies.js"),i=o("./node_modules/axios/lib/helpers/buildURL.js"),a=o("./node_modules/axios/lib/core/buildFullPath.js"),l=o("./node_modules/axios/lib/helpers/parseHeaders.js"),u=o("./node_modules/axios/lib/helpers/isURLSameOrigin.js"),c=o("./node_modules/axios/lib/defaults/transitional.js"),d=o("./node_modules/axios/lib/core/AxiosError.js"),f=o("./node_modules/axios/lib/cancel/CanceledError.js"),p=o("./node_modules/axios/lib/helpers/parseProtocol.js");e.exports=function(e){return new Promise((function(t,o){var m,h=e.data,b=e.headers,x=e.responseType;function v(){e.cancelToken&&e.cancelToken.unsubscribe(m),e.signal&&e.signal.removeEventListener("abort",m)}r.isFormData(h)&&r.isStandardBrowserEnv()&&delete b["Content-Type"];var j=new XMLHttpRequest;if(e.auth){var g=e.auth.username||"",y=e.auth.password?unescape(encodeURIComponent(e.auth.password)):"";b.Authorization="Basic "+btoa(g+":"+y)}var _=a(e.baseURL,e.url);function E(){if(j){var r="getAllResponseHeaders"in j?l(j.getAllResponseHeaders()):null,s={data:x&&"text"!==x&&"json"!==x?j.response:j.responseText,status:j.status,statusText:j.statusText,headers:r,config:e,request:j};n((function(e){t(e),v()}),(function(e){o(e),v()}),s),j=null}}if(j.open(e.method.toUpperCase(),i(_,e.params,e.paramsSerializer),!0),j.timeout=e.timeout,"onloadend"in j?j.onloadend=E:j.onreadystatechange=function(){j&&4===j.readyState&&(0!==j.status||j.responseURL&&0===j.responseURL.indexOf("file:"))&&setTimeout(E)},j.onabort=function(){j&&(o(new d("Request aborted",d.ECONNABORTED,e,j)),j=null)},j.onerror=function(){o(new d("Network Error",d.ERR_NETWORK,e,j,j)),j=null},j.ontimeout=function(){var t=e.timeout?"timeout of "+e.timeout+"ms exceeded":"timeout exceeded",r=e.transitional||c;e.timeoutErrorMessage&&(t=e.timeoutErrorMessage),o(new d(t,r.clarifyTimeoutError?d.ETIMEDOUT:d.ECONNABORTED,e,j)),j=null},r.isStandardBrowserEnv()){var w=(e.withCredentials||u(_))&&e.xsrfCookieName?s.read(e.xsrfCookieName):void 0;w&&(b[e.xsrfHeaderName]=w)}"setRequestHeader"in j&&r.forEach(b,(function(e,t){void 0===h&&"content-type"===t.toLowerCase()?delete b[t]:j.setRequestHeader(t,e)})),r.isUndefined(e.withCredentials)||(j.withCredentials=!!e.withCredentials),x&&"json"!==x&&(j.responseType=e.responseType),"function"==typeof e.onDownloadProgress&&j.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&j.upload&&j.upload.addEventListener("progress",e.onUploadProgress),(e.cancelToken||e.signal)&&(m=function(e){j&&(o(!e||e&&e.type?new f:e),j.abort(),j=null)},e.cancelToken&&e.cancelToken.subscribe(m),e.signal&&(e.signal.aborted?m():e.signal.addEventListener("abort",m))),h||(h=null);var R=p(_);R&&-1===["http","https","file"].indexOf(R)?o(new d("Unsupported protocol "+R+":",d.ERR_BAD_REQUEST,e)):j.send(h)}))}},"./node_modules/axios/lib/axios.js":(e,t,o)=>{"use strict";var r=o("./node_modules/axios/lib/utils.js"),n=o("./node_modules/axios/lib/helpers/bind.js"),s=o("./node_modules/axios/lib/core/Axios.js"),i=o("./node_modules/axios/lib/core/mergeConfig.js");var a=function e(t){var o=new s(t),a=n(s.prototype.request,o);return r.extend(a,s.prototype,o),r.extend(a,o),a.create=function(o){return e(i(t,o))},a}(o("./node_modules/axios/lib/defaults/index.js"));a.Axios=s,a.CanceledError=o("./node_modules/axios/lib/cancel/CanceledError.js"),a.CancelToken=o("./node_modules/axios/lib/cancel/CancelToken.js"),a.isCancel=o("./node_modules/axios/lib/cancel/isCancel.js"),a.VERSION=o("./node_modules/axios/lib/env/data.js").version,a.toFormData=o("./node_modules/axios/lib/helpers/toFormData.js"),a.AxiosError=o("./node_modules/axios/lib/core/AxiosError.js"),a.Cancel=a.CanceledError,a.all=function(e){return Promise.all(e)},a.spread=o("./node_modules/axios/lib/helpers/spread.js"),a.isAxiosError=o("./node_modules/axios/lib/helpers/isAxiosError.js"),e.exports=a,e.exports.default=a},"./node_modules/axios/lib/cancel/CancelToken.js":(e,t,o)=>{"use strict";var r=o("./node_modules/axios/lib/cancel/CanceledError.js");function n(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise((function(e){t=e}));var o=this;this.promise.then((function(e){if(o._listeners){var t,r=o._listeners.length;for(t=0;t{"use strict";var r=o("./node_modules/axios/lib/core/AxiosError.js");function n(e){r.call(this,null==e?"canceled":e,r.ERR_CANCELED),this.name="CanceledError"}o("./node_modules/axios/lib/utils.js").inherits(n,r,{__CANCEL__:!0}),e.exports=n},"./node_modules/axios/lib/cancel/isCancel.js":e=>{"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},"./node_modules/axios/lib/core/Axios.js":(e,t,o)=>{"use strict";var r=o("./node_modules/axios/lib/utils.js"),n=o("./node_modules/axios/lib/helpers/buildURL.js"),s=o("./node_modules/axios/lib/core/InterceptorManager.js"),i=o("./node_modules/axios/lib/core/dispatchRequest.js"),a=o("./node_modules/axios/lib/core/mergeConfig.js"),l=o("./node_modules/axios/lib/core/buildFullPath.js"),u=o("./node_modules/axios/lib/helpers/validator.js"),c=u.validators;function d(e){this.defaults=e,this.interceptors={request:new s,response:new s}}d.prototype.request=function(e,t){"string"==typeof e?(t=t||{}).url=e:t=e||{},(t=a(this.defaults,t)).method?t.method=t.method.toLowerCase():this.defaults.method?t.method=this.defaults.method.toLowerCase():t.method="get";var o=t.transitional;void 0!==o&&u.assertOptions(o,{silentJSONParsing:c.transitional(c.boolean),forcedJSONParsing:c.transitional(c.boolean),clarifyTimeoutError:c.transitional(c.boolean)},!1);var r=[],n=!0;this.interceptors.request.forEach((function(e){"function"==typeof e.runWhen&&!1===e.runWhen(t)||(n=n&&e.synchronous,r.unshift(e.fulfilled,e.rejected))}));var s,l=[];if(this.interceptors.response.forEach((function(e){l.push(e.fulfilled,e.rejected)})),!n){var d=[i,void 0];for(Array.prototype.unshift.apply(d,r),d=d.concat(l),s=Promise.resolve(t);d.length;)s=s.then(d.shift(),d.shift());return s}for(var f=t;r.length;){var p=r.shift(),m=r.shift();try{f=p(f)}catch(e){m(e);break}}try{s=i(f)}catch(e){return Promise.reject(e)}for(;l.length;)s=s.then(l.shift(),l.shift());return s},d.prototype.getUri=function(e){e=a(this.defaults,e);var t=l(e.baseURL,e.url);return n(t,e.params,e.paramsSerializer)},r.forEach(["delete","get","head","options"],(function(e){d.prototype[e]=function(t,o){return this.request(a(o||{},{method:e,url:t,data:(o||{}).data}))}})),r.forEach(["post","put","patch"],(function(e){function t(t){return function(o,r,n){return this.request(a(n||{},{method:e,headers:t?{"Content-Type":"multipart/form-data"}:{},url:o,data:r}))}}d.prototype[e]=t(),d.prototype[e+"Form"]=t(!0)})),e.exports=d},"./node_modules/axios/lib/core/AxiosError.js":(e,t,o)=>{"use strict";var r=o("./node_modules/axios/lib/utils.js");function n(e,t,o,r,n){Error.call(this),this.message=e,this.name="AxiosError",t&&(this.code=t),o&&(this.config=o),r&&(this.request=r),n&&(this.response=n)}r.inherits(n,Error,{toJSON:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code,status:this.response&&this.response.status?this.response.status:null}}});var s=n.prototype,i={};["ERR_BAD_OPTION_VALUE","ERR_BAD_OPTION","ECONNABORTED","ETIMEDOUT","ERR_NETWORK","ERR_FR_TOO_MANY_REDIRECTS","ERR_DEPRECATED","ERR_BAD_RESPONSE","ERR_BAD_REQUEST","ERR_CANCELED"].forEach((function(e){i[e]={value:e}})),Object.defineProperties(n,i),Object.defineProperty(s,"isAxiosError",{value:!0}),n.from=function(e,t,o,i,a,l){var u=Object.create(s);return r.toFlatObject(e,u,(function(e){return e!==Error.prototype})),n.call(u,e.message,t,o,i,a),u.name=e.name,l&&Object.assign(u,l),u},e.exports=n},"./node_modules/axios/lib/core/InterceptorManager.js":(e,t,o)=>{"use strict";var r=o("./node_modules/axios/lib/utils.js");function n(){this.handlers=[]}n.prototype.use=function(e,t,o){return this.handlers.push({fulfilled:e,rejected:t,synchronous:!!o&&o.synchronous,runWhen:o?o.runWhen:null}),this.handlers.length-1},n.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},n.prototype.forEach=function(e){r.forEach(this.handlers,(function(t){null!==t&&e(t)}))},e.exports=n},"./node_modules/axios/lib/core/buildFullPath.js":(e,t,o)=>{"use strict";var r=o("./node_modules/axios/lib/helpers/isAbsoluteURL.js"),n=o("./node_modules/axios/lib/helpers/combineURLs.js");e.exports=function(e,t){return e&&!r(t)?n(e,t):t}},"./node_modules/axios/lib/core/dispatchRequest.js":(e,t,o)=>{"use strict";var r=o("./node_modules/axios/lib/utils.js"),n=o("./node_modules/axios/lib/core/transformData.js"),s=o("./node_modules/axios/lib/cancel/isCancel.js"),i=o("./node_modules/axios/lib/defaults/index.js"),a=o("./node_modules/axios/lib/cancel/CanceledError.js");function l(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new a}e.exports=function(e){return l(e),e.headers=e.headers||{},e.data=n.call(e,e.data,e.headers,e.transformRequest),e.headers=r.merge(e.headers.common||{},e.headers[e.method]||{},e.headers),r.forEach(["delete","get","head","post","put","patch","common"],(function(t){delete e.headers[t]})),(e.adapter||i.adapter)(e).then((function(t){return l(e),t.data=n.call(e,t.data,t.headers,e.transformResponse),t}),(function(t){return s(t)||(l(e),t&&t.response&&(t.response.data=n.call(e,t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)}))}},"./node_modules/axios/lib/core/mergeConfig.js":(e,t,o)=>{"use strict";var r=o("./node_modules/axios/lib/utils.js");e.exports=function(e,t){t=t||{};var o={};function n(e,t){return r.isPlainObject(e)&&r.isPlainObject(t)?r.merge(e,t):r.isPlainObject(t)?r.merge({},t):r.isArray(t)?t.slice():t}function s(o){return r.isUndefined(t[o])?r.isUndefined(e[o])?void 0:n(void 0,e[o]):n(e[o],t[o])}function i(e){if(!r.isUndefined(t[e]))return n(void 0,t[e])}function a(o){return r.isUndefined(t[o])?r.isUndefined(e[o])?void 0:n(void 0,e[o]):n(void 0,t[o])}function l(o){return o in t?n(e[o],t[o]):o in e?n(void 0,e[o]):void 0}var u={url:i,method:i,data:i,baseURL:a,transformRequest:a,transformResponse:a,paramsSerializer:a,timeout:a,timeoutMessage:a,withCredentials:a,adapter:a,responseType:a,xsrfCookieName:a,xsrfHeaderName:a,onUploadProgress:a,onDownloadProgress:a,decompress:a,maxContentLength:a,maxBodyLength:a,beforeRedirect:a,transport:a,httpAgent:a,httpsAgent:a,cancelToken:a,socketPath:a,responseEncoding:a,validateStatus:l};return r.forEach(Object.keys(e).concat(Object.keys(t)),(function(e){var t=u[e]||s,n=t(e);r.isUndefined(n)&&t!==l||(o[e]=n)})),o}},"./node_modules/axios/lib/core/settle.js":(e,t,o)=>{"use strict";var r=o("./node_modules/axios/lib/core/AxiosError.js");e.exports=function(e,t,o){var n=o.config.validateStatus;o.status&&n&&!n(o.status)?t(new r("Request failed with status code "+o.status,[r.ERR_BAD_REQUEST,r.ERR_BAD_RESPONSE][Math.floor(o.status/100)-4],o.config,o.request,o)):e(o)}},"./node_modules/axios/lib/core/transformData.js":(e,t,o)=>{"use strict";var r=o("./node_modules/axios/lib/utils.js"),n=o("./node_modules/axios/lib/defaults/index.js");e.exports=function(e,t,o){var s=this||n;return r.forEach(o,(function(o){e=o.call(s,e,t)})),e}},"./node_modules/axios/lib/defaults/index.js":(e,t,o)=>{"use strict";var r=o("./node_modules/axios/lib/utils.js"),n=o("./node_modules/axios/lib/helpers/normalizeHeaderName.js"),s=o("./node_modules/axios/lib/core/AxiosError.js"),i=o("./node_modules/axios/lib/defaults/transitional.js"),a=o("./node_modules/axios/lib/helpers/toFormData.js"),l={"Content-Type":"application/x-www-form-urlencoded"};function u(e,t){!r.isUndefined(e)&&r.isUndefined(e["Content-Type"])&&(e["Content-Type"]=t)}var c,d={transitional:i,adapter:(("undefined"!=typeof XMLHttpRequest||"undefined"!=typeof process&&"[object process]"===Object.prototype.toString.call(process))&&(c=o("./node_modules/axios/lib/adapters/xhr.js")),c),transformRequest:[function(e,t){if(n(t,"Accept"),n(t,"Content-Type"),r.isFormData(e)||r.isArrayBuffer(e)||r.isBuffer(e)||r.isStream(e)||r.isFile(e)||r.isBlob(e))return e;if(r.isArrayBufferView(e))return e.buffer;if(r.isURLSearchParams(e))return u(t,"application/x-www-form-urlencoded;charset=utf-8"),e.toString();var o,s=r.isObject(e),i=t&&t["Content-Type"];if((o=r.isFileList(e))||s&&"multipart/form-data"===i){var l=this.env&&this.env.FormData;return a(o?{"files[]":e}:e,l&&new l)}return s||"application/json"===i?(u(t,"application/json"),function(e,t,o){if(r.isString(e))try{return(t||JSON.parse)(e),r.trim(e)}catch(e){if("SyntaxError"!==e.name)throw e}return(o||JSON.stringify)(e)}(e)):e}],transformResponse:[function(e){var t=this.transitional||d.transitional,o=t&&t.silentJSONParsing,n=t&&t.forcedJSONParsing,i=!o&&"json"===this.responseType;if(i||n&&r.isString(e)&&e.length)try{return JSON.parse(e)}catch(e){if(i){if("SyntaxError"===e.name)throw s.from(e,s.ERR_BAD_RESPONSE,this,null,this.response);throw e}}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:o("./node_modules/axios/lib/helpers/null.js")},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*"}}};r.forEach(["delete","get","head"],(function(e){d.headers[e]={}})),r.forEach(["post","put","patch"],(function(e){d.headers[e]=r.merge(l)})),e.exports=d},"./node_modules/axios/lib/defaults/transitional.js":e=>{"use strict";e.exports={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1}},"./node_modules/axios/lib/env/data.js":e=>{e.exports={version:"0.27.2"}},"./node_modules/axios/lib/helpers/bind.js":e=>{"use strict";e.exports=function(e,t){return function(){for(var o=new Array(arguments.length),r=0;r{"use strict";var r=o("./node_modules/axios/lib/utils.js");function n(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}e.exports=function(e,t,o){if(!t)return e;var s;if(o)s=o(t);else if(r.isURLSearchParams(t))s=t.toString();else{var i=[];r.forEach(t,(function(e,t){null!=e&&(r.isArray(e)?t+="[]":e=[e],r.forEach(e,(function(e){r.isDate(e)?e=e.toISOString():r.isObject(e)&&(e=JSON.stringify(e)),i.push(n(t)+"="+n(e))})))})),s=i.join("&")}if(s){var a=e.indexOf("#");-1!==a&&(e=e.slice(0,a)),e+=(-1===e.indexOf("?")?"?":"&")+s}return e}},"./node_modules/axios/lib/helpers/combineURLs.js":e=>{"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},"./node_modules/axios/lib/helpers/cookies.js":(e,t,o)=>{"use strict";var r=o("./node_modules/axios/lib/utils.js");e.exports=r.isStandardBrowserEnv()?{write:function(e,t,o,n,s,i){var a=[];a.push(e+"="+encodeURIComponent(t)),r.isNumber(o)&&a.push("expires="+new Date(o).toGMTString()),r.isString(n)&&a.push("path="+n),r.isString(s)&&a.push("domain="+s),!0===i&&a.push("secure"),document.cookie=a.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}:{write:function(){},read:function(){return null},remove:function(){}}},"./node_modules/axios/lib/helpers/isAbsoluteURL.js":e=>{"use strict";e.exports=function(e){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e)}},"./node_modules/axios/lib/helpers/isAxiosError.js":(e,t,o)=>{"use strict";var r=o("./node_modules/axios/lib/utils.js");e.exports=function(e){return r.isObject(e)&&!0===e.isAxiosError}},"./node_modules/axios/lib/helpers/isURLSameOrigin.js":(e,t,o)=>{"use strict";var r=o("./node_modules/axios/lib/utils.js");e.exports=r.isStandardBrowserEnv()?function(){var e,t=/(msie|trident)/i.test(navigator.userAgent),o=document.createElement("a");function n(e){var r=e;return t&&(o.setAttribute("href",r),r=o.href),o.setAttribute("href",r),{href:o.href,protocol:o.protocol?o.protocol.replace(/:$/,""):"",host:o.host,search:o.search?o.search.replace(/^\?/,""):"",hash:o.hash?o.hash.replace(/^#/,""):"",hostname:o.hostname,port:o.port,pathname:"/"===o.pathname.charAt(0)?o.pathname:"/"+o.pathname}}return e=n(window.location.href),function(t){var o=r.isString(t)?n(t):t;return o.protocol===e.protocol&&o.host===e.host}}():function(){return!0}},"./node_modules/axios/lib/helpers/normalizeHeaderName.js":(e,t,o)=>{"use strict";var r=o("./node_modules/axios/lib/utils.js");e.exports=function(e,t){r.forEach(e,(function(o,r){r!==t&&r.toUpperCase()===t.toUpperCase()&&(e[t]=o,delete e[r])}))}},"./node_modules/axios/lib/helpers/null.js":e=>{e.exports=null},"./node_modules/axios/lib/helpers/parseHeaders.js":(e,t,o)=>{"use strict";var r=o("./node_modules/axios/lib/utils.js"),n=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];e.exports=function(e){var t,o,s,i={};return e?(r.forEach(e.split("\n"),(function(e){if(s=e.indexOf(":"),t=r.trim(e.substr(0,s)).toLowerCase(),o=r.trim(e.substr(s+1)),t){if(i[t]&&n.indexOf(t)>=0)return;i[t]="set-cookie"===t?(i[t]?i[t]:[]).concat([o]):i[t]?i[t]+", "+o:o}})),i):i}},"./node_modules/axios/lib/helpers/parseProtocol.js":e=>{"use strict";e.exports=function(e){var t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||""}},"./node_modules/axios/lib/helpers/spread.js":e=>{"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}},"./node_modules/axios/lib/helpers/toFormData.js":(e,t,o)=>{"use strict";var r=o("./node_modules/axios/lib/utils.js");e.exports=function(e,t){t=t||new FormData;var o=[];function n(e){return null===e?"":r.isDate(e)?e.toISOString():r.isArrayBuffer(e)||r.isTypedArray(e)?"function"==typeof Blob?new Blob([e]):Buffer.from(e):e}return function e(s,i){if(r.isPlainObject(s)||r.isArray(s)){if(-1!==o.indexOf(s))throw Error("Circular reference detected in "+i);o.push(s),r.forEach(s,(function(o,s){if(!r.isUndefined(o)){var a,l=i?i+"."+s:s;if(o&&!i&&"object"==typeof o)if(r.endsWith(s,"{}"))o=JSON.stringify(o);else if(r.endsWith(s,"[]")&&(a=r.toArray(o)))return void a.forEach((function(e){!r.isUndefined(e)&&t.append(l,n(e))}));e(o,l)}})),o.pop()}else t.append(i,n(s))}(e),t}},"./node_modules/axios/lib/helpers/validator.js":(e,t,o)=>{"use strict";var r=o("./node_modules/axios/lib/env/data.js").version,n=o("./node_modules/axios/lib/core/AxiosError.js"),s={};["object","boolean","number","function","string","symbol"].forEach((function(e,t){s[e]=function(o){return typeof o===e||"a"+(t<1?"n ":" ")+e}}));var i={};s.transitional=function(e,t,o){function s(e,t){return"[Axios v"+r+"] Transitional option '"+e+"'"+t+(o?". "+o:"")}return function(o,r,a){if(!1===e)throw new n(s(r," has been removed"+(t?" in "+t:"")),n.ERR_DEPRECATED);return t&&!i[r]&&(i[r]=!0,console.warn(s(r," has been deprecated since v"+t+" and will be removed in the near future"))),!e||e(o,r,a)}},e.exports={assertOptions:function(e,t,o){if("object"!=typeof e)throw new n("options must be an object",n.ERR_BAD_OPTION_VALUE);for(var r=Object.keys(e),s=r.length;s-- >0;){var i=r[s],a=t[i];if(a){var l=e[i],u=void 0===l||a(l,i,e);if(!0!==u)throw new n("option "+i+" must be "+u,n.ERR_BAD_OPTION_VALUE)}else if(!0!==o)throw new n("Unknown option "+i,n.ERR_BAD_OPTION)}},validators:s}},"./node_modules/axios/lib/utils.js":(e,t,o)=>{"use strict";var r,n=o("./node_modules/axios/lib/helpers/bind.js"),s=Object.prototype.toString,i=(r=Object.create(null),function(e){var t=s.call(e);return r[t]||(r[t]=t.slice(8,-1).toLowerCase())});function a(e){return e=e.toLowerCase(),function(t){return i(t)===e}}function l(e){return Array.isArray(e)}function u(e){return void 0===e}var c=a("ArrayBuffer");function d(e){return null!==e&&"object"==typeof e}function f(e){if("object"!==i(e))return!1;var t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}var p=a("Date"),m=a("File"),h=a("Blob"),b=a("FileList");function x(e){return"[object Function]"===s.call(e)}var v=a("URLSearchParams");function j(e,t){if(null!=e)if("object"!=typeof e&&(e=[e]),l(e))for(var o=0,r=e.length;o0;)i[s=r[n]]||(t[s]=e[s],i[s]=!0);e=Object.getPrototypeOf(e)}while(e&&(!o||o(e,t))&&e!==Object.prototype);return t},kindOf:i,kindOfTest:a,endsWith:function(e,t,o){e=String(e),(void 0===o||o>e.length)&&(o=e.length),o-=t.length;var r=e.indexOf(t,o);return-1!==r&&r===o},toArray:function(e){if(!e)return null;var t=e.length;if(u(t))return null;for(var o=new Array(t);t-- >0;)o[t]=e[t];return o},isTypedArray:y,isFileList:b}}},t={};function o(r){var n=t[r];if(void 0!==n)return n.exports;var s=t[r]={exports:{}};return e[r](s,s.exports,o),s.exports}o.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return o.d(t,{a:t}),t},o.d=(e,t)=>{for(var r in t)o.o(t,r)&&!o.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},o.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),o.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e;o.g.importScripts&&(e=o.g.location+"");var t=o.g.document;if(!e&&t&&(t.currentScript&&(e=t.currentScript.src),!e)){var r=t.getElementsByTagName("script");r.length&&(e=r[r.length-1].src)}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),o.p=e})();var r={};(()=>{"use strict";o.r({});o("./node_modules/axios/index.js");(0,o("./assets/scripts/contact.js").contactFormSetup)()})(),(()=>{"use strict";o.r(r),o.d(r,{default:()=>e});const e=o.p+"app.min.css"})()})(); \ No newline at end of file diff --git a/public/dist/app.min.js.LICENSE.txt b/public/dist/app.min.js.LICENSE.txt index 2b7c40c..a791db8 100755 --- a/public/dist/app.min.js.LICENSE.txt +++ b/public/dist/app.min.js.LICENSE.txt @@ -1,3 +1,233 @@ +/*! ../adapters/xhr */ + +/*! ../cancel/CanceledError */ + +/*! ../cancel/isCancel */ + +/*! ../core/AxiosError */ + +/*! ../core/buildFullPath */ + +/*! ../defaults */ + +/*! ../defaults/transitional */ + +/*! ../env/data */ + +/*! ../helpers/buildURL */ + +/*! ../helpers/combineURLs */ + +/*! ../helpers/isAbsoluteURL */ + +/*! ../helpers/normalizeHeaderName */ + +/*! ../helpers/parseProtocol */ + +/*! ../helpers/toFormData */ + +/*! ../helpers/validator */ + +/*! ../lib/core/AxiosError */ + +/*! ../utils */ + +/*! ./../core/settle */ + +/*! ./../helpers/buildURL */ + +/*! ./../helpers/cookies */ + +/*! ./../helpers/isURLSameOrigin */ + +/*! ./../helpers/parseHeaders */ + +/*! ./../utils */ + +/*! ./AxiosError */ + +/*! ./CanceledError */ + +/*! ./InterceptorManager */ + +/*! ./buildFullPath */ + +/*! ./cancel/CancelToken */ + +/*! ./cancel/CanceledError */ + +/*! ./cancel/isCancel */ + +/*! ./contact */ + +/*! ./core/Axios */ + +/*! ./core/mergeConfig */ + +/*! ./defaults */ + +/*! ./dispatchRequest */ + +/*! ./env/FormData */ + +/*! ./env/data */ + +/*! ./helpers/bind */ + +/*! ./helpers/isAxiosError */ + +/*! ./helpers/spread */ + +/*! ./helpers/toFormData */ + +/*! ./lib/axios */ + +/*! ./mergeConfig */ + +/*! ./transformData */ + +/*! ./transitional */ + +/*! ./utils */ + +/*! axios */ + +/*!********************************!*\ + !*** ./assets/scripts/main.js ***! + \********************************/ + /*!*********************************!*\ !*** ./assets/styles/main.scss ***! \*********************************/ + +/*!***********************************!*\ + !*** ./assets/scripts/contact.js ***! + \***********************************/ + +/*!*************************************!*\ + !*** ./node_modules/axios/index.js ***! + \*************************************/ + +/*!*****************************************!*\ + !*** ./node_modules/axios/lib/axios.js ***! + \*****************************************/ + +/*!*****************************************!*\ + !*** ./node_modules/axios/lib/utils.js ***! + \*****************************************/ + +/*!********************************************!*\ + !*** ./node_modules/axios/lib/env/data.js ***! + \********************************************/ + +/*!**********************************************!*\ + !*** ./node_modules/axios/lib/core/Axios.js ***! + \**********************************************/ + +/*!***********************************************!*\ + !*** ./node_modules/axios/lib/core/settle.js ***! + \***********************************************/ + +/*!************************************************!*\ + !*** ./node_modules/axios/lib/adapters/xhr.js ***! + \************************************************/ + +/*!************************************************!*\ + !*** ./node_modules/axios/lib/helpers/bind.js ***! + \************************************************/ + +/*!************************************************!*\ + !*** ./node_modules/axios/lib/helpers/null.js ***! + \************************************************/ + +/*!**************************************************!*\ + !*** ./node_modules/axios/lib/defaults/index.js ***! + \**************************************************/ + +/*!**************************************************!*\ + !*** ./node_modules/axios/lib/helpers/spread.js ***! + \**************************************************/ + +/*!***************************************************!*\ + !*** ./node_modules/axios/lib/cancel/isCancel.js ***! + \***************************************************/ + +/*!***************************************************!*\ + !*** ./node_modules/axios/lib/core/AxiosError.js ***! + \***************************************************/ + +/*!***************************************************!*\ + !*** ./node_modules/axios/lib/helpers/cookies.js ***! + \***************************************************/ + +/*!****************************************************!*\ + !*** ./node_modules/axios/lib/core/mergeConfig.js ***! + \****************************************************/ + +/*!****************************************************!*\ + !*** ./node_modules/axios/lib/helpers/buildURL.js ***! + \****************************************************/ + +/*!*****************************************************!*\ + !*** ./node_modules/axios/lib/helpers/validator.js ***! + \*****************************************************/ + +/*!******************************************************!*\ + !*** ./node_modules/axios/lib/cancel/CancelToken.js ***! + \******************************************************/ + +/*!******************************************************!*\ + !*** ./node_modules/axios/lib/core/buildFullPath.js ***! + \******************************************************/ + +/*!******************************************************!*\ + !*** ./node_modules/axios/lib/core/transformData.js ***! + \******************************************************/ + +/*!******************************************************!*\ + !*** ./node_modules/axios/lib/helpers/toFormData.js ***! + \******************************************************/ + +/*!*******************************************************!*\ + !*** ./node_modules/axios/lib/helpers/combineURLs.js ***! + \*******************************************************/ + +/*!********************************************************!*\ + !*** ./node_modules/axios/lib/cancel/CanceledError.js ***! + \********************************************************/ + +/*!********************************************************!*\ + !*** ./node_modules/axios/lib/core/dispatchRequest.js ***! + \********************************************************/ + +/*!********************************************************!*\ + !*** ./node_modules/axios/lib/helpers/isAxiosError.js ***! + \********************************************************/ + +/*!********************************************************!*\ + !*** ./node_modules/axios/lib/helpers/parseHeaders.js ***! + \********************************************************/ + +/*!*********************************************************!*\ + !*** ./node_modules/axios/lib/defaults/transitional.js ***! + \*********************************************************/ + +/*!*********************************************************!*\ + !*** ./node_modules/axios/lib/helpers/isAbsoluteURL.js ***! + \*********************************************************/ + +/*!*********************************************************!*\ + !*** ./node_modules/axios/lib/helpers/parseProtocol.js ***! + \*********************************************************/ + +/*!***********************************************************!*\ + !*** ./node_modules/axios/lib/core/InterceptorManager.js ***! + \***********************************************************/ + +/*!***********************************************************!*\ + !*** ./node_modules/axios/lib/helpers/isURLSameOrigin.js ***! + \***********************************************************/ + +/*!***************************************************************!*\ + !*** ./node_modules/axios/lib/helpers/normalizeHeaderName.js ***! + \***************************************************************/ diff --git a/public/imgs/icons/checkmark-circle-outline.svg b/public/imgs/icons/checkmark-circle-outline.svg new file mode 100644 index 0000000..18ffea3 --- /dev/null +++ b/public/imgs/icons/checkmark-circle-outline.svg @@ -0,0 +1 @@ +Checkmark Circle \ No newline at end of file diff --git a/public/imgs/icons/close-circle-outline.svg b/public/imgs/icons/close-circle-outline.svg new file mode 100644 index 0000000..5b0112c --- /dev/null +++ b/public/imgs/icons/close-circle-outline.svg @@ -0,0 +1 @@ +Close Circle \ No newline at end of file diff --git a/public/imgs/logos/discord.svg b/public/imgs/logos/discord.svg new file mode 100644 index 0000000..1a44e7b --- /dev/null +++ b/public/imgs/logos/discord.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/public/imgs/logos/github.svg b/public/imgs/logos/github.svg new file mode 100644 index 0000000..12b008f --- /dev/null +++ b/public/imgs/logos/github.svg @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/public/imgs/logos/gitlab.svg b/public/imgs/logos/gitlab.svg new file mode 100644 index 0000000..95a22f1 --- /dev/null +++ b/public/imgs/logos/gitlab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/imgs/logos/malt.svg b/public/imgs/logos/malt.svg new file mode 100644 index 0000000..87675bd --- /dev/null +++ b/public/imgs/logos/malt.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/public/imgs/logos/mastodon.svg b/public/imgs/logos/mastodon.svg new file mode 100644 index 0000000..87ec2c5 --- /dev/null +++ b/public/imgs/logos/mastodon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/imgs/logos/openstreetmap.svg b/public/imgs/logos/openstreetmap.svg new file mode 100644 index 0000000..cc5e0d4 --- /dev/null +++ b/public/imgs/logos/openstreetmap.svg @@ -0,0 +1,758 @@ + + + + OpenStreetMap logo 2011 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + OpenStreetMap logo 2011 + + + Ken Vermette + + + + April 2011 + + + OpenStreetMap.org + + + Replacement logo for OpenStreetMap Foundation + + + OSM openstreetmap logo + + + http://wiki.openstreetmap.org/wiki/File:Public-images-osm_logo.svg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 010110010011010110010011 + 010110010011010110010011 + + + \ No newline at end of file diff --git a/public/imgs/logos/osm.svg b/public/imgs/logos/osm.svg new file mode 100644 index 0000000..cc5e0d4 --- /dev/null +++ b/public/imgs/logos/osm.svg @@ -0,0 +1,758 @@ + + + + OpenStreetMap logo 2011 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + OpenStreetMap logo 2011 + + + Ken Vermette + + + + April 2011 + + + OpenStreetMap.org + + + Replacement logo for OpenStreetMap Foundation + + + OSM openstreetmap logo + + + http://wiki.openstreetmap.org/wiki/File:Public-images-osm_logo.svg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 010110010011010110010011 + 010110010011010110010011 + + + \ No newline at end of file diff --git a/public/imgs/logos/stackoverflow.svg b/public/imgs/logos/stackoverflow.svg new file mode 100644 index 0000000..d3ba0fe --- /dev/null +++ b/public/imgs/logos/stackoverflow.svg @@ -0,0 +1,49 @@ + + diff --git a/public/imgs/logos/wikidata.svg b/public/imgs/logos/wikidata.svg new file mode 100644 index 0000000..2f2bb56 --- /dev/null +++ b/public/imgs/logos/wikidata.svg @@ -0,0 +1,50 @@ + + + + + + + + + diff --git a/public/imgs/logos/wikipedia.svg b/public/imgs/logos/wikipedia.svg new file mode 100644 index 0000000..87876d4 --- /dev/null +++ b/public/imgs/logos/wikipedia.svg @@ -0,0 +1,3 @@ + + +Wikipedia logo version 2 \ No newline at end of file diff --git a/public/imgs/projects/forum_asso/ask_approval.png b/public/imgs/projects/forum_asso/ask_approval.png new file mode 100644 index 0000000..e60194d Binary files /dev/null and b/public/imgs/projects/forum_asso/ask_approval.png differ diff --git a/public/imgs/projects/forum_asso/changes_approval.png b/public/imgs/projects/forum_asso/changes_approval.png new file mode 100644 index 0000000..57b2f88 Binary files /dev/null and b/public/imgs/projects/forum_asso/changes_approval.png differ diff --git a/public/imgs/projects/forum_asso/condorcet.jpg b/public/imgs/projects/forum_asso/condorcet.jpg new file mode 100644 index 0000000..9cf88da Binary files /dev/null and b/public/imgs/projects/forum_asso/condorcet.jpg differ diff --git a/public/imgs/projects/forum_asso/edit_contact.png b/public/imgs/projects/forum_asso/edit_contact.png new file mode 100644 index 0000000..ea2ca68 Binary files /dev/null and b/public/imgs/projects/forum_asso/edit_contact.png differ diff --git a/public/imgs/projects/forum_asso/edit_description.png b/public/imgs/projects/forum_asso/edit_description.png new file mode 100644 index 0000000..b987815 Binary files /dev/null and b/public/imgs/projects/forum_asso/edit_description.png differ diff --git a/public/imgs/projects/forum_asso/edit_images.png b/public/imgs/projects/forum_asso/edit_images.png new file mode 100644 index 0000000..bd85d66 Binary files /dev/null and b/public/imgs/projects/forum_asso/edit_images.png differ diff --git a/public/imgs/projects/forum_asso/edit_main.png b/public/imgs/projects/forum_asso/edit_main.png new file mode 100644 index 0000000..77c532c Binary files /dev/null and b/public/imgs/projects/forum_asso/edit_main.png differ diff --git a/public/imgs/projects/forum_asso/edit_prices.png b/public/imgs/projects/forum_asso/edit_prices.png new file mode 100644 index 0000000..211dafe Binary files /dev/null and b/public/imgs/projects/forum_asso/edit_prices.png differ diff --git a/public/imgs/projects/forum_asso/edit_schedule.png b/public/imgs/projects/forum_asso/edit_schedule.png new file mode 100644 index 0000000..2b41cae Binary files /dev/null and b/public/imgs/projects/forum_asso/edit_schedule.png differ diff --git a/public/imgs/projects/forum_asso/manage_organizations.png b/public/imgs/projects/forum_asso/manage_organizations.png new file mode 100644 index 0000000..a46bd75 Binary files /dev/null and b/public/imgs/projects/forum_asso/manage_organizations.png differ diff --git a/public/imgs/projects/forum_asso/manage_tags.png b/public/imgs/projects/forum_asso/manage_tags.png new file mode 100644 index 0000000..7f87b9b Binary files /dev/null and b/public/imgs/projects/forum_asso/manage_tags.png differ diff --git a/public/imgs/projects/forum_asso/public_list.png b/public/imgs/projects/forum_asso/public_list.png new file mode 100644 index 0000000..06cf521 Binary files /dev/null and b/public/imgs/projects/forum_asso/public_list.png differ diff --git a/public/imgs/projects/forum_asso/public_page.png b/public/imgs/projects/forum_asso/public_page.png new file mode 100644 index 0000000..8d8d8ea Binary files /dev/null and b/public/imgs/projects/forum_asso/public_page.png differ diff --git a/public/imgs/projects/jobatator/logo.png b/public/imgs/projects/jobatator/logo.png new file mode 100644 index 0000000..c14d70e Binary files /dev/null and b/public/imgs/projects/jobatator/logo.png differ diff --git a/public/imgs/projects/retrobox/logo_alone_square.png b/public/imgs/projects/retrobox/logo_alone_square.png new file mode 100644 index 0000000..f7ca9cd Binary files /dev/null and b/public/imgs/projects/retrobox/logo_alone_square.png differ diff --git a/public/imgs/projects/tracklift/logo_square.svg b/public/imgs/projects/tracklift/logo_square.svg new file mode 100644 index 0000000..77bc6f1 --- /dev/null +++ b/public/imgs/projects/tracklift/logo_square.svg @@ -0,0 +1,47 @@ + + + + + + + diff --git a/public/imgs/projects/werobot/logo-300.png b/public/imgs/projects/werobot/logo-300.png new file mode 100644 index 0000000..888b102 Binary files /dev/null and b/public/imgs/projects/werobot/logo-300.png differ diff --git a/public/imgs/technos/electron.svg b/public/imgs/technos/electron.svg new file mode 100644 index 0000000..dc81c99 --- /dev/null +++ b/public/imgs/technos/electron.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/imgs/technos/golang.svg b/public/imgs/technos/go.svg similarity index 100% rename from public/imgs/technos/golang.svg rename to public/imgs/technos/go.svg diff --git a/public/index.php b/public/index.php index d33dc10..9d384ed 100755 --- a/public/index.php +++ b/public/index.php @@ -1,6 +1,7 @@ addRoutingMiddleware(); $errorMiddleware = $app->addErrorMiddleware(true, true, true); -$twig = Twig::create('../templates', ['cache' => false]); -$app->add(TwigMiddleware::create($app, $twig)); +// we place the definition BEFORE the localization middleware to get the locale attribute +$app->add(new TwigLocalizationMiddleware($container)); +$app->add($container->get(LocalizationMiddleware::class)); + +$app->add(TwigMiddleware::create($app, $container->get(Twig::class))); + require '../src/routes.php'; diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php index d4d7dc4..6128566 100644 --- a/src/Controller/HomeController.php +++ b/src/Controller/HomeController.php @@ -2,6 +2,7 @@ namespace App\Controller; +use App\Middlewares\LocalizationMiddleware; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Slim\Views\Twig; @@ -10,14 +11,69 @@ class HomeController extends AbstractController { public function getHome(ServerRequestInterface $request, ResponseInterface $response) { - return Twig::fromRequest($request)->render( + // $locale = $request->getAttributes()['locale']; + // dd($locale); + return $this->container->get(Twig::class)->render( $response, 'home/home.html.twig', [ + 'links' => array_map(function ($link) { + if (!isset($link['thumbnail'])) { + $link['thumbnail'] = ['src' => $link['id'] . '.svg', 'alt' => $link['id'] . " logo"]; + } + + return $link; + }, $this->container->get('links')), 'technologies' => $this->container->get('technologies'), 'pro_projects' => $this->container->get('pro_projects'), 'side_projects' => $this->container->get('side_projects'), - 'locale' => 'fr' + 'email' => $this->container->get('misc')['email'] ] ); } + + public function getAbout(ServerRequestInterface $request, ResponseInterface $response) + { + return $this->container->get(Twig::class)->render( + $response, 'about.html.twig', + [ + 'email' => $this->container->get('misc')['email'] + ] + ); + } + + public function getProject(ServerRequestInterface $request, ResponseInterface $response) + { + $id = $request->getAttribute('id'); + $projects = [...$this->container->get('pro_projects'), ...$this->container->get('side_projects')]; + $projectsCandidates = array_values(array_filter($projects, fn ($p) => $p['id'] == $id)); + if (count($projectsCandidates) === 0) { + // TODO: add proper response body + return $response + ->withStatus(404); + } + $project = $projectsCandidates[0]; + + $technologies = $this->container->get('technologies'); + + $projectTechnologies = array_map(fn ($tId) => array_values(array_filter($technologies, fn ($t) => $t['id'] == $tId))[0], $project['technologies']); + + return $this->container->get(Twig::class)->render( + $response, 'project.html.twig', + [ + 'project' => [...$project, 'technologies' => $projectTechnologies] + ] + ); + } + + public function switchToLocale(ServerRequestInterface $request, ResponseInterface $response) + { + $newLocale = substr($request->getUri()->getPath(), 1); + $redir = $request->hasHeader('Referer') ? $request->getHeader('Referer') : '/'; + return $this->container->get(LocalizationMiddleware::class)->withSetCookieLocale( + $response + ->withStatus(302) + ->withAddedHeader('Location', $redir), + $newLocale + ); + } } diff --git a/src/Middlewares/LocalizationMiddleware.php b/src/Middlewares/LocalizationMiddleware.php new file mode 100644 index 0000000..20aead1 --- /dev/null +++ b/src/Middlewares/LocalizationMiddleware.php @@ -0,0 +1,332 @@ +setAvailableLocales($locales); + $this->setDefaultLocale($default); + $this->setSearchOrder([ + self::FROM_URI_PATH, + self::FROM_URI_PARAM, + self::FROM_COOKIE, + self::FROM_HEADER + ]); + $this->setReqAttrName('locale'); + $this->setUriParamName('locale'); + $this->setCookieName('locale'); + $this->setCookiePath('/'); + $this->setCookieExpire(3600 * 24 * 30); // 30 days + $this->setLocaleCallback(function () { /* empty function */ }); + } + + /** + * @param array $locales a list of available locales + */ + public function setAvailableLocales(array $locales) + { + $this->availableLocales = []; + foreach ($locales as $locale) { + $this->availableLocales[] = $this->parseLocale($locale); + } + } + + /** + * @param string $default the default locale + */ + public function setDefaultLocale(string $default) + { + $this->defaultLocale = $default; + } + + /** + * @param array $order the order in which the search will be performed to + * resolve the locale + */ + public function setSearchOrder(array $order) + { + $this->searchOrder = $order; + } + + /** + * @param callable $func callable to invoke when searching the locale + */ + public function setSearchCallback(callable $func) + { + $this->searchCallback = $func; + } + + /** + * @param string $name the name for the attribute attached to the request + */ + public function setReqAttrName(string $name) + { + $this->reqAttrName = $name; + } + + /** + * @param string $name the name for the locale URI parameter + */ + public function setUriParamName(string $name) + { + $this->uriParamName = $name; + } + + /** + * @param string $name the name for the locale cookie + */ + public function setCookieName(string $name) + { + $this->cookieName = $name; + } + + /** + * @param string $path the locale cookie's path + */ + public function setCookiePath(string $path) + { + $this->cookiePath = $path; + } + + /** + * @param int $secs cookie expiration in seconds from now + */ + public function setCookieExpire(int $secs) + { + $this->cookieExpire = gmdate('D, d M Y H:i:s T', time() + $secs); + } + + /** + * @param callable $func callable to invoke when locale is determined + */ + public function setLocaleCallback(callable $func) + { + $this->localeCallback = $func; + } + + /** + * Add the locale to the request and response objects + */ + public function process(Request $req, RequestHandler $handler): Response + { + $locale = $this->getLocale($req); + + $this->localeCallback->__invoke($locale); + + $req = $req->withAttribute($this->reqAttrName, $locale); + + $resp = $handler->handle($req); + + if (in_array(self::FROM_COOKIE, $this->searchOrder) && $req->getAttribute('doSetLocaleCookie') === true) { + $resp = $this->withSetCookieLocale($resp, $locale); + } + return $resp; + } + + public function withSetCookieLocale(Response $res, string $locale): Response + { + return $res->withAddedHeader('Set-Cookie', "{$this->cookieName}=$locale; Path={$this->cookiePath}; Expires={$this->cookieExpire}"); + } + + protected function getLocale(Request $req) + { + foreach ($this->searchOrder as $order) { + switch ($order) { + case self::FROM_URI_PATH: + $locale = $this->localeFromPath($req); + break; + + case self::FROM_URI_PARAM: + $locale = $this->localeFromParam($req); + break; + + case self::FROM_COOKIE: + $locale = $this->localeFromCookie($req); + break; + + case self::FROM_HEADER: + $locale = $this->localeFromHeader($req); + break; + + case self::FROM_CALLBACK: + $locale = $this->localeFromCallback($req); + break; + + default: + throw new \DomainException('Unknown search option provided'); + } + if (!empty($locale)) { + return $locale; + } + } + return $this->defaultLocale; + } + + protected function localeFromPath(Request $req): string + { + list(, $value) = explode('/', $req->getUri()->getPath()); + return $this->filterLocale($value); + } + + protected function localeFromParam(Request $req): string + { + $params = $req->getQueryParams(); + $value = $params[$this->uriParamName] ?? ''; + return $this->filterLocale($value); + } + + protected function localeFromCookie(Request $req): string + { + $cookies = $req->getCookieParams(); + $value = $cookies[$this->cookieName] ?? ''; + return $this->filterLocale($value); + } + + protected function localeFromCallback(Request $req): string + { + if (!is_callable($this->searchCallback)) { + throw new \LogicException('Search callback not set'); + } + $locale = $this->searchCallback->__invoke($req); + return $this->filterLocale($locale); + } + + protected function localeFromHeader(Request $req): string + { + $header = $req->getHeaderLine('Accept-Language'); + if (empty($header)) { + return ''; + } + + $values = $this->parse($header); + usort($values, [$this, 'sort']); + foreach ($values as $value) { + $value = $this->filterLocale($value['locale']); + if (!empty($value)) { + return $value; + } + } + // search language if a full locale is not found + foreach ($values as $value) { + $value = $this->filterLocale($value['language']); + if (!empty($value)) { + return $value; + } + } + return ''; + } + + protected function filterLocale(string $locale): string + { + // return the locale if it is available + foreach ($this->availableLocales as $avail) { + if ($locale == $avail['locale']) { + return $avail['locale']; + } + } + return ''; + } + + protected function parse(string $header): array + { + // the value may contain multiple languages separated by commas, + // possibly as locales (ex: en_US) with quality (ex: en_US;q=0.5) + $values = []; + foreach (explode(',', $header) as $lang) { + @list($locale, $quality) = explode(';', $lang, 2); + $val = $this->parseLocale(str_replace('*', $this->defaultLocale, $locale)); + $val['quality'] = $this->parseQuality($quality ?? ''); + $values[] = $val; + } + return $values; + } + + protected function parseLocale(string $locale): array + { + // Locale format: language[_territory[.encoding[@modifier]]] + // + // Language and territory should be separated by an underscore + // although sometimes a hyphen is used. The language code should + // be lowercase. Territory should be uppercase. Take this into + // account but normalize the returned string as lowercase, + // underscore, uppercase. + // + // The possible codeset and modifier is discarded since the header + // *should* really list languages (not locales) in the first place + // and the chances of needing to present content at that level of + // granularity are pretty slim. + $lang = '([[:alpha:]]{2})'; + $terr = '([[:alpha:]]{2})'; + $code = '([-\\w]+)'; + $mod = '([-\\w]+)'; + $regex = "/$lang(?:[-_]$terr(?:\\.$code(?:@$mod)?)?)?/"; + preg_match_all($regex, $locale, $m); + + $locale = $language = strtolower($m[1][0]); + if (!empty($m[2][0])) { + $locale .= '_' . strtoupper($m[2][0]); + } + + return [ + 'locale' => $locale, + 'language' => $language + ]; + } + + protected function parseQuality(string $quality): float + { + // If no quality is given then return 1 as this is the default quality + // defined in RFC 2616 HTTP/1.1 section 14.4 Accept-Language + // See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 + @list(, $value) = explode('=', $quality, 2); + return (float)($value ?: 1.0); + } + + protected function sort(array $a, array $b): int + { + // Sort order is determined first by quality (higher values are + // placed first) then by order of their apperance in the header. + if ($a['quality'] < $b['quality']) { + return 1; + } + if ($a['quality'] == $b['quality']) { + return 0; + } + return -1; + } +} diff --git a/src/Middlewares/TwigLocalizationMiddleware.php b/src/Middlewares/TwigLocalizationMiddleware.php new file mode 100644 index 0000000..81375cf --- /dev/null +++ b/src/Middlewares/TwigLocalizationMiddleware.php @@ -0,0 +1,32 @@ +container->get(Twig::class)->getEnvironment()->addGlobal('locale', $request->getAttribute('locale')); + + return $handler->handle($request); + } +} \ No newline at end of file diff --git a/src/Util/ContainerBuilder.php b/src/Util/ContainerBuilder.php index 67b278d..ed781b5 100644 --- a/src/Util/ContainerBuilder.php +++ b/src/Util/ContainerBuilder.php @@ -10,6 +10,7 @@ class ContainerBuilder { private static array $definitions = [ 'app', + 'container' ]; public static function getContainerBuilder(\DI\ContainerBuilder $containerBuilder = null): \DI\ContainerBuilder diff --git a/src/config/app.php b/src/config/app.php index caa66d3..b18c06c 100644 --- a/src/config/app.php +++ b/src/config/app.php @@ -4,14 +4,22 @@ use App\App; use Symfony\Component\Yaml\Yaml; function getYAML($name) { - $path = App::getBasePath() . '/config/' . $name; + $path = App::getBasePath() . '/' . $name; return Yaml::parseFile($path); } +$misc = getYAML('config/misc.yaml'); + return [ 'basePath' => App::getBasePath(), - 'foo' => 'barz', - 'technologies' => getYAML('technologies.yaml')['technologies'], - 'pro_projects' => getYAML('projects.yaml')['pro_projects'], - 'side_projects' => getYAML('projects.yaml')['side_projects'] + 'misc' => $misc, + 'last_update' => new DateTime($misc['updated_at']), + 'links' => getYAML('config/links.yaml')['links'], + 'technologies' => getYAML('config/technologies.yaml')['technologies'], + 'pro_projects' => getYAML('config/projects.yaml')['pro_projects'], + 'side_projects' => getYAML('config/projects.yaml')['side_projects'], + 'locales' => [ + 'fr' => new \Adbar\Dot(getYAML('assets/locales/fr.yaml')), + 'en' => new \Adbar\Dot(getYAML('assets/locales/en.yaml')) + ], ]; diff --git a/src/config/container.php b/src/config/container.php new file mode 100644 index 0000000..0932dbc --- /dev/null +++ b/src/config/container.php @@ -0,0 +1,47 @@ + function(ContainerInterface $container) { + $twig = Twig::create('../templates', ['cache' => false]); + + $twig->getEnvironment()->addFunction(new \Twig\TwigFunction('getDynLocalizedStr', function (\Twig\Environment $env, $inp) { + $locale = $env->getGlobals()['locale']; + if (is_array($inp)) { + return $inp[$locale]; + } + if (is_string($inp)) { + return $inp; + } + + return 'invalid'; + }, ['needs_environment' => true])); + + $twig->getEnvironment()->addFunction(new \Twig\TwigFunction('getLocalizedStr', function (\Twig\Environment $env, $inp) use ($container) { + $locale = $env->getGlobals()['locale']; + + if (!$container->get('locales')[$locale]->has($inp)) { + return $inp; + } + return $container->get('locales')[$locale]->get($inp); + }, ['needs_environment' => true])); + + $twig->getEnvironment()->addFunction(new \Twig\TwigFunction('getLocale', function (\Twig\Environment $env) { + return $env->getGlobals()['locale']; + }, ['needs_environment' => true])); + + $twig->getEnvironment()->addGlobal('lastUpdate', $container->get('last_update')); + + + return $twig; + }, + + LocalizationMiddleware::class => function () { + $availableLocales = ['en', 'fr']; + $defaultLocale = 'en'; + return new LocalizationMiddleware($availableLocales, $defaultLocale); + } +]; diff --git a/src/routes.php b/src/routes.php index d78000a..b5aabfb 100644 --- a/src/routes.php +++ b/src/routes.php @@ -2,6 +2,8 @@ namespace App; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Server\RequestHandlerInterface; use Slim\Routing\RouteCollectorProxy; function addRoutes(\Slim\App $app): void @@ -9,5 +11,14 @@ function addRoutes(\Slim\App $app): void $container = $app->getContainer(); $app->get('/', [Controller\HomeController::class, 'getHome']); + $app->get('/project/{id}', [Controller\HomeController::class, 'getProject']); + $app->get('/about', [Controller\HomeController::class, 'getAbout']); $app->get('/ping', [Controller\PingController::class, 'getPing']); + + $addDoNotSetLocaleCookieMiddleware = function (ServerRequestInterface $req, RequestHandlerInterface $handler) { + return $handler->handle($req->withAttribute('doSetLocaleCookie', false)); + }; + + $app->get('/en', [Controller\HomeController::class, 'switchToLocale'])->add($addDoNotSetLocaleCookieMiddleware); + $app->get('/fr', [Controller\HomeController::class, 'switchToLocale'])->add($addDoNotSetLocaleCookieMiddleware); } diff --git a/templates/about.html.twig b/templates/about.html.twig new file mode 100644 index 0000000..122a10e --- /dev/null +++ b/templates/about.html.twig @@ -0,0 +1,21 @@ +{% extends 'layout.html.twig' %} +{% block content %} +
+ + {% if getLocale() == 'fr' %} +

A propos de ce site

+ +

+

+

+ {% endif %} + {% if getLocale() == 'en' %} +

About this website

+ + + {% endif %} +
+{% endblock %} + diff --git a/templates/home/contact.html.twig b/templates/home/contact.html.twig index e69de29..c185bb0 100644 --- a/templates/home/contact.html.twig +++ b/templates/home/contact.html.twig @@ -0,0 +1,83 @@ +
+
+
+

{{ getLocalizedStr('contact.title') }}

+
+ +
+

+ {{ getLocalizedStr('contact.use-email') }} {{ email }} +

+ +

+ {{ getLocalizedStr('contact.use-form') }} +

+
+ +
+
+ + + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
diff --git a/templates/home/home.html.twig b/templates/home/home.html.twig index 00f3f6d..b3fca4b 100644 --- a/templates/home/home.html.twig +++ b/templates/home/home.html.twig @@ -3,6 +3,7 @@ {% include('home/profile.html.twig') %} {% include('home/technologies.html.twig') %} {% include('home/projects.html.twig') %} - {# {% include('home/side_projects.html.twig') %} #} + {% include('home/links.html.twig') %} + {% include('home/contact.html.twig') %} {% endblock %} diff --git a/templates/home/links.html.twig b/templates/home/links.html.twig new file mode 100644 index 0000000..505ac6b --- /dev/null +++ b/templates/home/links.html.twig @@ -0,0 +1,26 @@ + + diff --git a/templates/home/pro_projects.html.twig b/templates/home/pro_projects.html.twig deleted file mode 100644 index a8e0928..0000000 --- a/templates/home/pro_projects.html.twig +++ /dev/null @@ -1,28 +0,0 @@ - -
-
-
-

Professional projects

-
-
- -
-

- Annuaire associatif administré -

-

- Light add loss traditional on. Against capital political black network option. - Need education ready. Too knowledge once practice customer. - Environmental loss level southern protect guy. -

-
-
-

- Tracklift -

-

-

-
-
-
-
diff --git a/templates/home/profile.html.twig b/templates/home/profile.html.twig index 93de9eb..30f1d91 100644 --- a/templates/home/profile.html.twig +++ b/templates/home/profile.html.twig @@ -50,8 +50,8 @@
- Hi! I'm Matthieu - I like to program stuff + {{ getLocalizedStr('profile.intro.main') }} + {{ getLocalizedStr('profile.intro.secondary') }}
diff --git a/templates/home/projects.html.twig b/templates/home/projects.html.twig index 5f97385..f72ff87 100644 --- a/templates/home/projects.html.twig +++ b/templates/home/projects.html.twig @@ -1,20 +1,22 @@ -
+
-

Highlighted professional projects

+

+ {{ getLocalizedStr('projects.pro.title') }} +

{% include 'home/projects_list.html.twig' with {'projects': pro_projects} %}
-
+
-

Highlighted side projects

+

+ {{ getLocalizedStr('projects.side.title') }} +

{% include 'home/projects_list.html.twig' with {'projects': side_projects} %}
- - diff --git a/templates/home/projects_list.html.twig b/templates/home/projects_list.html.twig index f842786..138cad2 100644 --- a/templates/home/projects_list.html.twig +++ b/templates/home/projects_list.html.twig @@ -1,21 +1,43 @@
{% for project in projects %}
-

- - {% if project.locales[locale].name is not defined %} - {{ project.name }} - {% else %} - {{ project.locales[locale].name }} - {% endif %} +
+
+

+ {% if project.detailled_page %} + + {{ getDynLocalizedStr(project.name) }} + + {% else %} + {{ getDynLocalizedStr(project.name) }} + {% endif %} +

+ {% if project.description is defined %} +

+ {{ getDynLocalizedStr(project.description) }} +

+ {% endif %} +
+
+ {% if project.thumbnail is defined %} + {{ getDynLocalizedStr(project.thumbnail.alt) }} + {% endif %} +
+
+ +

- {% if project.locales is defined %} -

- {{ project.locales[locale].description }} -

- {% endif %} +
{% endfor %} - diff --git a/templates/home/side_projects.html.twig b/templates/home/side_projects.html.twig deleted file mode 100644 index e81b40d..0000000 --- a/templates/home/side_projects.html.twig +++ /dev/null @@ -1,23 +0,0 @@ - -
-
-
-

Highlighted projects

-
-
- -
-

- Annuaire associatif administré -

-

- Light add loss traditional on. Against capital political black network option. - Need education ready. Too knowledge once practice customer. - Environmental loss level southern protect guy. -

-
-
-
-
-
-
diff --git a/templates/home/technologies.html.twig b/templates/home/technologies.html.twig index 34e0737..c3acab4 100644 --- a/templates/home/technologies.html.twig +++ b/templates/home/technologies.html.twig @@ -1,7 +1,9 @@ -
+
-

Some of the technologies that I'm currently working with

+

+ {{ getLocalizedStr('technologies.title') }} +

{% for tech in technologies %} diff --git a/templates/layout.html.twig b/templates/layout.html.twig index b64ec01..82e92b5 100644 --- a/templates/layout.html.twig +++ b/templates/layout.html.twig @@ -1,14 +1,42 @@ - + - Matthieu Bessat - Web Developer - + {% block title %}{% endblock %}{{ getLocalizedStr('page.title') }} + {% block content %}{% endblock %} - + + + + + diff --git a/templates/project.html.twig b/templates/project.html.twig new file mode 100644 index 0000000..7ea8336 --- /dev/null +++ b/templates/project.html.twig @@ -0,0 +1,40 @@ + +{% extends 'layout.html.twig' %} +{% block title %} + {{ getDynLocalizedStr(project.name) }} +{% endblock %} +{% block content %} +
+ +
+

{{ getLocalizedStr('projects.project') }} {{ getDynLocalizedStr(project.name) }}

+ + {% if project.background is defined %} +

{{ getLocalizedStr('projects.background') }}

+

+ {{ getDynLocalizedStr(project.background) }} +

+ {% endif %} + + {% if project.solution is defined %} +

{{ getLocalizedStr('projects.solution') }}

+

+ {{ getDynLocalizedStr(project.solution) }} +

+ {% endif %} + +

{{ getLocalizedStr('projects.technologies') }}

+

+

+ {% for tech in project.technologies %} +
+ {{ tech.name }} +
+ {% endfor %} +
+
+
+{% endblock %} + diff --git a/wikidata.txt b/wikidata.txt new file mode 100644 index 0000000..8956140 --- /dev/null +++ b/wikidata.txt @@ -0,0 +1,33 @@ +Q11707176 Cascading Style Sheets Level 3 style sheet language +Q2053 HTML5 fifth and current version of the hypertext markup language for structuring and presenting content for the World Wide Web +Q230036 jQuery JavaScript library +Q756100 Node.js JavaScript runtime environment +Q49007 yarn long continuous length of interlocked fibers +Q22909730 webpack open-source JavaScript module bundler +Q1572865 Sass Syntactically Awesome StyleSheets stylesheet language +no result +Q2225 electron subatomic particle with negative electric charge +Q28865 Python general-purpose programming language +Q5618801 Gunicorn web server +Q1537445 stevedore occupation of loading and unloading ships +Q411840 cadmium chloride chemical compound +Q306144 nginx open source web server and a reverse proxy server +Q81683 varnish transparent, hard, protective finish or film used in painting +Q4778915 Cloudflare Web infrastructure and website security company +Q787177 MariaDB Database management system, relational, open source, community developed fork of MySQL +Q1165204 MongoDB cross-platform document-oriented database +Q105097049 Curator tool to tend Elasticsearch indices +Q2136322 Redis NoSQL database management software +Q17193 Philippine peso official currency of the Philippines +Q108740058 API Platform +Q36834 composer musician who is an author of music in any form; person who creates music, either by musical notation or oral tradition +no result +Q13634357 Laravel open source web application framework, written in PHP +Q1322933 Symfony PHP web application framework for MVC applications +no result +Q24589705 Vue.js JavaScript framework +Q55641291 Nuxt.js JavaScript framework +no result +Q11413 go board game for two players that originated in China more than 2,500 years ago +Q16878131 Express.js JavaScript framework + diff --git a/yarn.lock b/yarn.lock index 852a5e4..ef05002 100755 --- a/yarn.lock +++ b/yarn.lock @@ -251,11 +251,24 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + atob@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +axios@^0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== + dependencies: + follow-redirects "^1.14.9" + form-data "^4.0.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -456,6 +469,13 @@ colorette@^1.2.1: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -554,6 +574,11 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + detect-file@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" @@ -756,11 +781,25 @@ findup-sync@^3.0.0: micromatch "^3.0.4" resolve-dir "^1.0.1" +follow-redirects@^1.14.9: + version "1.15.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" + integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" @@ -1224,6 +1263,18 @@ mime-db@1.49.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed" integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA== +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mime-types@^2.1.27: version "2.1.32" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5"